From 5573ea1948fe594691f8f7015e6dd6337118c73b Mon Sep 17 00:00:00 2001 From: Leonmmcoset Date: Wed, 22 Apr 2026 22:12:58 +0800 Subject: [PATCH] partctl --- README.md | 4 + README.zh-CN.md | 4 + cleonos/CMakeLists.txt | 2 +- cleonos/c/apps/help_main.c | 1 + cleonos/c/apps/partctl_main.c | 469 ++++++++++++++++++++++++++++ cleonos/c/include/cleonos_syscall.h | 4 + cleonos/c/src/syscall.c | 8 + clks/include/clks/syscall.h | 2 + clks/kernel/runtime/syscall.c | 47 ++- docs/syscall.md | 24 +- wine/README.md | 8 +- wine/cleonos_wine_lib/constants.py | 2 + wine/cleonos_wine_lib/runner.py | 74 ++++- 13 files changed, 639 insertions(+), 10 deletions(-) create mode 100644 cleonos/c/apps/partctl_main.c diff --git a/README.md b/README.md index b19f1bc..355c600 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,10 @@ grep -n exec /shell/init.cmd cat /shell/init.cmd | grep -n exec ls /shell > /temp/shell_list.txt diskinfo +partctl list +partctl init-mbr +partctl create 1 2048 32768 0x0C 1 +partctl delete 1 mkfsfat32 CLEONOS mount /temp/disk write /temp/disk/hello.txt hello-disk diff --git a/README.zh-CN.md b/README.zh-CN.md index b16ec7e..b5a5326 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -111,6 +111,10 @@ grep -n exec /shell/init.cmd cat /shell/init.cmd | grep -n exec ls /shell > /temp/shell_list.txt diskinfo +partctl list +partctl init-mbr +partctl create 1 2048 32768 0x0C 1 +partctl delete 1 mkfsfat32 CLEONOS mount /temp/disk write /temp/disk/hello.txt hello-disk diff --git a/cleonos/CMakeLists.txt b/cleonos/CMakeLists.txt index 09302ba..b08e33f 100644 --- a/cleonos/CMakeLists.txt +++ b/cleonos/CMakeLists.txt @@ -130,7 +130,7 @@ set(USER_SHELL_COMMAND_APPS help args ls cat grep head tail wc cut uniq sort pwd cd exec pid spawn wait sleep yield bg fg jobs kill ps top procstat sysstat - diskinfo mkfsfat32 mount + diskinfo mkfsfat32 mount partctl shutdown restart exit clear ansi ansitest wavplay fastfetch memstat fsstat taskstat userstat shstat stats tty dmesg kbdstat mkdir touch write append cp mv rm kdbg bmpview qrcode ) diff --git a/cleonos/c/apps/help_main.c b/cleonos/c/apps/help_main.c index 1acd332..0435c24 100644 --- a/cleonos/c/apps/help_main.c +++ b/cleonos/c/apps/help_main.c @@ -32,6 +32,7 @@ static int ush_cmd_help(void) { ush_writeln(" diskinfo"); ush_writeln(" mkfsfat32 [label]"); ush_writeln(" mount [path] (default suggested: /temp/disk)"); + ush_writeln(" partctl (mbr partition control: list/init-mbr/create/delete/set-boot)"); ush_writeln(" pid"); ush_writeln(" spawn [args...] / bg [args...]"); ush_writeln(" wait / fg [pid]"); diff --git a/cleonos/c/apps/partctl_main.c b/cleonos/c/apps/partctl_main.c new file mode 100644 index 0000000..a21d6d1 --- /dev/null +++ b/cleonos/c/apps/partctl_main.c @@ -0,0 +1,469 @@ +#include "cmd_runtime.h" +#include +#include + +#define PARTCTL_SECTOR_SIZE 512U +#define PARTCTL_MBR_ENTRY_OFFSET 446U +#define PARTCTL_MBR_ENTRY_SIZE 16U +#define PARTCTL_MBR_ENTRY_COUNT 4U +#define PARTCTL_MBR_SIG_OFFSET 510U + +static unsigned int partctl_read_u32_le(const unsigned char *ptr) { + return (unsigned int)ptr[0] | ((unsigned int)ptr[1] << 8U) | ((unsigned int)ptr[2] << 16U) | ((unsigned int)ptr[3] << 24U); +} + +static void partctl_write_u32_le(unsigned char *ptr, unsigned int value) { + ptr[0] = (unsigned char)(value & 0xFFU); + ptr[1] = (unsigned char)((value >> 8U) & 0xFFU); + ptr[2] = (unsigned char)((value >> 16U) & 0xFFU); + ptr[3] = (unsigned char)((value >> 24U) & 0xFFU); +} + +static int partctl_parse_u64(const char *text, u64 *out_value) { + char *end = (char *)0; + unsigned long long value; + + if (text == (const char *)0 || text[0] == '\0' || out_value == (u64 *)0) { + return 0; + } + + value = strtoull(text, &end, 0); + if (end == text || *end != '\0') { + return 0; + } + + *out_value = (u64)value; + return 1; +} + +static int partctl_tokenize(const char *arg, char tokens[][USH_ARG_MAX], int max_tokens) { + int count = 0; + u64 i = 0ULL; + + if (arg == (const char *)0 || max_tokens <= 0) { + return 0; + } + + while (arg[i] != '\0' && count < max_tokens) { + u64 tlen = 0ULL; + + while (arg[i] != '\0' && ush_is_space(arg[i]) != 0) { + i++; + } + + if (arg[i] == '\0') { + break; + } + + while (arg[i] != '\0' && ush_is_space(arg[i]) == 0) { + if (tlen + 1ULL < USH_ARG_MAX) { + tokens[count][tlen++] = arg[i]; + } + i++; + } + + tokens[count][tlen] = '\0'; + count++; + } + + return count; +} + +static int partctl_disk_required(void) { + if (cleonos_sys_disk_present() == 0ULL) { + (void)fputs("partctl: disk not present\n", 1); + return 0; + } + return 1; +} + +static int partctl_read_sector0(unsigned char *sector) { + if (sector == (unsigned char *)0) { + return 0; + } + + if (partctl_disk_required() == 0) { + return 0; + } + + if (cleonos_sys_disk_read_sector(0ULL, (void *)sector) == 0ULL) { + (void)fputs("partctl: read sector0 failed\n", 1); + return 0; + } + + return 1; +} + +static int partctl_write_sector0(const unsigned char *sector) { + if (sector == (const unsigned char *)0) { + return 0; + } + + if (partctl_disk_required() == 0) { + return 0; + } + + if (cleonos_sys_disk_write_sector(0ULL, (const void *)sector) == 0ULL) { + (void)fputs("partctl: write sector0 failed\n", 1); + return 0; + } + + return 1; +} + +static int partctl_mbr_has_signature(const unsigned char *sector) { + return (sector[PARTCTL_MBR_SIG_OFFSET] == 0x55U && sector[PARTCTL_MBR_SIG_OFFSET + 1U] == 0xAAU) ? 1 : 0; +} + +static void partctl_mbr_ensure_signature(unsigned char *sector) { + sector[PARTCTL_MBR_SIG_OFFSET] = 0x55U; + sector[PARTCTL_MBR_SIG_OFFSET + 1U] = 0xAAU; +} + +static void partctl_usage(void) { + (void)fputs("partctl usage:\n", 1); + (void)fputs(" partctl list\n", 1); + (void)fputs(" partctl init-mbr\n", 1); + (void)fputs(" partctl create [boot:0|1]\n", 1); + (void)fputs(" partctl delete \n", 1); + (void)fputs(" partctl set-boot <0|1>\n", 1); +} + +static int partctl_cmd_list(void) { + unsigned char sector[PARTCTL_SECTOR_SIZE]; + u64 disk_sectors; + u64 i; + int has_any = 0; + + if (partctl_read_sector0(sector) == 0) { + return 0; + } + + disk_sectors = cleonos_sys_disk_sector_count(); + (void)printf("disk.sectors: %llu\n", (unsigned long long)disk_sectors); + (void)printf("mbr.signature: %s\n", partctl_mbr_has_signature(sector) != 0 ? "0x55AA" : "invalid"); + (void)fputs("idx boot type start_lba sectors end_lba\n", 1); + + for (i = 0ULL; i < PARTCTL_MBR_ENTRY_COUNT; i++) { + u64 off = PARTCTL_MBR_ENTRY_OFFSET + (i * PARTCTL_MBR_ENTRY_SIZE); + unsigned char boot = sector[off + 0ULL]; + unsigned char type = sector[off + 4ULL]; + unsigned int start = partctl_read_u32_le(§or[off + 8ULL]); + unsigned int size = partctl_read_u32_le(§or[off + 12ULL]); + u64 end_lba = 0ULL; + + if (size != 0U) { + has_any = 1; + end_lba = (u64)start + (u64)size - 1ULL; + } + + (void)printf("%llu %s 0x%02X %-12llu %-12llu ", + (unsigned long long)(i + 1ULL), (boot == 0x80U) ? "yes " : "no ", (unsigned int)type, + (unsigned long long)start, (unsigned long long)size); + + if (size == 0U) { + (void)fputs("-\n", 1); + } else { + (void)printf("%llu\n", (unsigned long long)end_lba); + } + } + + if (has_any == 0) { + (void)fputs("partctl: no partition entries\n", 1); + } + + return 1; +} + +static int partctl_check_overlap(const unsigned char *sector, u64 skip_index, u64 start, u64 sectors) { + u64 i; + u64 end = start + sectors; + + for (i = 0ULL; i < PARTCTL_MBR_ENTRY_COUNT; i++) { + u64 off; + unsigned int ex_start; + unsigned int ex_size; + u64 ex_end; + + if (i == skip_index) { + continue; + } + + off = PARTCTL_MBR_ENTRY_OFFSET + (i * PARTCTL_MBR_ENTRY_SIZE); + ex_start = partctl_read_u32_le(§or[off + 8ULL]); + ex_size = partctl_read_u32_le(§or[off + 12ULL]); + + if (ex_size == 0U) { + continue; + } + + ex_end = (u64)ex_start + (u64)ex_size; + if (!(end <= (u64)ex_start || start >= ex_end)) { + return 0; + } + } + + return 1; +} + +static int partctl_cmd_init_mbr(void) { + unsigned char sector[PARTCTL_SECTOR_SIZE]; + + if (partctl_disk_required() == 0) { + return 0; + } + + ush_zero((void *)sector, (u64)sizeof(sector)); + partctl_mbr_ensure_signature(sector); + + if (partctl_write_sector0(sector) == 0) { + return 0; + } + + (void)fputs("partctl: MBR initialized\n", 1); + return 1; +} + +static int partctl_cmd_delete(const char *index_text) { + unsigned char sector[PARTCTL_SECTOR_SIZE]; + u64 index = 0ULL; + u64 off; + + if (partctl_parse_u64(index_text, &index) == 0 || index < 1ULL || index > PARTCTL_MBR_ENTRY_COUNT) { + (void)fputs("partctl: delete usage: partctl delete \n", 1); + return 0; + } + + if (partctl_read_sector0(sector) == 0) { + return 0; + } + + off = PARTCTL_MBR_ENTRY_OFFSET + ((index - 1ULL) * PARTCTL_MBR_ENTRY_SIZE); + ush_zero((void *)§or[off], PARTCTL_MBR_ENTRY_SIZE); + partctl_mbr_ensure_signature(sector); + + if (partctl_write_sector0(sector) == 0) { + return 0; + } + + (void)printf("partctl: deleted partition %llu\n", (unsigned long long)index); + return 1; +} + +static int partctl_cmd_set_boot(const char *index_text, const char *value_text) { + unsigned char sector[PARTCTL_SECTOR_SIZE]; + u64 index = 0ULL; + u64 value = 0ULL; + u64 off; + u64 i; + + if (partctl_parse_u64(index_text, &index) == 0 || index < 1ULL || index > PARTCTL_MBR_ENTRY_COUNT) { + (void)fputs("partctl: set-boot usage: partctl set-boot <0|1>\n", 1); + return 0; + } + + if (partctl_parse_u64(value_text, &value) == 0 || (value != 0ULL && value != 1ULL)) { + (void)fputs("partctl: set-boot usage: partctl set-boot <0|1>\n", 1); + return 0; + } + + if (partctl_read_sector0(sector) == 0) { + return 0; + } + + for (i = 0ULL; i < PARTCTL_MBR_ENTRY_COUNT; i++) { + u64 eoff = PARTCTL_MBR_ENTRY_OFFSET + (i * PARTCTL_MBR_ENTRY_SIZE); + if (value != 0ULL && (i + 1ULL) != index) { + sector[eoff] = 0x00U; + } + } + + off = PARTCTL_MBR_ENTRY_OFFSET + ((index - 1ULL) * PARTCTL_MBR_ENTRY_SIZE); + sector[off] = (value != 0ULL) ? 0x80U : 0x00U; + partctl_mbr_ensure_signature(sector); + + if (partctl_write_sector0(sector) == 0) { + return 0; + } + + (void)printf("partctl: partition %llu boot flag=%llu\n", (unsigned long long)index, (unsigned long long)value); + return 1; +} + +static int partctl_cmd_create(const char *index_text, const char *start_text, const char *sectors_text, + const char *type_text, const char *boot_text) { + unsigned char sector[PARTCTL_SECTOR_SIZE]; + u64 index = 0ULL; + u64 start_lba = 0ULL; + u64 sectors = 0ULL; + u64 type = 0ULL; + u64 boot = 0ULL; + u64 disk_sectors; + u64 off; + u64 i; + + if (partctl_parse_u64(index_text, &index) == 0 || index < 1ULL || index > PARTCTL_MBR_ENTRY_COUNT) { + (void)fputs("partctl: create invalid index\n", 1); + return 0; + } + + if (partctl_parse_u64(start_text, &start_lba) == 0 || partctl_parse_u64(sectors_text, §ors) == 0 || + partctl_parse_u64(type_text, &type) == 0) { + (void)fputs("partctl: create invalid number\n", 1); + return 0; + } + + if (boot_text != (const char *)0 && boot_text[0] != '\0') { + if (partctl_parse_u64(boot_text, &boot) == 0 || (boot != 0ULL && boot != 1ULL)) { + (void)fputs("partctl: create invalid boot flag (expected 0|1)\n", 1); + return 0; + } + } + + if (start_lba == 0ULL || sectors == 0ULL) { + (void)fputs("partctl: start_lba and sectors must be > 0\n", 1); + return 0; + } + + if (type > 0xFFULL || start_lba > 0xFFFFFFFFULL || sectors > 0xFFFFFFFFULL) { + (void)fputs("partctl: MBR supports only 32-bit LBA/size and 8-bit type\n", 1); + return 0; + } + + disk_sectors = cleonos_sys_disk_sector_count(); + if (start_lba >= disk_sectors || sectors > disk_sectors || (start_lba + sectors) > disk_sectors || + (start_lba + sectors) < start_lba) { + (void)fputs("partctl: range exceeds disk size\n", 1); + return 0; + } + + if (partctl_read_sector0(sector) == 0) { + return 0; + } + + if (partctl_check_overlap(sector, index - 1ULL, start_lba, sectors) == 0) { + (void)fputs("partctl: range overlaps an existing partition\n", 1); + return 0; + } + + if (boot != 0ULL) { + for (i = 0ULL; i < PARTCTL_MBR_ENTRY_COUNT; i++) { + u64 eoff = PARTCTL_MBR_ENTRY_OFFSET + (i * PARTCTL_MBR_ENTRY_SIZE); + sector[eoff] = 0x00U; + } + } + + off = PARTCTL_MBR_ENTRY_OFFSET + ((index - 1ULL) * PARTCTL_MBR_ENTRY_SIZE); + ush_zero((void *)§or[off], PARTCTL_MBR_ENTRY_SIZE); + sector[off + 0ULL] = (boot != 0ULL) ? 0x80U : 0x00U; + sector[off + 1ULL] = 0xFFU; + sector[off + 2ULL] = 0xFFU; + sector[off + 3ULL] = 0xFFU; + sector[off + 4ULL] = (unsigned char)type; + sector[off + 5ULL] = 0xFFU; + sector[off + 6ULL] = 0xFFU; + sector[off + 7ULL] = 0xFFU; + partctl_write_u32_le(§or[off + 8ULL], (unsigned int)start_lba); + partctl_write_u32_le(§or[off + 12ULL], (unsigned int)sectors); + partctl_mbr_ensure_signature(sector); + + if (partctl_write_sector0(sector) == 0) { + return 0; + } + + (void)printf("partctl: created p%llu start=%llu sectors=%llu type=0x%02X boot=%llu\n", + (unsigned long long)index, (unsigned long long)start_lba, (unsigned long long)sectors, + (unsigned int)((unsigned char)type), (unsigned long long)boot); + return 1; +} + +static int partctl_run(const char *arg) { + char tokens[8][USH_ARG_MAX]; + int token_count = partctl_tokenize(arg, tokens, 8); + + if (token_count == 0 || ush_streq(tokens[0], "help") != 0) { + partctl_usage(); + return 1; + } + + if (ush_streq(tokens[0], "list") != 0) { + return partctl_cmd_list(); + } + + if (ush_streq(tokens[0], "init-mbr") != 0) { + return partctl_cmd_init_mbr(); + } + + if (ush_streq(tokens[0], "delete") != 0) { + if (token_count != 2) { + partctl_usage(); + return 0; + } + return partctl_cmd_delete(tokens[1]); + } + + if (ush_streq(tokens[0], "set-boot") != 0) { + if (token_count != 3) { + partctl_usage(); + return 0; + } + return partctl_cmd_set_boot(tokens[1], tokens[2]); + } + + if (ush_streq(tokens[0], "create") != 0) { + if (token_count != 5 && token_count != 6) { + partctl_usage(); + return 0; + } + return partctl_cmd_create(tokens[1], tokens[2], tokens[3], tokens[4], (token_count == 6) ? tokens[5] : ""); + } + + (void)fputs("partctl: unknown subcommand\n", 1); + partctl_usage(); + return 0; +} + +int cleonos_app_main(void) { + ush_cmd_ctx ctx; + ush_cmd_ret ret; + ush_state sh; + char initial_cwd[USH_PATH_MAX]; + int has_context = 0; + int success = 0; + const char *arg = ""; + + ush_zero(&ctx, (u64)sizeof(ctx)); + ush_zero(&ret, (u64)sizeof(ret)); + ush_init_state(&sh); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + + if (ush_command_ctx_read(&ctx) != 0) { + if (ctx.cmd[0] != '\0' && ush_streq(ctx.cmd, "partctl") != 0) { + has_context = 1; + arg = ctx.arg; + if (ctx.cwd[0] == '/') { + ush_copy(sh.cwd, (u64)sizeof(sh.cwd), ctx.cwd); + ush_copy(initial_cwd, (u64)sizeof(initial_cwd), sh.cwd); + } + } + } + + success = partctl_run(arg); + + if (has_context != 0) { + if (ush_streq(sh.cwd, initial_cwd) == 0) { + ret.flags |= USH_CMD_RET_FLAG_CWD; + ush_copy(ret.cwd, (u64)sizeof(ret.cwd), sh.cwd); + } + + if (sh.exit_requested != 0) { + ret.flags |= USH_CMD_RET_FLAG_EXIT; + ret.exit_code = sh.exit_code; + } + + (void)ush_command_ret_write(&ret); + } + + return (success != 0) ? 0 : 1; +} + diff --git a/cleonos/c/include/cleonos_syscall.h b/cleonos/c/include/cleonos_syscall.h index 764a91e..9320b97 100644 --- a/cleonos/c/include/cleonos_syscall.h +++ b/cleonos/c/include/cleonos_syscall.h @@ -153,6 +153,8 @@ typedef struct cleonos_fb_blit_req { #define CLEONOS_SYSCALL_DISK_MOUNT 90ULL #define CLEONOS_SYSCALL_DISK_MOUNTED 91ULL #define CLEONOS_SYSCALL_DISK_MOUNT_PATH 92ULL +#define CLEONOS_SYSCALL_DISK_READ_SECTOR 93ULL +#define CLEONOS_SYSCALL_DISK_WRITE_SECTOR 94ULL u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); u64 cleonos_sys_log_write(const char *message, u64 length); @@ -248,5 +250,7 @@ u64 cleonos_sys_disk_format_fat32(const char *label); u64 cleonos_sys_disk_mount(const char *mount_path); u64 cleonos_sys_disk_mounted(void); u64 cleonos_sys_disk_mount_path(char *out_path, u64 out_size); +u64 cleonos_sys_disk_read_sector(u64 lba, void *out_sector); +u64 cleonos_sys_disk_write_sector(u64 lba, const void *sector_data); #endif diff --git a/cleonos/c/src/syscall.c b/cleonos/c/src/syscall.c index 644430f..d4ddcec 100644 --- a/cleonos/c/src/syscall.c +++ b/cleonos/c/src/syscall.c @@ -403,3 +403,11 @@ u64 cleonos_sys_disk_mounted(void) { u64 cleonos_sys_disk_mount_path(char *out_path, u64 out_size) { return cleonos_syscall(CLEONOS_SYSCALL_DISK_MOUNT_PATH, (u64)out_path, out_size, 0ULL); } + +u64 cleonos_sys_disk_read_sector(u64 lba, void *out_sector) { + return cleonos_syscall(CLEONOS_SYSCALL_DISK_READ_SECTOR, lba, (u64)out_sector, 0ULL); +} + +u64 cleonos_sys_disk_write_sector(u64 lba, const void *sector_data) { + return cleonos_syscall(CLEONOS_SYSCALL_DISK_WRITE_SECTOR, lba, (u64)sector_data, 0ULL); +} diff --git a/clks/include/clks/syscall.h b/clks/include/clks/syscall.h index 15418a2..2c2679f 100644 --- a/clks/include/clks/syscall.h +++ b/clks/include/clks/syscall.h @@ -96,6 +96,8 @@ #define CLKS_SYSCALL_DISK_MOUNT 90ULL #define CLKS_SYSCALL_DISK_MOUNTED 91ULL #define CLKS_SYSCALL_DISK_MOUNT_PATH 92ULL +#define CLKS_SYSCALL_DISK_READ_SECTOR 93ULL +#define CLKS_SYSCALL_DISK_WRITE_SECTOR 94ULL void clks_syscall_init(void); u64 clks_syscall_dispatch(void *frame_ptr); diff --git a/clks/kernel/runtime/syscall.c b/clks/kernel/runtime/syscall.c index 714eb9b..7b7f2f2 100644 --- a/clks/kernel/runtime/syscall.c +++ b/clks/kernel/runtime/syscall.c @@ -37,7 +37,8 @@ #define CLKS_SYSCALL_KDBG_STACK_WINDOW_BYTES (128ULL * 1024ULL) #define CLKS_SYSCALL_KERNEL_SYMBOL_FILE "/system/kernel.sym" #define CLKS_SYSCALL_KERNEL_ADDR_BASE 0xFFFF800000000000ULL -#define CLKS_SYSCALL_STATS_MAX_ID CLKS_SYSCALL_DISK_MOUNT_PATH +#define CLKS_SYSCALL_STATS_MAX_ID CLKS_SYSCALL_DISK_WRITE_SECTOR +#define CLKS_SYSCALL_DISK_SECTOR_BYTES 512U #define CLKS_SYSCALL_STATS_RING_SIZE 256U #define CLKS_SYSCALL_USC_MAX_ALLOWED_APPS 64U @@ -530,6 +531,40 @@ static u64 clks_syscall_disk_mount_path(u64 arg0, u64 arg1) { return clks_syscall_copy_text_to_user(arg0, arg1, mount_path, clks_strlen(mount_path)); } +static u64 clks_syscall_disk_read_sector(u64 arg0, u64 arg1) { + u8 sector[CLKS_SYSCALL_DISK_SECTOR_BYTES]; + + if (arg1 == 0ULL) { + return 0ULL; + } + + if (clks_syscall_user_ptr_writable(arg1, (u64)CLKS_SYSCALL_DISK_SECTOR_BYTES) == CLKS_FALSE) { + return 0ULL; + } + + if (clks_disk_read_sector(arg0, (void *)sector) == CLKS_FALSE) { + return 0ULL; + } + + clks_memcpy((void *)arg1, sector, (usize)CLKS_SYSCALL_DISK_SECTOR_BYTES); + return 1ULL; +} + +static u64 clks_syscall_disk_write_sector(u64 arg0, u64 arg1) { + u8 sector[CLKS_SYSCALL_DISK_SECTOR_BYTES]; + + if (arg1 == 0ULL) { + return 0ULL; + } + + if (clks_syscall_user_ptr_readable(arg1, (u64)CLKS_SYSCALL_DISK_SECTOR_BYTES) == CLKS_FALSE) { + return 0ULL; + } + + clks_memcpy(sector, (const void *)arg1, (usize)CLKS_SYSCALL_DISK_SECTOR_BYTES); + return (clks_disk_write_sector(arg0, (const void *)sector) == CLKS_TRUE) ? 1ULL : 0ULL; +} + static u64 clks_syscall_fd_open(u64 arg0, u64 arg1, u64 arg2) { char path[CLKS_SYSCALL_PATH_MAX]; @@ -2061,6 +2096,10 @@ static const char *clks_syscall_usc_syscall_name(u64 id) { return "DISK_MOUNTED"; case CLKS_SYSCALL_DISK_MOUNT_PATH: return "DISK_MOUNT_PATH"; + case CLKS_SYSCALL_DISK_READ_SECTOR: + return "DISK_READ_SECTOR"; + case CLKS_SYSCALL_DISK_WRITE_SECTOR: + return "DISK_WRITE_SECTOR"; default: return "UNKNOWN"; } @@ -2094,6 +2133,8 @@ static clks_bool clks_syscall_usc_is_dangerous(u64 id) { return (CLKS_CFG_USC_SC_RESTART != 0) ? CLKS_TRUE : CLKS_FALSE; case CLKS_SYSCALL_DISK_FORMAT_FAT32: return CLKS_TRUE; + case CLKS_SYSCALL_DISK_WRITE_SECTOR: + return CLKS_TRUE; default: return CLKS_FALSE; } @@ -2613,6 +2654,10 @@ u64 clks_syscall_dispatch(void *frame_ptr) { return clks_syscall_disk_mounted(); case CLKS_SYSCALL_DISK_MOUNT_PATH: return clks_syscall_disk_mount_path(frame->rbx, frame->rcx); + case CLKS_SYSCALL_DISK_READ_SECTOR: + return clks_syscall_disk_read_sector(frame->rbx, frame->rcx); + case CLKS_SYSCALL_DISK_WRITE_SECTOR: + return clks_syscall_disk_write_sector(frame->rbx, frame->rcx); default: return (u64)-1; } diff --git a/docs/syscall.md b/docs/syscall.md index e0a6858..2da5655 100644 --- a/docs/syscall.md +++ b/docs/syscall.md @@ -83,7 +83,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - `/proc/`:指定 PID 快照文本 - `/proc` 为只读;写入类 syscall 不支持。 -## 4. Syscall 列表(0~92) +## 4. Syscall 列表(0~94) ### 0 `CLEONOS_SYSCALL_LOG_WRITE` @@ -715,6 +715,22 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - 返回:实际写入字节数(不含终止符),失败返回 `0` - 说明:查询当前挂载点路径;返回值为写入长度,且内核保证 `\0` 终止。 +### 93 `CLEONOS_SYSCALL_DISK_READ_SECTOR` + +- 参数: +- `arg0`: `u64 lba` +- `arg1`: `void *out_sector`(512 字节缓冲区) +- 返回:成功 `1`,失败 `0` +- 说明:按 LBA 读取单个扇区(512B)到用户缓冲区,常用于分区表/低级磁盘工具。 + +### 94 `CLEONOS_SYSCALL_DISK_WRITE_SECTOR` + +- 参数: +- `arg0`: `u64 lba` +- `arg1`: `const void *sector_data`(512 字节缓冲区) +- 返回:成功 `1`,失败 `0` +- 说明:按 LBA 写入单个扇区(512B)。该 syscall 在 USC 策略中默认视为高风险操作。 + ## 5. 用户态封装函数 用户态封装位于: @@ -746,6 +762,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - `cleonos_sys_kernel_version()` - `cleonos_sys_disk_present()` / `cleonos_sys_disk_size_bytes()` / `cleonos_sys_disk_sector_count()` - `cleonos_sys_disk_formatted()` / `cleonos_sys_disk_format_fat32()` / `cleonos_sys_disk_mount()` / `cleonos_sys_disk_mounted()` / `cleonos_sys_disk_mount_path()` +- `cleonos_sys_disk_read_sector()` / `cleonos_sys_disk_write_sector()` ## 6. 开发注意事项 @@ -756,7 +773,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); ## 7. Wine 兼容说明 -- `wine/cleonos_wine_lib/runner.py` 当前已覆盖到 `0..92`(含 `DL_*`、`FB_*`、`KERNEL_VERSION`、`DISK_*`)。 +- `wine/cleonos_wine_lib/runner.py` 当前已覆盖到 `0..94`(含 `DL_*`、`FB_*`、`KERNEL_VERSION`、`DISK_*`)。 - `DL_*`(`77..79`)在 Wine 中为“可运行兼容”实现: - `DL_OPEN`:加载 guest ELF 到当前 Unicorn 地址空间,返回稳定 `handle`,并做引用计数。 - `DL_SYM`:解析 ELF `SYMTAB/DYNSYM` 并返回 guest 可调用地址。 @@ -768,10 +785,11 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2); - 配合 Wine 参数 `--fb-window` 可将 framebuffer 实时显示到主机窗口(pygame 后端);未启用时保持内存缓冲模式。 - `FB_CLEAR` 支持清屏颜色写入。 - `KERNEL_VERSION`(`84`)在 Wine 中返回内核版本字符串(当前默认 `1.0.0-alpha`)。 -- `DISK_*`(`85..92`)在 Wine 中已实现: +- `DISK_*`(`85..94`)在 Wine 中已实现: - 提供虚拟磁盘容量信息与 FAT32 格式化状态查询。 - `DISK_FORMAT_FAT32` 会初始化/重置 Wine rootfs 下的虚拟磁盘目录。 - `DISK_MOUNT`/`DISK_MOUNT_PATH` 支持挂载点管理,并与 `FS_MKDIR/WRITE/APPEND/REMOVE` 的路径规则联动。 +- `DISK_READ_SECTOR`/`DISK_WRITE_SECTOR`(`93..94`)在 Wine 中已实现为 512B 原始扇区读写(host 文件后端)。 - Wine 在运行时崩溃场景下会生成与内核一致格式的“信号编码退出状态”,可通过 `WAITPID` 读取。 - Wine 当前音频 syscall 为占位实现:`AUDIO_AVAILABLE=0`,`AUDIO_PLAY_TONE=0`,`AUDIO_STOP=1`。 - Wine 版本号策略固定为 `85.0.0-wine`(历史兼容号;不会随 syscall 扩展继续增长)。 diff --git a/wine/README.md b/wine/README.md index 803df2f..5d76c47 100644 --- a/wine/README.md +++ b/wine/README.md @@ -38,7 +38,7 @@ python wine/cleonos_wine.py build/x86_64/ramdisk_root/shell/shell.elf --rootfs b ## 支持 - ELF64 (x86_64) PT_LOAD 段装载 -- CLeonOS `int 0x80` syscall 0..92(含 `FD_*`、`DL_*`、`FB_*`、`PROC_*`、`STATS_*`、`EXEC_PATHV_IO`、`KERNEL_VERSION`、`DISK_*`) +- CLeonOS `int 0x80` syscall 0..94(含 `FD_*`、`DL_*`、`FB_*`、`PROC_*`、`STATS_*`、`EXEC_PATHV_IO`、`KERNEL_VERSION`、`DISK_*`) - TTY 输出与键盘输入队列 - rootfs 文件/目录访问(`FS_*`) - `/temp` 与已挂载磁盘路径写入限制(`FS_MKDIR/WRITE/APPEND/REMOVE`) @@ -52,14 +52,14 @@ python wine/cleonos_wine.py build/x86_64/ramdisk_root/shell/shell.elf --rootfs b - 动态库兼容加载(`DL_OPEN/DL_CLOSE/DL_SYM`,基于 ELF 符号解析) - framebuffer 兼容(`FB_INFO/FB_BLIT/FB_CLEAR`,支持内存缓冲与窗口显示) - 内核版本查询(`KERNEL_VERSION`) -- 磁盘接口兼容(`DISK_PRESENT/SIZE_BYTES/SECTOR_COUNT/FORMATTED/FORMAT_FAT32/MOUNT/MOUNTED/MOUNT_PATH`) -- Wine 虚拟磁盘目录默认位于 `/__clks_disk0__`(格式化标记文件 `.fat32`) +- 磁盘接口兼容(`DISK_PRESENT/SIZE_BYTES/SECTOR_COUNT/FORMATTED/FORMAT_FAT32/MOUNT/MOUNTED/MOUNT_PATH/READ_SECTOR/WRITE_SECTOR`) +- Wine 虚拟磁盘目录默认位于 `/__clks_disk0__`(格式化标记文件 `.fat32`,原始扇区后端文件 `.rawdisk.img`) - 异常退出状态编码与故障元信息(`PROC_LAST_SIGNAL/PROC_FAULT_*`) ## 版本策略 - CLeonOS-Wine 版本号固定为:`85.0.0-wine` -- 该值按项目策略固定,不再随新增 syscall 变更(即使当前实现范围已扩展到 `0..92`) +- 该值按项目策略固定,不再随新增 syscall 变更(即使当前实现范围已扩展到 `0..94`) ## 参数 diff --git a/wine/cleonos_wine_lib/constants.py b/wine/cleonos_wine_lib/constants.py index e5ec14d..3f4e96a 100644 --- a/wine/cleonos_wine_lib/constants.py +++ b/wine/cleonos_wine_lib/constants.py @@ -107,6 +107,8 @@ SYS_DISK_FORMAT_FAT32 = 89 SYS_DISK_MOUNT = 90 SYS_DISK_MOUNTED = 91 SYS_DISK_MOUNT_PATH = 92 +SYS_DISK_READ_SECTOR = 93 +SYS_DISK_WRITE_SECTOR = 94 # proc states (from cleonos/c/include/cleonos_syscall.h) PROC_STATE_UNUSED = 0 diff --git a/wine/cleonos_wine_lib/runner.py b/wine/cleonos_wine_lib/runner.py index 69f3aa2..d7ceb6f 100644 --- a/wine/cleonos_wine_lib/runner.py +++ b/wine/cleonos_wine_lib/runner.py @@ -69,8 +69,10 @@ from .constants import ( SYS_DISK_MOUNTED, SYS_DISK_MOUNT_PATH, SYS_DISK_PRESENT, + SYS_DISK_READ_SECTOR, SYS_DISK_SECTOR_COUNT, SYS_DISK_SIZE_BYTES, + SYS_DISK_WRITE_SECTOR, SYS_GETPID, SYS_KERNEL_VERSION, SYS_KBD_BUFFERED, @@ -286,10 +288,12 @@ class CLeonOSWineNative: self._disk_mount_path = "/temp/disk" self._disk_root = (self.rootfs / "__clks_disk0__").resolve() self._disk_marker = self._disk_root / ".fat32" + self._disk_image_file = self._disk_root / ".rawdisk.img" self._disk_formatted = False self._disk_mounted = False try: self._disk_root.mkdir(parents=True, exist_ok=True) + self._disk_prepare_raw_image() if self._disk_marker.exists(): self._disk_formatted = True self._disk_mounted = True @@ -762,6 +766,16 @@ class CLeonOSWineNative: return 1 if self._disk_mounted else 0 if sid == SYS_DISK_MOUNT_PATH: return self._disk_mount_path_query(uc, arg0, arg1) + if sid == SYS_DISK_READ_SECTOR: + sector_data = self._disk_read_sector_raw(int(arg0)) + if sector_data is None: + return 0 + return 1 if self._write_guest_bytes(uc, int(arg1), sector_data) else 0 + if sid == SYS_DISK_WRITE_SECTOR: + sector_data = self._read_guest_bytes_exact(uc, int(arg1), 512) + if sector_data is None: + return 0 + return 1 if self._disk_write_sector_raw(int(arg0), sector_data) else 0 return u64_neg1() @@ -1080,20 +1094,78 @@ class CLeonOSWineNative: return None return host + def _disk_prepare_raw_image(self) -> None: + target_size = int(self._disk_size_bytes) + + if target_size < 512: + raise RuntimeError("wine disk image size too small") + + if not self._disk_image_file.exists(): + with open(self._disk_image_file, "wb") as fp: + fp.truncate(target_size) + return + + current_size = int(self._disk_image_file.stat().st_size) + if current_size != target_size: + with open(self._disk_image_file, "r+b") as fp: + fp.truncate(target_size) + + def _disk_read_sector_raw(self, lba: int) -> Optional[bytes]: + if not self._disk_present: + return None + if lba < 0: + return None + offset = int(lba) * 512 + if offset + 512 > int(self._disk_size_bytes): + return None + try: + with open(self._disk_image_file, "rb") as fp: + fp.seek(offset) + data = fp.read(512) + if len(data) != 512: + return None + return data + except Exception: + return None + + def _disk_write_sector_raw(self, lba: int, data: bytes) -> bool: + if not self._disk_present: + return False + if lba < 0: + return False + if len(data) != 512: + return False + offset = int(lba) * 512 + if offset + 512 > int(self._disk_size_bytes): + return False + try: + with open(self._disk_image_file, "r+b") as fp: + fp.seek(offset) + fp.write(data) + fp.flush() + return True + except Exception: + return False + def _disk_format_fat32(self, label: str) -> int: _ = label if not self._disk_present: return 0 try: + self._disk_prepare_raw_image() self._disk_root.mkdir(parents=True, exist_ok=True) for child in list(self._disk_root.iterdir()): - if child.name == self._disk_marker.name: + if child.name == self._disk_marker.name or child.name == self._disk_image_file.name: continue if child.is_dir(): shutil.rmtree(child) else: child.unlink() + with open(self._disk_image_file, "r+b") as fp: + fp.seek(0) + fp.write(b"\x00" * 512) + fp.flush() self._disk_marker.write_text("FAT32\n", encoding="utf-8") self._disk_formatted = True return 1