This commit is contained in:
2026-04-22 22:12:58 +08:00
parent 0fe34d9a09
commit 5573ea1948
13 changed files with 639 additions and 10 deletions

View File

@@ -111,6 +111,10 @@ grep -n exec /shell/init.cmd
cat /shell/init.cmd | grep -n exec cat /shell/init.cmd | grep -n exec
ls /shell > /temp/shell_list.txt ls /shell > /temp/shell_list.txt
diskinfo diskinfo
partctl list
partctl init-mbr
partctl create 1 2048 32768 0x0C 1
partctl delete 1
mkfsfat32 CLEONOS mkfsfat32 CLEONOS
mount /temp/disk mount /temp/disk
write /temp/disk/hello.txt hello-disk write /temp/disk/hello.txt hello-disk

View File

@@ -111,6 +111,10 @@ grep -n exec /shell/init.cmd
cat /shell/init.cmd | grep -n exec cat /shell/init.cmd | grep -n exec
ls /shell > /temp/shell_list.txt ls /shell > /temp/shell_list.txt
diskinfo diskinfo
partctl list
partctl init-mbr
partctl create 1 2048 32768 0x0C 1
partctl delete 1
mkfsfat32 CLEONOS mkfsfat32 CLEONOS
mount /temp/disk mount /temp/disk
write /temp/disk/hello.txt hello-disk write /temp/disk/hello.txt hello-disk

View File

@@ -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 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 bg fg jobs kill ps top
procstat sysstat procstat sysstat
diskinfo mkfsfat32 mount diskinfo mkfsfat32 mount partctl
shutdown restart exit clear ansi ansitest wavplay fastfetch memstat fsstat taskstat userstat 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 shstat stats tty dmesg kbdstat mkdir touch write append cp mv rm kdbg bmpview qrcode
) )

View File

@@ -32,6 +32,7 @@ static int ush_cmd_help(void) {
ush_writeln(" diskinfo"); ush_writeln(" diskinfo");
ush_writeln(" mkfsfat32 [label]"); ush_writeln(" mkfsfat32 [label]");
ush_writeln(" mount [path] (default suggested: /temp/disk)"); ush_writeln(" mount [path] (default suggested: /temp/disk)");
ush_writeln(" partctl <subcommand> (mbr partition control: list/init-mbr/create/delete/set-boot)");
ush_writeln(" pid"); ush_writeln(" pid");
ush_writeln(" spawn <path|name> [args...] / bg <path|name> [args...]"); ush_writeln(" spawn <path|name> [args...] / bg <path|name> [args...]");
ush_writeln(" wait <pid> / fg [pid]"); ush_writeln(" wait <pid> / fg [pid]");

View File

@@ -0,0 +1,469 @@
#include "cmd_runtime.h"
#include <stdio.h>
#include <stdlib.h>
#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 <index:1-4> <start_lba> <sectors> <type_hex> [boot:0|1]\n", 1);
(void)fputs(" partctl delete <index:1-4>\n", 1);
(void)fputs(" partctl set-boot <index:1-4> <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(&sector[off + 8ULL]);
unsigned int size = partctl_read_u32_le(&sector[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(&sector[off + 8ULL]);
ex_size = partctl_read_u32_le(&sector[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 <index:1-4>\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 *)&sector[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 <index:1-4> <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 <index:1-4> <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, &sectors) == 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 *)&sector[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(&sector[off + 8ULL], (unsigned int)start_lba);
partctl_write_u32_le(&sector[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;
}

View File

@@ -153,6 +153,8 @@ typedef struct cleonos_fb_blit_req {
#define CLEONOS_SYSCALL_DISK_MOUNT 90ULL #define CLEONOS_SYSCALL_DISK_MOUNT 90ULL
#define CLEONOS_SYSCALL_DISK_MOUNTED 91ULL #define CLEONOS_SYSCALL_DISK_MOUNTED 91ULL
#define CLEONOS_SYSCALL_DISK_MOUNT_PATH 92ULL #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_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2);
u64 cleonos_sys_log_write(const char *message, u64 length); 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_mount(const char *mount_path);
u64 cleonos_sys_disk_mounted(void); u64 cleonos_sys_disk_mounted(void);
u64 cleonos_sys_disk_mount_path(char *out_path, u64 out_size); 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 #endif

View File

@@ -403,3 +403,11 @@ u64 cleonos_sys_disk_mounted(void) {
u64 cleonos_sys_disk_mount_path(char *out_path, u64 out_size) { 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); 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);
}

View File

@@ -96,6 +96,8 @@
#define CLKS_SYSCALL_DISK_MOUNT 90ULL #define CLKS_SYSCALL_DISK_MOUNT 90ULL
#define CLKS_SYSCALL_DISK_MOUNTED 91ULL #define CLKS_SYSCALL_DISK_MOUNTED 91ULL
#define CLKS_SYSCALL_DISK_MOUNT_PATH 92ULL #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); void clks_syscall_init(void);
u64 clks_syscall_dispatch(void *frame_ptr); u64 clks_syscall_dispatch(void *frame_ptr);

View File

@@ -37,7 +37,8 @@
#define CLKS_SYSCALL_KDBG_STACK_WINDOW_BYTES (128ULL * 1024ULL) #define CLKS_SYSCALL_KDBG_STACK_WINDOW_BYTES (128ULL * 1024ULL)
#define CLKS_SYSCALL_KERNEL_SYMBOL_FILE "/system/kernel.sym" #define CLKS_SYSCALL_KERNEL_SYMBOL_FILE "/system/kernel.sym"
#define CLKS_SYSCALL_KERNEL_ADDR_BASE 0xFFFF800000000000ULL #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_STATS_RING_SIZE 256U
#define CLKS_SYSCALL_USC_MAX_ALLOWED_APPS 64U #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)); 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) { static u64 clks_syscall_fd_open(u64 arg0, u64 arg1, u64 arg2) {
char path[CLKS_SYSCALL_PATH_MAX]; char path[CLKS_SYSCALL_PATH_MAX];
@@ -2061,6 +2096,10 @@ static const char *clks_syscall_usc_syscall_name(u64 id) {
return "DISK_MOUNTED"; return "DISK_MOUNTED";
case CLKS_SYSCALL_DISK_MOUNT_PATH: case CLKS_SYSCALL_DISK_MOUNT_PATH:
return "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: default:
return "UNKNOWN"; 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; return (CLKS_CFG_USC_SC_RESTART != 0) ? CLKS_TRUE : CLKS_FALSE;
case CLKS_SYSCALL_DISK_FORMAT_FAT32: case CLKS_SYSCALL_DISK_FORMAT_FAT32:
return CLKS_TRUE; return CLKS_TRUE;
case CLKS_SYSCALL_DISK_WRITE_SECTOR:
return CLKS_TRUE;
default: default:
return CLKS_FALSE; return CLKS_FALSE;
} }
@@ -2613,6 +2654,10 @@ u64 clks_syscall_dispatch(void *frame_ptr) {
return clks_syscall_disk_mounted(); return clks_syscall_disk_mounted();
case CLKS_SYSCALL_DISK_MOUNT_PATH: case CLKS_SYSCALL_DISK_MOUNT_PATH:
return clks_syscall_disk_mount_path(frame->rbx, frame->rcx); 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: default:
return (u64)-1; return (u64)-1;
} }

View File

@@ -83,7 +83,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2);
- `/proc/<pid>`:指定 PID 快照文本 - `/proc/<pid>`:指定 PID 快照文本
- `/proc` 为只读;写入类 syscall 不支持。 - `/proc` 为只读;写入类 syscall 不支持。
## 4. Syscall 列表0~92 ## 4. Syscall 列表0~94
### 0 `CLEONOS_SYSCALL_LOG_WRITE` ### 0 `CLEONOS_SYSCALL_LOG_WRITE`
@@ -715,6 +715,22 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2);
- 返回:实际写入字节数(不含终止符),失败返回 `0` - 返回:实际写入字节数(不含终止符),失败返回 `0`
- 说明:查询当前挂载点路径;返回值为写入长度,且内核保证 `\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. 用户态封装函数 ## 5. 用户态封装函数
用户态封装位于: 用户态封装位于:
@@ -746,6 +762,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2);
- `cleonos_sys_kernel_version()` - `cleonos_sys_kernel_version()`
- `cleonos_sys_disk_present()` / `cleonos_sys_disk_size_bytes()` / `cleonos_sys_disk_sector_count()` - `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_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. 开发注意事项 ## 6. 开发注意事项
@@ -756,7 +773,7 @@ u64 cleonos_syscall(u64 id, u64 arg0, u64 arg1, u64 arg2);
## 7. Wine 兼容说明 ## 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_*``77..79`)在 Wine 中为“可运行兼容”实现:
- `DL_OPEN`:加载 guest ELF 到当前 Unicorn 地址空间,返回稳定 `handle`,并做引用计数。 - `DL_OPEN`:加载 guest ELF 到当前 Unicorn 地址空间,返回稳定 `handle`,并做引用计数。
- `DL_SYM`:解析 ELF `SYMTAB/DYNSYM` 并返回 guest 可调用地址。 - `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 后端);未启用时保持内存缓冲模式。 - 配合 Wine 参数 `--fb-window` 可将 framebuffer 实时显示到主机窗口pygame 后端);未启用时保持内存缓冲模式。
- `FB_CLEAR` 支持清屏颜色写入。 - `FB_CLEAR` 支持清屏颜色写入。
- `KERNEL_VERSION``84`)在 Wine 中返回内核版本字符串(当前默认 `1.0.0-alpha`)。 - `KERNEL_VERSION``84`)在 Wine 中返回内核版本字符串(当前默认 `1.0.0-alpha`)。
- `DISK_*``85..92`)在 Wine 中已实现: - `DISK_*``85..94`)在 Wine 中已实现:
- 提供虚拟磁盘容量信息与 FAT32 格式化状态查询。 - 提供虚拟磁盘容量信息与 FAT32 格式化状态查询。
- `DISK_FORMAT_FAT32` 会初始化/重置 Wine rootfs 下的虚拟磁盘目录。 - `DISK_FORMAT_FAT32` 会初始化/重置 Wine rootfs 下的虚拟磁盘目录。
- `DISK_MOUNT`/`DISK_MOUNT_PATH` 支持挂载点管理,并与 `FS_MKDIR/WRITE/APPEND/REMOVE` 的路径规则联动。 - `DISK_MOUNT`/`DISK_MOUNT_PATH` 支持挂载点管理,并与 `FS_MKDIR/WRITE/APPEND/REMOVE` 的路径规则联动。
- `DISK_READ_SECTOR`/`DISK_WRITE_SECTOR``93..94`)在 Wine 中已实现为 512B 原始扇区读写host 文件后端)。
- Wine 在运行时崩溃场景下会生成与内核一致格式的“信号编码退出状态”,可通过 `WAITPID` 读取。 - Wine 在运行时崩溃场景下会生成与内核一致格式的“信号编码退出状态”,可通过 `WAITPID` 读取。
- Wine 当前音频 syscall 为占位实现:`AUDIO_AVAILABLE=0``AUDIO_PLAY_TONE=0``AUDIO_STOP=1` - Wine 当前音频 syscall 为占位实现:`AUDIO_AVAILABLE=0``AUDIO_PLAY_TONE=0``AUDIO_STOP=1`
- Wine 版本号策略固定为 `85.0.0-wine`(历史兼容号;不会随 syscall 扩展继续增长)。 - Wine 版本号策略固定为 `85.0.0-wine`(历史兼容号;不会随 syscall 扩展继续增长)。

View File

@@ -38,7 +38,7 @@ python wine/cleonos_wine.py build/x86_64/ramdisk_root/shell/shell.elf --rootfs b
## 支持 ## 支持
- ELF64 (x86_64) PT_LOAD 段装载 - 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 输出与键盘输入队列 - TTY 输出与键盘输入队列
- rootfs 文件/目录访问(`FS_*` - rootfs 文件/目录访问(`FS_*`
- `/temp` 与已挂载磁盘路径写入限制(`FS_MKDIR/WRITE/APPEND/REMOVE` - `/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 符号解析) - 动态库兼容加载(`DL_OPEN/DL_CLOSE/DL_SYM`,基于 ELF 符号解析)
- framebuffer 兼容(`FB_INFO/FB_BLIT/FB_CLEAR`,支持内存缓冲与窗口显示) - framebuffer 兼容(`FB_INFO/FB_BLIT/FB_CLEAR`,支持内存缓冲与窗口显示)
- 内核版本查询(`KERNEL_VERSION` - 内核版本查询(`KERNEL_VERSION`
- 磁盘接口兼容(`DISK_PRESENT/SIZE_BYTES/SECTOR_COUNT/FORMATTED/FORMAT_FAT32/MOUNT/MOUNTED/MOUNT_PATH` - 磁盘接口兼容(`DISK_PRESENT/SIZE_BYTES/SECTOR_COUNT/FORMATTED/FORMAT_FAT32/MOUNT/MOUNTED/MOUNT_PATH/READ_SECTOR/WRITE_SECTOR`
- Wine 虚拟磁盘目录默认位于 `<rootfs>/__clks_disk0__`(格式化标记文件 `.fat32` - Wine 虚拟磁盘目录默认位于 `<rootfs>/__clks_disk0__`(格式化标记文件 `.fat32`,原始扇区后端文件 `.rawdisk.img`
- 异常退出状态编码与故障元信息(`PROC_LAST_SIGNAL/PROC_FAULT_*` - 异常退出状态编码与故障元信息(`PROC_LAST_SIGNAL/PROC_FAULT_*`
## 版本策略 ## 版本策略
- CLeonOS-Wine 版本号固定为:`85.0.0-wine` - CLeonOS-Wine 版本号固定为:`85.0.0-wine`
- 该值按项目策略固定,不再随新增 syscall 变更(即使当前实现范围已扩展到 `0..92` - 该值按项目策略固定,不再随新增 syscall 变更(即使当前实现范围已扩展到 `0..94`
## 参数 ## 参数

View File

@@ -107,6 +107,8 @@ SYS_DISK_FORMAT_FAT32 = 89
SYS_DISK_MOUNT = 90 SYS_DISK_MOUNT = 90
SYS_DISK_MOUNTED = 91 SYS_DISK_MOUNTED = 91
SYS_DISK_MOUNT_PATH = 92 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 states (from cleonos/c/include/cleonos_syscall.h)
PROC_STATE_UNUSED = 0 PROC_STATE_UNUSED = 0

View File

@@ -69,8 +69,10 @@ from .constants import (
SYS_DISK_MOUNTED, SYS_DISK_MOUNTED,
SYS_DISK_MOUNT_PATH, SYS_DISK_MOUNT_PATH,
SYS_DISK_PRESENT, SYS_DISK_PRESENT,
SYS_DISK_READ_SECTOR,
SYS_DISK_SECTOR_COUNT, SYS_DISK_SECTOR_COUNT,
SYS_DISK_SIZE_BYTES, SYS_DISK_SIZE_BYTES,
SYS_DISK_WRITE_SECTOR,
SYS_GETPID, SYS_GETPID,
SYS_KERNEL_VERSION, SYS_KERNEL_VERSION,
SYS_KBD_BUFFERED, SYS_KBD_BUFFERED,
@@ -286,10 +288,12 @@ class CLeonOSWineNative:
self._disk_mount_path = "/temp/disk" self._disk_mount_path = "/temp/disk"
self._disk_root = (self.rootfs / "__clks_disk0__").resolve() self._disk_root = (self.rootfs / "__clks_disk0__").resolve()
self._disk_marker = self._disk_root / ".fat32" self._disk_marker = self._disk_root / ".fat32"
self._disk_image_file = self._disk_root / ".rawdisk.img"
self._disk_formatted = False self._disk_formatted = False
self._disk_mounted = False self._disk_mounted = False
try: try:
self._disk_root.mkdir(parents=True, exist_ok=True) self._disk_root.mkdir(parents=True, exist_ok=True)
self._disk_prepare_raw_image()
if self._disk_marker.exists(): if self._disk_marker.exists():
self._disk_formatted = True self._disk_formatted = True
self._disk_mounted = True self._disk_mounted = True
@@ -762,6 +766,16 @@ class CLeonOSWineNative:
return 1 if self._disk_mounted else 0 return 1 if self._disk_mounted else 0
if sid == SYS_DISK_MOUNT_PATH: if sid == SYS_DISK_MOUNT_PATH:
return self._disk_mount_path_query(uc, arg0, arg1) 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() return u64_neg1()
@@ -1080,20 +1094,78 @@ class CLeonOSWineNative:
return None return None
return host 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: def _disk_format_fat32(self, label: str) -> int:
_ = label _ = label
if not self._disk_present: if not self._disk_present:
return 0 return 0
try: try:
self._disk_prepare_raw_image()
self._disk_root.mkdir(parents=True, exist_ok=True) self._disk_root.mkdir(parents=True, exist_ok=True)
for child in list(self._disk_root.iterdir()): 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 continue
if child.is_dir(): if child.is_dir():
shutil.rmtree(child) shutil.rmtree(child)
else: else:
child.unlink() 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_marker.write_text("FAT32\n", encoding="utf-8")
self._disk_formatted = True self._disk_formatted = True
return 1 return 1