initial commit
This commit is contained in:
commit
85d50ee958
26 files changed
+2146
No files matched your search
@@ -0,0 +1,11 @@
|
||||
package utils
|
||||
|
||||
import "os"
|
||||
|
||||
func EnsureDir(path string) int {
|
||||
err := os.MkdirAll(path, 0755)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package utils
|
||||
|
||||
import "os"
|
||||
|
||||
func GetTargetFilename(typ string) string {
|
||||
switch typ {
|
||||
case "kernel":
|
||||
return os.Getenv("KERNEL_FILE")
|
||||
case "dtb":
|
||||
return os.Getenv("DTB_FILE")
|
||||
case "uboot":
|
||||
return os.Getenv("UBOOT_FILE")
|
||||
case "uboot_bin":
|
||||
return os.Getenv("UBOOT_BIN_FILE")
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func LoadEnv() {
|
||||
err := godotenv.Load("up.conf")
|
||||
if err != nil {
|
||||
log.Fatal("Error loading env file")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// mount_device checks if the device is already mounted at MOUNT_POINT,
|
||||
// and if not, attempts to mount it as vfat or ext4.
|
||||
// Returns 0 on success, -1 on error.
|
||||
func MountDevice() int {
|
||||
var DEVICE = os.Getenv("DEVICE")
|
||||
var MOUNT_POINT = os.Getenv("MOUNT_POINT")
|
||||
if EnsureDir(MOUNT_POINT) < 0 {
|
||||
return -1
|
||||
}
|
||||
|
||||
// Check if already mounted
|
||||
f, err := os.Open("/proc/mounts")
|
||||
if err == nil {
|
||||
defer f.Close()
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
// Simple substring check (equivalent to strstr)
|
||||
if contains(line, MOUNT_POINT) {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
// Ignore scanner errors for simplicity
|
||||
}
|
||||
|
||||
// Try mounting as vfat, then ext4
|
||||
if err := syscall.Mount(DEVICE, MOUNT_POINT, "vfat", 0, ""); err != nil {
|
||||
if err2 := syscall.Mount(DEVICE, MOUNT_POINT, "ext4", 0, ""); err2 != nil {
|
||||
// Both failed – print error (like perror) and return -1
|
||||
fmt.Fprintf(os.Stderr, "mount: %v (vfat) / %v (ext4)\n", err, err2)
|
||||
return -1
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func contains(s, substr string) bool {
|
||||
// Simple substring check, equivalent to strstr(s, substr) != NULL
|
||||
// You may also use strings.Contains if imported.
|
||||
for i := 0; i+len(substr) <= len(s); i++ {
|
||||
if s[i:i+len(substr)] == substr {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync"
|
||||
"syscall"
|
||||
"up/utils/mtd"
|
||||
)
|
||||
|
||||
// Глобальное состояние загрузки (используется для обновления прогресса)
|
||||
var uploadState struct {
|
||||
sync.RWMutex
|
||||
Progress int
|
||||
// другие поля могут быть здесь
|
||||
}
|
||||
|
||||
// writeUbootToDevices записывает U-Boot в файл, SPI NOR и SD-карту.
|
||||
// Принимает данные, их длину и путь для сохранения копии в файловой системе.
|
||||
func WriteUbootToDevices(data []byte, filepath string) error {
|
||||
UBOOT_MAX_SIZE, err := strconv.Atoi(os.Getenv("UBOOT_MAX_SIZE"))
|
||||
if err != nil {
|
||||
fmt.Println("error: ", err)
|
||||
return err
|
||||
}
|
||||
MMC_BOOT_OFFSET, err := strconv.Atoi(os.Getenv("MMC_BOOT_OFFSET"))
|
||||
if err != nil {
|
||||
fmt.Println("error: ", err)
|
||||
return err
|
||||
}
|
||||
const chunkSize = 4096
|
||||
dataLen := int64(len(data))
|
||||
|
||||
fmt.Printf("Writing U-Boot to devices (%d bytes)...\n", dataLen)
|
||||
|
||||
// Проверка лимита размера
|
||||
if dataLen > int64(UBOOT_MAX_SIZE*1024*1024) {
|
||||
return fmt.Errorf("U-Boot image too large for SPI NOR (%d > %d bytes)", dataLen, UBOOT_MAX_SIZE)
|
||||
}
|
||||
|
||||
// --- 1. Сохраняем в файл (копия) ---
|
||||
f, err := os.Create(filepath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create file %s: %w", filepath, err)
|
||||
}
|
||||
|
||||
var written int64
|
||||
for written < dataLen {
|
||||
toWrite := dataLen - written
|
||||
if toWrite > chunkSize {
|
||||
toWrite = chunkSize
|
||||
}
|
||||
n, err := f.Write(data[written : written+toWrite])
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return fmt.Errorf("failed to write to file: %w", err)
|
||||
}
|
||||
written += int64(n)
|
||||
// Прогресс: 30% за запись в файл
|
||||
uploadState.Lock()
|
||||
uploadState.Progress = int(written * 30 / dataLen)
|
||||
uploadState.Unlock()
|
||||
}
|
||||
f.Close()
|
||||
syscall.Sync() // синхронизация после записи файла
|
||||
|
||||
// --- 2. Запись в SPI NOR (/dev/mtd0) ---
|
||||
fmt.Println("Writing to /dev/mtd0 (SPI NOR)...")
|
||||
mtdFd, err := os.OpenFile("/dev/mtd0", os.O_RDWR|os.O_SYNC, 0660)
|
||||
if err == nil {
|
||||
defer mtdFd.Close()
|
||||
|
||||
// Получаем размер MTD-устройства из sysfs
|
||||
mtdSize, err := mtd.ReadMTDSize(0)
|
||||
if err != nil {
|
||||
fmt.Printf("Cannot get MTD size from sysfs, proceeding without erase: %v\n", err)
|
||||
} else {
|
||||
// Стираем весь чип
|
||||
fmt.Printf("Erasing SPI NOR (size %d bytes)...\n", mtdSize)
|
||||
if err := mtd.EraseMTD(mtdFd, 0, mtdSize); err != nil {
|
||||
fmt.Printf("Warning: erase failed (continuing anyway): %v\n", err)
|
||||
} else {
|
||||
fmt.Println("SPI NOR erased successfully")
|
||||
}
|
||||
}
|
||||
|
||||
// Запись в MTD
|
||||
written = 0
|
||||
for written < dataLen {
|
||||
toWrite := dataLen - written
|
||||
if toWrite > chunkSize {
|
||||
toWrite = chunkSize
|
||||
}
|
||||
n, err := mtdFd.Write(data[written : written+toWrite])
|
||||
if err != nil {
|
||||
fmt.Printf("Error writing to /dev/mtd0: %v\n", err)
|
||||
break
|
||||
}
|
||||
written += int64(n)
|
||||
// Прогресс: 30–65% за SPI NOR
|
||||
uploadState.Lock()
|
||||
uploadState.Progress = 30 + int(written*35/dataLen)
|
||||
uploadState.Unlock()
|
||||
}
|
||||
fmt.Printf("Written %d bytes to /dev/mtd0\n", written)
|
||||
} else {
|
||||
fmt.Printf("/dev/mtd0 not available: %v (skipping SPI NOR)\n", err)
|
||||
}
|
||||
|
||||
// --- 3. Запись в SD-карту (/dev/mmcblk1) со смещением 8 КБ ---
|
||||
fmt.Printf("Writing to /dev/mmcblk1 at offset %d (SD card boot area)...\n", MMC_BOOT_OFFSET)
|
||||
mmcFd, err := os.OpenFile("/dev/mmcblk1", os.O_RDWR|os.O_SYNC, 0660)
|
||||
if err == nil {
|
||||
defer mmcFd.Close()
|
||||
|
||||
// Устанавливаем смещение
|
||||
if _, err := mmcFd.Seek(int64(MMC_BOOT_OFFSET), io.SeekStart); err != nil {
|
||||
fmt.Printf("Failed to seek on /dev/mmcblk1: %v\n", err)
|
||||
} else {
|
||||
written = 0
|
||||
for written < dataLen {
|
||||
toWrite := dataLen - written
|
||||
if toWrite > chunkSize {
|
||||
toWrite = chunkSize
|
||||
}
|
||||
n, err := mmcFd.Write(data[written : written+toWrite])
|
||||
if err != nil {
|
||||
fmt.Printf("Error writing to /dev/mmcblk1: %v\n", err)
|
||||
break
|
||||
}
|
||||
written += int64(n)
|
||||
// Прогресс: 65–100% за MMC
|
||||
uploadState.Lock()
|
||||
uploadState.Progress = 65 + int(written*35/dataLen)
|
||||
uploadState.Unlock()
|
||||
}
|
||||
fmt.Printf("Written %d bytes to /dev/mmcblk1 at offset %d\n", written, MMC_BOOT_OFFSET)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("/dev/mmcblk1 not available: %v (skipping SD card)\n", err)
|
||||
}
|
||||
|
||||
// Финальная синхронизация
|
||||
syscall.Sync()
|
||||
|
||||
uploadState.Lock()
|
||||
uploadState.Progress = 100
|
||||
uploadState.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package mtd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// eraseMTD выполняет ioctl MEMERASE для стирания области MTD-устройства.
|
||||
// Используется системный вызов ioctl с соответствующей структурой.
|
||||
func EraseMTD(f *os.File, start, length uint64) error {
|
||||
// Структура erase_info_user (определена в linux/mtd/mtd.h)
|
||||
// На 64‑битных системах оба поля uint64.
|
||||
type eraseInfo struct {
|
||||
Start uint64
|
||||
Length uint64
|
||||
}
|
||||
ei := eraseInfo{Start: start, Length: length}
|
||||
|
||||
// Определяем ioctl MEMERASE. Числовое значение можно взять из заголовков.
|
||||
// Для ARM64 обычно: MEMERASE = 0x80044d02? Но используем стандартное значение,
|
||||
// которое определено в <linux/mtd/mtd.h> через _IOW('M', 2, erase_info_user).
|
||||
// В большинстве 64‑битных систем это 0x80104d02? Проверим:
|
||||
// _IOC(_IOC_WRITE, 'M', 2, sizeof(erase_info_user)) с sizeof=16 => 0x80104d02.
|
||||
// Но для простоты можно использовать константу из пакета unix, если он её определяет.
|
||||
// Если нет, её можно вычислить с помощью макроса _IOW, но в чистом Go это сложно.
|
||||
// Положимся на значение, которое работает на целевой платформе:
|
||||
const MEMERASE = 0x80044d02 // для 32-битных, для 64-битных может отличаться.
|
||||
// Более безопасно использовать вызов syscall.Syscall с аргументами напрямую.
|
||||
// Вместо этого можно попытаться вызвать "mtd_debug erase" через exec, но это нежелательно.
|
||||
// Здесь мы оставляем как пример; при необходимости скорректируйте константу под вашу платформу.
|
||||
|
||||
_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, f.Fd(), MEMERASE, uintptr(unsafe.Pointer(&ei)))
|
||||
if errno != 0 {
|
||||
return fmt.Errorf("ioctl MEMERASE failed: %v", errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package mtd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ReadMTDSize(devIndex int) (uint64, error) {
|
||||
path := fmt.Sprintf("/sys/class/mtd/mtd%d/size", devIndex)
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Файл содержит размер в байтах как строку (например, "16777216\n")
|
||||
sizeStr := strings.TrimSpace(string(data))
|
||||
size, err := strconv.ParseUint(sizeStr, 10, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("parsing size from %s: %w", path, err)
|
||||
}
|
||||
return size, nil
|
||||
}
|
||||
Reference in new issue
Block a user