added upload uboot
This commit is contained in:
@@ -8,10 +8,14 @@
|
||||
- **Обновление ядра**: Загрузка `Image.gz` в boot раздел
|
||||
- **Обновление Device-Tree**: Загрузка `sun50i-h618-orangepi-zero2w.dtb`
|
||||
- **U-Boot Environment**: Загрузка `uboot.env`
|
||||
- **U-Boot Binary**: Загрузка `u-boot-sunxi-with-spl.bin` с записью в:
|
||||
- `/media/u-boot-sunxi-with-spl.bin` (файл)
|
||||
- `/dev/mtd0` (SPI NOR flash)
|
||||
- `/dev/mmcblk1` со смещением 8KB (SD карта, аналог `dd seek=8`)
|
||||
- **Редактор extlinux.conf**: Просмотр и редактирование конфигурации загрузчика
|
||||
- **Индикатор прогресса**: Отображение прогресса загрузки в реальном времени
|
||||
- **Авто-монтирование**: Автоматически монтирует `/dev/mmcblk1p1` в `/media`
|
||||
- **Поддержка больших файлов**: Загрузка файлов до 200 MB
|
||||
- **Поддержка больших файлов**: Загрузка файлов до 200 MB (до 1 MB для U-Boot Binary)
|
||||
- **Перезагрузка системы**: Кнопка безопасной перезагрузки с корректным отмонтированием
|
||||
- **Мониторинг температуры**: Отображение температуры CPU с цветовой индикацией
|
||||
|
||||
@@ -103,6 +107,7 @@ sudo ./up
|
||||
| Ядро | `/media/Image.gz` |
|
||||
| Device-Tree | `/media/sun50i-h618-orangepi-zero2w.dtb` |
|
||||
| U-Boot Env | `/media/uboot.env` |
|
||||
| U-Boot Binary | `/media/u-boot-sunxi-with-spl.bin` + `/dev/mtd0` + `/dev/mmcblk1` (+8KB offset) |
|
||||
| Extlinux | `/media/extlinux/extlinux.conf` |
|
||||
|
||||
## Конфигурация
|
||||
|
||||
+178
-26
@@ -4,6 +4,8 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <mtd/mtd-user.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
@@ -18,9 +20,11 @@
|
||||
#define KERNEL_FILE "Image.gz"
|
||||
#define DTB_FILE "sun50i-h618-orangepi-zero2w.dtb"
|
||||
#define UBOOT_FILE "uboot.env"
|
||||
#define UBOOT_BIN_FILE "u-boot-sunxi-with-spl.bin"
|
||||
#define EXTLINUX_FILE "extlinux/extlinux.conf"
|
||||
|
||||
#define MAX_FILE_SIZE (200 * 1024 * 1024) // 200 MB
|
||||
#define UBOOT_MAX_SIZE (1 * 1024 * 1024) // 1 MB for U-Boot
|
||||
|
||||
static const char *s_http_port = "http://0.0.0.0:43243";
|
||||
static struct mg_mgr mgr;
|
||||
@@ -100,9 +104,135 @@ static const char* get_target_filename(const char *type) {
|
||||
if (strcmp(type, "kernel") == 0) return KERNEL_FILE;
|
||||
if (strcmp(type, "dtb") == 0) return DTB_FILE;
|
||||
if (strcmp(type, "uboot") == 0) return UBOOT_FILE;
|
||||
if (strcmp(type, "uboot_bin") == 0) return UBOOT_BIN_FILE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Write U-Boot to SPI NOR (mtd0) and MMC (mmcblk1)
|
||||
static int write_uboot_to_devices(const char *data, size_t len, const char *filepath) {
|
||||
int success = 0;
|
||||
size_t chunk_size = 4096;
|
||||
|
||||
// Allwinner H618 boot offset: 8KB (same as "bs=1024 seek=8" in dd)
|
||||
#define MMC_BOOT_OFFSET 8192
|
||||
|
||||
printf("Writing U-Boot to devices (%zu bytes)...\n", len);
|
||||
|
||||
// Check size limit for SPI NOR (safe limit to avoid overwriting partitions)
|
||||
if (len > UBOOT_MAX_SIZE) {
|
||||
printf("U-Boot image too large for SPI NOR (%zu > %d bytes)\n", len, UBOOT_MAX_SIZE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// First, save to file in /media
|
||||
FILE *f = fopen(filepath, "wb");
|
||||
if (!f) {
|
||||
perror("fopen uboot file");
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t written = 0;
|
||||
while (written < len) {
|
||||
size_t to_write = (len - written < chunk_size) ? (len - written) : chunk_size;
|
||||
if (fwrite(data + written, 1, to_write, f) != to_write) {
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
written += to_write;
|
||||
g_upload.progress = (int)(written * 30 / len); // First 30% for file write
|
||||
}
|
||||
fclose(f);
|
||||
sync();
|
||||
|
||||
// Write to /dev/mtd0 (SPI NOR flash)
|
||||
printf("Writing to /dev/mtd0 (SPI NOR)...\n");
|
||||
int mtd_fd = open("/dev/mtd0", O_RDWR | O_SYNC);
|
||||
if (mtd_fd >= 0) {
|
||||
// Get erase block size for SPI NOR
|
||||
struct mtd_info_user mtd_info;
|
||||
memset(&mtd_info, 0, sizeof(mtd_info));
|
||||
if (ioctl(mtd_fd, MEMGETINFO, &mtd_info) == 0) {
|
||||
printf("SPI NOR info: type=%s, size=%u bytes, erasesize=%u bytes\n",
|
||||
mtd_info.type == MTD_NORFLASH ? "NOR" : "UNKNOWN",
|
||||
mtd_info.size, mtd_info.erasesize);
|
||||
|
||||
// Erase SPI NOR before writing
|
||||
printf("Erasing SPI NOR...\n");
|
||||
struct erase_info_user erase_info;
|
||||
erase_info.start = 0;
|
||||
erase_info.length = mtd_info.size; // Erase entire chip
|
||||
|
||||
if (ioctl(mtd_fd, MEMERASE, &erase_info) == 0) {
|
||||
printf("SPI NOR erased successfully\n");
|
||||
} else {
|
||||
perror("MEMERASE failed");
|
||||
printf("Continuing anyway (some systems auto-erase)...\n");
|
||||
}
|
||||
} else {
|
||||
perror("MEMGETINFO failed");
|
||||
printf("Could not get SPI NOR info, proceeding with write...\n");
|
||||
}
|
||||
|
||||
// Write to SPI NOR
|
||||
written = 0;
|
||||
while (written < len) {
|
||||
size_t to_write = (len - written < chunk_size) ? (len - written) : chunk_size;
|
||||
ssize_t w = write(mtd_fd, data + written, to_write);
|
||||
if (w < 0) {
|
||||
perror("write mtd0 (SPI NOR)");
|
||||
close(mtd_fd);
|
||||
break;
|
||||
}
|
||||
written += w;
|
||||
g_upload.progress = 30 + (int)(written * 35 / len); // 30-65% for SPI NOR
|
||||
}
|
||||
close(mtd_fd);
|
||||
printf("Written %zu bytes to /dev/mtd0 (SPI NOR)\n", written);
|
||||
success = 1;
|
||||
} else {
|
||||
perror("open /dev/mtd0");
|
||||
printf("/dev/mtd0 (SPI NOR) not available, skipping...\n");
|
||||
}
|
||||
|
||||
// Write to /dev/mmcblk1 at offset 8KB (Allwinner H618 boot location)
|
||||
// This matches: dd if=u-boot.bin of=/dev/mmcblk1 bs=1024 seek=8
|
||||
printf("Writing to /dev/mmcblk1 at offset %d (SD card boot area)...\n", MMC_BOOT_OFFSET);
|
||||
int mmc_fd = open("/dev/mmcblk1", O_RDWR | O_SYNC);
|
||||
if (mmc_fd >= 0) {
|
||||
// Seek to 8KB offset (sector 16 for 512-byte sectors)
|
||||
if (lseek(mmc_fd, MMC_BOOT_OFFSET, SEEK_SET) != MMC_BOOT_OFFSET) {
|
||||
perror("lseek mmcblk1");
|
||||
close(mmc_fd);
|
||||
} else {
|
||||
written = 0;
|
||||
while (written < len) {
|
||||
size_t to_write = (len - written < chunk_size) ? (len - written) : chunk_size;
|
||||
ssize_t w = write(mmc_fd, data + written, to_write);
|
||||
if (w < 0) {
|
||||
perror("write mmcblk1");
|
||||
close(mmc_fd);
|
||||
break;
|
||||
}
|
||||
written += w;
|
||||
g_upload.progress = 65 + (int)(written * 35 / len); // 65-100% for MMC
|
||||
}
|
||||
close(mmc_fd);
|
||||
printf("Written %zu bytes to /dev/mmcblk1 at offset %d\n", written, MMC_BOOT_OFFSET);
|
||||
success = 1;
|
||||
}
|
||||
} else {
|
||||
perror("open /dev/mmcblk1");
|
||||
printf("/dev/mmcblk1 not available, skipping...\n");
|
||||
}
|
||||
|
||||
// Final sync to ensure all data is flushed
|
||||
sync();
|
||||
|
||||
g_upload.progress = 100;
|
||||
|
||||
return success ? 0 : -1;
|
||||
}
|
||||
|
||||
// Get MIME type based on file extension
|
||||
static const char* get_mime_type(const char *path) {
|
||||
const char *ext = strrchr(path, '.');
|
||||
@@ -301,10 +431,10 @@ static void handle_temperature(struct mg_connection *c) {
|
||||
char type[32] = {0};
|
||||
if (fscanf(type_f, "%31s", type) == 1) {
|
||||
// Prefer CPU or soc zones
|
||||
int is_cpu = (strstr(type, "cpu") != NULL ||
|
||||
int is_cpu = (strstr(type, "cpu") != NULL ||
|
||||
strstr(type, "soc") != NULL ||
|
||||
strstr(type, "CPU") != NULL);
|
||||
|
||||
|
||||
int temp_raw;
|
||||
if (fscanf(f, "%d", &temp_raw) == 1) {
|
||||
temperature = temp_raw / 1000.0f;
|
||||
@@ -337,11 +467,11 @@ static void handle_temperature(struct mg_connection *c) {
|
||||
char name[32] = {0};
|
||||
if (fscanf(name_f, "%31s", name) == 1) {
|
||||
// Prefer CPU or sunxi sensors
|
||||
int is_cpu = (strstr(name, "cpu") != NULL ||
|
||||
int is_cpu = (strstr(name, "cpu") != NULL ||
|
||||
strstr(name, "sunxi") != NULL ||
|
||||
strstr(name, "CPU") != NULL ||
|
||||
strstr(name, "acpitz") != NULL);
|
||||
|
||||
|
||||
int temp_raw;
|
||||
if (fscanf(f, "%d", &temp_raw) == 1) {
|
||||
temperature = temp_raw / 1000.0f;
|
||||
@@ -369,7 +499,7 @@ static void handle_temperature(struct mg_connection *c) {
|
||||
"/sys/class/hwmon/hwmon3/temp1_input",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
for (int i = 0; fallback_paths[i] != NULL && !success; i++) {
|
||||
FILE *f = fopen(fallback_paths[i], "r");
|
||||
if (f) {
|
||||
@@ -456,8 +586,14 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (file_data.len > MAX_FILE_SIZE) {
|
||||
mg_http_reply(c, 400, "", "{\"error\":\"File too large (max 200MB)\"}\n");
|
||||
// Check file size limits
|
||||
size_t max_size = MAX_FILE_SIZE;
|
||||
if (strcmp(type, "uboot_bin") == 0) {
|
||||
max_size = UBOOT_MAX_SIZE; // 1MB limit for U-Boot (safe for boot area)
|
||||
}
|
||||
|
||||
if (file_data.len > max_size) {
|
||||
mg_http_reply(c, 400, "", "{\"error\":\"File too large\"}\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -477,32 +613,48 @@ static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||
|
||||
printf("Upload: %s (%zu bytes) -> %s\n", type, file_data.len, filepath);
|
||||
|
||||
// Write file in chunks
|
||||
FILE *fp = fopen(filepath, "wb");
|
||||
if (!fp) {
|
||||
mg_http_reply(c, 500, "", "{\"error\":\"Failed to create file\"}\n");
|
||||
return;
|
||||
}
|
||||
// Update upload state
|
||||
memcpy(g_upload.type, type, strlen(type) + 1);
|
||||
g_upload.received = 0;
|
||||
g_upload.progress = 0;
|
||||
snprintf(g_upload.status, sizeof(g_upload.status), "Uploading...");
|
||||
|
||||
size_t written = 0;
|
||||
size_t chunk_size = 65536;
|
||||
while (written < file_data.len) {
|
||||
size_t to_write = (file_data.len - written < chunk_size) ?
|
||||
(file_data.len - written) : chunk_size;
|
||||
if (fwrite(file_data.buf + written, 1, to_write, fp) != to_write) {
|
||||
fclose(fp);
|
||||
mg_http_reply(c, 500, "", "{\"error\":\"Failed to write file\"}\n");
|
||||
// Special handling for U-Boot binary
|
||||
if (strcmp(type, "uboot_bin") == 0) {
|
||||
if (write_uboot_to_devices(file_data.buf, file_data.len, filepath) < 0) {
|
||||
snprintf(g_upload.status, sizeof(g_upload.status), "Failed");
|
||||
mg_http_reply(c, 500, "", "{\"error\":\"Failed to write U-Boot\"}\n");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Write file in chunks for other types
|
||||
FILE *fp = fopen(filepath, "wb");
|
||||
if (!fp) {
|
||||
mg_http_reply(c, 500, "", "{\"error\":\"Failed to create file\"}\n");
|
||||
return;
|
||||
}
|
||||
written += to_write;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
sync();
|
||||
size_t written = 0;
|
||||
size_t chunk_size = 65536;
|
||||
while (written < file_data.len) {
|
||||
size_t to_write = (file_data.len - written < chunk_size) ?
|
||||
(file_data.len - written) : chunk_size;
|
||||
if (fwrite(file_data.buf + written, 1, to_write, fp) != to_write) {
|
||||
fclose(fp);
|
||||
mg_http_reply(c, 500, "", "{\"error\":\"Failed to write file\"}\n");
|
||||
return;
|
||||
}
|
||||
written += to_write;
|
||||
g_upload.progress = (int)(written * 100 / file_data.len);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
sync();
|
||||
g_upload.progress = 100;
|
||||
}
|
||||
|
||||
printf("Upload complete: %s\n", filepath);
|
||||
|
||||
memcpy(g_upload.type, type, strlen(type) + 1);
|
||||
g_upload.received = file_data.len;
|
||||
g_upload.progress = 100;
|
||||
snprintf(g_upload.status, sizeof(g_upload.status), "Complete");
|
||||
|
||||
@@ -15,6 +15,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
const mountStatus = document.getElementById("mount-status");
|
||||
const extlinuxEditor = document.getElementById("extlinux-editor");
|
||||
const editorStatus = document.getElementById("editor-status");
|
||||
const uploadHint = document.getElementById("upload-hint");
|
||||
|
||||
// Tab buttons
|
||||
const tabBtns = document.querySelectorAll(".tab-btn");
|
||||
@@ -32,6 +33,13 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
this.classList.add("active");
|
||||
uploadType.value = this.dataset.type;
|
||||
hideResult();
|
||||
|
||||
// Show/hide U-Boot warning
|
||||
if (this.dataset.type === "uboot_bin") {
|
||||
uploadHint.classList.add("visible");
|
||||
} else {
|
||||
uploadHint.classList.remove("visible");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -56,6 +56,9 @@
|
||||
<button class="tab-btn" data-type="uboot">
|
||||
U-Boot Environment
|
||||
</button>
|
||||
<button class="tab-btn" data-type="uboot_bin">
|
||||
U-Boot Binary (SPL)
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="upload-area">
|
||||
@@ -81,6 +84,11 @@
|
||||
<span class="file-name" id="file-name"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="upload-hint" id="upload-hint">
|
||||
⚠️ U-Boot Binary will be written to SPI NOR
|
||||
(/dev/mtd0) and SD card at 8KB offset (/dev/mmcblk1
|
||||
+8KB)
|
||||
</div>
|
||||
<button type="submit" class="upload-btn">Upload</button>
|
||||
</form>
|
||||
|
||||
|
||||
@@ -146,6 +146,21 @@ h2 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.upload-hint {
|
||||
margin: 15px 0;
|
||||
padding: 12px;
|
||||
background: rgba(255, 165, 0, 0.15);
|
||||
border: 1px solid rgba(255, 165, 0, 0.5);
|
||||
border-radius: 8px;
|
||||
color: #ffa500;
|
||||
font-size: 0.9rem;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.upload-hint.visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.file-input-wrapper {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user