This commit is contained in:
xyou
2026-07-31 13:53:49 +08:00
parent 5ea84fac97
commit f52157b7af
241 changed files with 26843 additions and 8155 deletions
+262 -10
View File
@@ -23,9 +23,11 @@
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <dirent.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
/* Shared IPC protocol — must match UTSM side */
#include "../../../utsm-ipc/ipc_proto.h"
@@ -35,6 +37,8 @@
#define UTSM_IOCTL_SEND_MSG _IOWR('U', 2, struct utsm_ioctl_msg)
#define UTSM_IOCTL_PARK _IO('U', 3)
#define UTSM_IOCTL_GET_READY _IOR('U', 4, int)
#define UTSM_IOCTL_WRITE_POOL _IOW('U', 5, struct utsm_ioctl_pool)
#define UTSM_IOCTL_READ_POOL _IOWR('U', 6, struct utsm_ioctl_pool)
struct utsm_ioctl_msg {
unsigned int type;
@@ -44,6 +48,13 @@ struct utsm_ioctl_msg {
unsigned char data[UTSM_IPC_MSG_DATA_SIZE];
};
/* Pool transfer descriptor — must match utsm_hcall.c driver */
struct utsm_ioctl_pool {
unsigned int offset;
unsigned int len;
unsigned long long user_buf;
};
static int g_utsm_fd = -1;
/* Send a typed message to UTSM via the linux_to_utsm ring */
@@ -77,6 +88,14 @@ static int park(void)
return ioctl(g_utsm_fd, UTSM_IOCTL_PARK);
}
/* Best-effort write to the child's stderr (exec failure path);
* the result is intentionally ignored. */
static void err_write(const char *s, unsigned int len)
{
ssize_t n = write(STDERR_FILENO, s, len);
(void)n;
}
/* ===== Exec request handler =====
*
* Fork a child to exec the requested program. Parent reads stdout/stderr
@@ -138,13 +157,13 @@ static void handle_exec_request(const struct ipc_exec_request *req)
execv(req->path, argv);
/* execv only returns on failure */
const char *err1 = "utsm_exec_daemon: exec failed: ";
write(STDERR_FILENO, err1, strlen(err1));
write(STDERR_FILENO, req->path, strnlen(req->path, UTSM_EXEC_PATH_MAX));
err_write(err1, (unsigned int)strlen(err1));
err_write(req->path, (unsigned int)strnlen(req->path, UTSM_EXEC_PATH_MAX));
const char *err2 = ": ";
write(STDERR_FILENO, err2, strlen(err2));
err_write(err2, (unsigned int)strlen(err2));
const char *emsg = strerror(errno);
write(STDERR_FILENO, emsg, strlen(emsg));
write(STDERR_FILENO, "\n", 1);
err_write(emsg, (unsigned int)strlen(emsg));
err_write("\n", 1);
_exit(127);
}
@@ -174,6 +193,232 @@ static void handle_exec_request(const struct ipc_exec_request *req)
send_msg(UTSM_MSG_EXEC_EXIT, &ex, sizeof(ex));
}
/* ===== File transfer handlers (Phase 3) =====
*
* UTSM sends FILE_LIST_REQUEST / FILE_READ_REQUEST with an ipc_file_request
* payload. Bulk result data is staged into the shared payload_pool via
* UTSM_IOCTL_WRITE_POOL, then a FILE_RESPONSE message (ipc_file_response)
* carries status + data_len + total_size back to UTSM.
*
* Directory listing format: one entry per line, directories suffixed '/'. */
/* Staging buffer for file reads / listings (capped by pool capacity ~1MB) */
#define FT_STAGE_SIZE (768 * 1024)
static unsigned char g_ft_stage[FT_STAGE_SIZE];
static int pool_write(unsigned int offset, const void *buf, unsigned int len)
{
struct utsm_ioctl_pool p;
p.offset = offset;
p.len = len;
p.user_buf = (unsigned long long)(uintptr_t)buf;
return ioctl(g_utsm_fd, UTSM_IOCTL_WRITE_POOL, &p);
}
static int pool_read(unsigned int offset, void *buf, unsigned int len)
{
struct utsm_ioctl_pool p;
p.offset = offset;
p.len = len;
p.user_buf = (unsigned long long)(uintptr_t)buf;
return ioctl(g_utsm_fd, UTSM_IOCTL_READ_POOL, &p);
}
static unsigned int errno_to_file_status(int e)
{
switch (e) {
case ENOENT: return UTSM_FILE_ERR_NOENT;
case ENOTDIR: return UTSM_FILE_ERR_NOTDIR;
case EISDIR: return UTSM_FILE_ERR_ISDIR;
case EACCES: return UTSM_FILE_ERR_PERM;
case ENOSPC:
case EFBIG: return UTSM_FILE_ERR_NOSPC;
default: return UTSM_FILE_ERR_IO;
}
}
static void send_file_response(unsigned int status, unsigned int data_len,
unsigned long long total_size)
{
struct ipc_file_response resp;
memset(&resp, 0, sizeof(resp));
resp.status = status;
resp.data_len = data_len;
resp.total_size = total_size;
send_msg(UTSM_MSG_FILE_RESPONSE, &resp, sizeof(resp));
}
/* FILE_LIST_REQUEST: list directory entries as "name\n" ("name/\n" for dirs) */
static void handle_file_list(const struct ipc_file_request *req)
{
char path[UTSM_FILE_PATH_MAX + 1];
memcpy(path, req->path, UTSM_FILE_PATH_MAX);
path[UTSM_FILE_PATH_MAX] = '\0';
if (req->pool_capacity == 0 || req->pool_offset >= UTSM_IPC_PAYLOAD_SIZE) {
send_file_response(UTSM_FILE_ERR_INVAL, 0, 0);
return;
}
DIR *d = opendir(path);
if (!d) {
send_file_response(errno_to_file_status(errno), 0, 0);
return;
}
unsigned int cap = req->pool_capacity;
if (cap > FT_STAGE_SIZE) cap = FT_STAGE_SIZE;
unsigned int used = 0;
struct dirent *de;
while ((de = readdir(d)) != NULL) {
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
continue;
/* Determine directory flag: d_type if available, else stat */
int is_dir = 0;
if (de->d_type == DT_DIR) {
is_dir = 1;
} else if (de->d_type == DT_UNKNOWN || de->d_type == DT_LNK) {
struct stat st;
char full[512];
snprintf(full, sizeof(full), "%s/%s", path, de->d_name);
if (stat(full, &st) == 0 && S_ISDIR(st.st_mode))
is_dir = 1;
}
int nl = snprintf((char *)g_ft_stage + used, cap - used,
"%s%s\n", de->d_name, is_dir ? "/" : "");
if (nl < 0) break;
if ((unsigned int)nl >= cap - used) {
/* 缓冲已满:本条未完整写入,截断结束 */
used = cap;
break;
}
used += (unsigned int)nl;
}
closedir(d);
if (used > 0 && pool_write(req->pool_offset, g_ft_stage, used) != 0) {
send_file_response(UTSM_FILE_ERR_IO, 0, 0);
return;
}
send_file_response(UTSM_FILE_OK, used, 0);
}
/* FILE_READ_REQUEST: read up to pool_capacity bytes at file_offset */
static void handle_file_read(const struct ipc_file_request *req)
{
char path[UTSM_FILE_PATH_MAX + 1];
memcpy(path, req->path, UTSM_FILE_PATH_MAX);
path[UTSM_FILE_PATH_MAX] = '\0';
if (req->pool_capacity == 0 || req->pool_offset >= UTSM_IPC_PAYLOAD_SIZE) {
send_file_response(UTSM_FILE_ERR_INVAL, 0, 0);
return;
}
struct stat st;
if (stat(path, &st) != 0) {
send_file_response(errno_to_file_status(errno), 0, 0);
return;
}
if (S_ISDIR(st.st_mode)) {
send_file_response(UTSM_FILE_ERR_ISDIR, 0, (unsigned long long)st.st_size);
return;
}
int fd = open(path, O_RDONLY);
if (fd < 0) {
send_file_response(errno_to_file_status(errno), 0,
(unsigned long long)st.st_size);
return;
}
unsigned int cap = req->pool_capacity;
if (cap > FT_STAGE_SIZE) cap = FT_STAGE_SIZE;
off_t off = (off_t)req->file_offset;
ssize_t n = pread(fd, g_ft_stage, cap, off);
int saved = errno;
close(fd);
if (n < 0) {
send_file_response(errno_to_file_status(saved), 0,
(unsigned long long)st.st_size);
return;
}
if (n > 0 && pool_write(req->pool_offset, g_ft_stage, (unsigned int)n) != 0) {
send_file_response(UTSM_FILE_ERR_IO, 0, (unsigned long long)st.st_size);
return;
}
send_file_response(UTSM_FILE_OK, (unsigned int)n,
(unsigned long long)st.st_size);
}
/* FILE_WRITE_REQUEST: write payload_pool data to file at file_offset.
* UTSM pre-stages req->pool_capacity bytes at req->pool_offset in the pool.
* Convention: file_offset == 0 truncates the file first (push = replace);
* the file is created with mode 0644 if missing. Data is moved in
* FT_STAGE_SIZE pieces so a full pool (~1MB) can be consumed per request. */
static void handle_file_write(const struct ipc_file_request *req)
{
char path[UTSM_FILE_PATH_MAX + 1];
memcpy(path, req->path, UTSM_FILE_PATH_MAX);
path[UTSM_FILE_PATH_MAX] = '\0';
if (req->pool_offset >= UTSM_IPC_PAYLOAD_SIZE ||
req->pool_capacity > UTSM_IPC_PAYLOAD_SIZE - req->pool_offset) {
send_file_response(UTSM_FILE_ERR_INVAL, 0, 0);
return;
}
struct stat st;
if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
send_file_response(UTSM_FILE_ERR_ISDIR, 0, 0);
return;
}
int flags = O_WRONLY | O_CREAT;
if (req->file_offset == 0)
flags |= O_TRUNC;
int fd = open(path, flags, 0644);
if (fd < 0) {
send_file_response(errno_to_file_status(errno), 0, 0);
return;
}
unsigned int remaining = req->pool_capacity;
unsigned int done = 0;
while (remaining > 0) {
unsigned int chunk = remaining;
if (chunk > FT_STAGE_SIZE) chunk = FT_STAGE_SIZE;
if (pool_read(req->pool_offset + done, g_ft_stage, chunk) != 0) {
close(fd);
send_file_response(UTSM_FILE_ERR_IO, 0, 0);
return;
}
ssize_t n = pwrite(fd, g_ft_stage, chunk,
(off_t)(req->file_offset + done));
if (n < 0) {
int saved = errno;
close(fd);
send_file_response(errno_to_file_status(saved), done, 0);
return;
}
done += (unsigned int)n;
remaining -= (unsigned int)n;
if ((unsigned int)n < chunk)
break; /* short write (e.g. ENOSPC on next call would fail) */
}
unsigned long long total = 0;
if (fstat(fd, &st) == 0)
total = (unsigned long long)st.st_size;
close(fd);
send_file_response(UTSM_FILE_OK, done, total);
}
/* ===== Main loop ===== */
int main(void)
@@ -197,19 +442,26 @@ int main(void)
/* Drain any pending messages (there may be multiple if UTSM
* queued several before vmresume). */
int got_request = 0;
while (recv_msg(&umsg) == 0) {
if (umsg.type == UTSM_MSG_EXEC_REQUEST &&
umsg.data_len >= sizeof(struct ipc_exec_request)) {
handle_exec_request((const struct ipc_exec_request *)umsg.data);
got_request = 1;
} else if (umsg.type == UTSM_MSG_FILE_LIST_REQUEST &&
umsg.data_len >= sizeof(struct ipc_file_request)) {
handle_file_list((const struct ipc_file_request *)umsg.data);
} else if (umsg.type == UTSM_MSG_FILE_READ_REQUEST &&
umsg.data_len >= sizeof(struct ipc_file_request)) {
handle_file_read((const struct ipc_file_request *)umsg.data);
} else if (umsg.type == UTSM_MSG_FILE_WRITE_REQUEST &&
umsg.data_len >= sizeof(struct ipc_file_request)) {
handle_file_write((const struct ipc_file_request *)umsg.data);
}
/* Ignore other message types (HELLO, PING, etc.) */
}
/* If we processed a request, the response is already sent.
* Park to let UTSM read the response and return to shell.
* If no request (spurious wakeup), just park again. */
/* Responses (if any) are already queued. Park to let UTSM read
* them and return to the shell; a spurious wakeup with no
* request simply parks again. */
park();
}
+69 -28
View File
@@ -2,17 +2,21 @@
#
# init — Linux guest init script for UTSM dual-kernel Linux compat layer.
#
# Boot sequence:
# 1. Mount essential filesystems
# 2. Load UTSM driver (built-in, /dev/utsm auto-created)
# 3. Start utsm_exec_daemon (background)
# 4. Daemon sends EXEC_READY, then HLT (park) → linux_launch() returns to UTSM
# 5. UTSM proceeds to load DSK; Linux guest stays parked until shell
# invokes `linux <prog>` which triggers vmresume.
# Boot sequence (phase 2 — Arch rootfs via virtio-blk /dev/vdb):
# 1. Mount essential filesystems (proc/sys/dev)
# 2. Wait for /dev/vdb (Arch rootfs virtio-blk device) to appear
# 3. Mount /dev/vdb as ext4 at /mnt/root
# 4. Copy /dev nodes + daemon (already baked into rootfs) into /mnt/root
# 5. switch_root /mnt/root /sbin/init
# 6. Arch /sbin/init starts utsm_exec_daemon → EXEC_READY → HLT (park)
# → linux_launch() returns to UTSM
#
# The daemon runs forever, parking between requests. This init script
# should NOT exit (would panic the kernel); it sleeps indefinitely after
# starting the daemon so the kernel has a runnable init task.
# Fallback (phase 1 — no rootfs, daemon in initramfs):
# - If /dev/vdb never appears, start daemon directly from initramfs.
# - If no daemon either, drop to interactive debug shell on ttyS0.
#
# The daemon runs forever, parking between requests. After switch_root,
# Arch /sbin/init owns PID 1 and must not exit.
PATH=/bin:/sbin:/usr/bin:/usr/sbin
export PATH
@@ -30,27 +34,64 @@ if [ ! -e /dev/utsm ]; then
echo "[utsm-linux] WARNING: /dev/utsm not found — UTSM driver not loaded"
fi
# Start the exec daemon in the background.
# The daemon sends EXEC_READY then HLTs (parks the guest).
# UTSM's linux_launch() returns after this first HLT.
# ===== Phase 2: switch_root to Arch rootfs on /dev/vdb =====
# The rootfs is exposed as a memory-backed virtio-blk device (GPA 0xF4002000,
# IRQ7). Wait up to ~5s for the kernel to discover it via devtmpfs.
echo "[utsm-linux] waiting for /dev/vdb (Arch rootfs)..."
VDB_OK=0
i=0
while [ $i -lt 50 ]; do
if [ -b /dev/vdb ]; then
VDB_OK=1
break
fi
# 100ms sleep (busybox sleep supports fractional seconds)
sleep 0.1
i=$((i + 1))
done
if [ $VDB_OK -eq 1 ]; then
echo "[utsm-linux] /dev/vdb found, attempting ext4 mount..."
mkdir -p /mnt/root
# Try ext4 (preferred); fall back to ext2 if ext4 module absent.
if mount -t ext4 -o ro /dev/vdb /mnt/root 2>/dev/null || \
mount -t ext2 -o ro /dev/vdb /mnt/root 2>/dev/null; then
echo "[utsm-linux] rootfs mounted, verifying /sbin/init..."
if [ -x /mnt/root/sbin/init ]; then
echo "[utsm-linux] switch_root → Arch /sbin/init"
# Move dev/proc/sys into new root so the Arch init can re-mount them.
mount --move /dev /mnt/root/dev 2>/dev/null
mount --move /proc /mnt/root/proc 2>/dev/null
mount --move /sys /mnt/root/sys 2>/dev/null
exec switch_root /mnt/root /sbin/init
# switch_root does not return
else
echo "[utsm-linux] WARNING: /mnt/root/sbin/init missing, not switching"
umount /mnt/root 2>/dev/null
fi
else
echo "[utsm-linux] WARNING: ext4/ext2 mount of /dev/vdb failed"
echo "[utsm-linux] available block devices:"
ls -l /dev/vd* /dev/sd* 2>/dev/null
fi
else
echo "[utsm-linux] /dev/vdb not found after 5s — phase-1 fallback"
fi
# ===== Fallback: daemon directly from initramfs (phase 1) =====
if [ -x /bin/utsm_exec_daemon ]; then
echo "[utsm-linux] starting utsm_exec_daemon..."
echo "[utsm-linux] starting utsm_exec_daemon from initramfs..."
/bin/utsm_exec_daemon &
DAEMON_PID=$!
echo "[utsm-linux] daemon PID=$DAEMON_PID"
else
# 无 daemon(第一阶段 bring-up 冒烟测试构建):
# 直接在 ttyS0 上起交互 shell,验证 console 输入输出。
# 注意:此模式下 guest 不会 park,UTSM linux_launch() 不返回,
# 仅用于验证"Linux guest 进入 shell 提示符、可执行 uname -a"。
echo "[utsm-linux] WARNING: no daemon, starting interactive debug shell on ttyS0"
exec /bin/setsid /bin/cttyhack /bin/sh
# init must not exit — sleep forever to keep the kernel happy.
echo "[utsm-linux] init: daemon running, entering idle hold"
while true; do
sleep 3600
done
fi
# init must not exit — sleep forever to keep the kernel happy.
# The daemon handles all UTSM exec requests; init just keeps the
# root task alive so the kernel doesn't panic.
echo "[utsm-linux] init: daemon running, entering idle hold"
while true; do
sleep 3600
done
# ===== Last resort: interactive debug shell on ttyS0 =====
# No daemon and no rootfs — bring-up smoke test only.
echo "[utsm-linux] WARNING: no daemon, no rootfs — starting interactive debug shell on ttyS0"
exec /bin/setsid /bin/cttyhack /bin/sh