ohhhhhhhhhhh (exclude linux-extra-rootfs.img build artifact)
This commit is contained in:
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Static mount(2) helper. Guest vdb util-linux/mount is unreadable
|
||||
* (runtime ext4 checksum) and persist ld.so hits SHSTK (fault at 0).
|
||||
* Built the same way as utsm_exec_daemon, which already runs in-guest.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sched.h>
|
||||
#include <string.h>
|
||||
#include <sys/mount.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void wstr(const char *s)
|
||||
{
|
||||
(void)write(2, s, strlen(s));
|
||||
}
|
||||
|
||||
static int do_mount(const char *src, const char *tgt,
|
||||
const char *fstype, unsigned long flags, const void *data)
|
||||
{
|
||||
if (mount(src, tgt, fstype, flags, data) == 0)
|
||||
return 0;
|
||||
wstr("utsm_bindmount errno=");
|
||||
if (errno >= 10) {
|
||||
char d[2];
|
||||
d[0] = (char)('0' + (errno / 10));
|
||||
d[1] = (char)('0' + (errno % 10));
|
||||
(void)write(2, d, 2);
|
||||
} else {
|
||||
char d = (char)('0' + errno);
|
||||
(void)write(2, &d, 1);
|
||||
}
|
||||
wstr("\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 3) {
|
||||
wstr("usage: utsm_bindmount bind|rbind|slave|fstype|msleep|yield|setinterp|umount|nsexec ...\n");
|
||||
return 2;
|
||||
}
|
||||
if (strcmp(argv[1], "bind") == 0 && argc >= 4)
|
||||
return do_mount(argv[2], argv[3], NULL, MS_BIND, NULL);
|
||||
if (strcmp(argv[1], "rbind") == 0 && argc >= 4)
|
||||
return do_mount(argv[2], argv[3], NULL, MS_BIND | MS_REC, NULL);
|
||||
if (strcmp(argv[1], "slave") == 0 && argc >= 3)
|
||||
return do_mount(NULL, argv[2], NULL, MS_SLAVE, NULL);
|
||||
if (strcmp(argv[1], "fstype") == 0 && argc >= 5)
|
||||
return do_mount(argv[3], argv[4], argv[2], 0,
|
||||
(argc >= 6) ? argv[5] : NULL);
|
||||
if (strcmp(argv[1], "yield") == 0) {
|
||||
sched_yield();
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(argv[1], "tail") == 0 && argc >= 3) {
|
||||
int fd, maxlines = 40, lines = 0, i;
|
||||
off_t sz, start;
|
||||
static char buf[16384];
|
||||
ssize_t n;
|
||||
if (argc >= 4) {
|
||||
const char *p = argv[3];
|
||||
maxlines = 0;
|
||||
while (*p >= '0' && *p <= '9') {
|
||||
maxlines = maxlines * 10 + (*p - '0');
|
||||
p++;
|
||||
}
|
||||
if (maxlines < 1)
|
||||
maxlines = 40;
|
||||
}
|
||||
fd = open(argv[2], O_RDONLY);
|
||||
if (fd < 0)
|
||||
return 1;
|
||||
sz = lseek(fd, 0, SEEK_END);
|
||||
if (sz < 0)
|
||||
sz = 0;
|
||||
start = (sz > (off_t)sizeof(buf)) ? sz - (off_t)sizeof(buf) : 0;
|
||||
if (lseek(fd, start, SEEK_SET) < 0)
|
||||
start = 0;
|
||||
n = read(fd, buf, sizeof(buf));
|
||||
close(fd);
|
||||
if (n <= 0)
|
||||
return 1;
|
||||
for (i = (int)n - 1; i >= 0; i--) {
|
||||
if (buf[i] == '\n')
|
||||
lines++;
|
||||
if (lines > maxlines) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < 0)
|
||||
i = 0;
|
||||
(void)write(1, buf + i, (size_t)(n - i));
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(argv[1], "msleep") == 0 && argc >= 3) {
|
||||
unsigned long ms = 0;
|
||||
const char *p = argv[2];
|
||||
struct timespec ts;
|
||||
while (*p >= '0' && *p <= '9') {
|
||||
ms = ms * 10ul + (unsigned long)(*p - '0');
|
||||
p++;
|
||||
}
|
||||
ts.tv_sec = (time_t)(ms / 1000ul);
|
||||
ts.tv_nsec = (long)(ms % 1000ul) * 1000000L;
|
||||
(void)nanosleep(&ts, 0);
|
||||
return 0;
|
||||
}
|
||||
if (strcmp(argv[1], "chroot") == 0 && argc >= 4) {
|
||||
int kfd;
|
||||
if (chroot(argv[2]) != 0) {
|
||||
wstr("utsm_bindmount: chroot failed\n");
|
||||
return 1;
|
||||
}
|
||||
if (chdir("/") != 0) {
|
||||
wstr("utsm_bindmount: chdir / failed\n");
|
||||
return 1;
|
||||
}
|
||||
kfd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
|
||||
if (kfd >= 0) {
|
||||
const char *m = "[utsm_bindmount] inside chroot, exec next\n";
|
||||
(void)write(kfd, m, strlen(m));
|
||||
close(kfd);
|
||||
}
|
||||
execv(argv[3], argv + 3);
|
||||
wstr("utsm_bindmount: exec after chroot failed path=");
|
||||
wstr(argv[3]);
|
||||
wstr(" errno=");
|
||||
if (errno >= 10) {
|
||||
char d[2];
|
||||
d[0] = (char)('0' + (errno / 10));
|
||||
d[1] = (char)('0' + (errno % 10));
|
||||
(void)write(2, d, 2);
|
||||
} else {
|
||||
char d = (char)('0' + errno);
|
||||
(void)write(2, &d, 1);
|
||||
}
|
||||
wstr("\n");
|
||||
return 127;
|
||||
}
|
||||
if (strcmp(argv[1], "nsexec") == 0 && argc >= 3) {
|
||||
int kfd;
|
||||
int u1, u2, u3, pr;
|
||||
kfd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
|
||||
if (unshare(CLONE_NEWNS) != 0) {
|
||||
wstr("nsexec: unshare failed\n");
|
||||
if (kfd >= 0) {
|
||||
const char *m = "[nsexec] unshare failed\n";
|
||||
(void)write(kfd, m, strlen(m));
|
||||
close(kfd);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
pr = mount((const char *)0, "/", (const char *)0,
|
||||
MS_REC | MS_PRIVATE, (const void *)0);
|
||||
if (pr != 0) {
|
||||
pr = mount((const char *)0, "/", (const char *)0,
|
||||
MS_REC | MS_SLAVE, (const void *)0);
|
||||
}
|
||||
/* BUG-XORG-SEGV 根修(2026-09-20):此处 umount 会把 chroot 里的
|
||||
* vdb ld.so bind 拆掉,Xorg 落回 persist ld.so —— 其 0x15ded 处
|
||||
* NULL+8 segfault(Xorg/dbus 均复现)。12:18 好运行中这些 bind
|
||||
* 全部保持激活(shell umount 均 EBUSY),Xorg 用 vdb ld.so 存活。
|
||||
* 恢复该行为:nsexec 不再卸载 ld.so bind。 */
|
||||
u1 = -1;
|
||||
u2 = -1;
|
||||
u3 = -1;
|
||||
if (kfd >= 0) {
|
||||
const char *m = "[nsexec] umount done, exec Xorg\n";
|
||||
(void)write(kfd, m, strlen(m));
|
||||
if (u1 == 0)
|
||||
m = "[nsexec] umount lib64 ld.so ok\n";
|
||||
else
|
||||
m = "[nsexec] umount lib64 ld.so FAIL\n";
|
||||
(void)write(kfd, m, strlen(m));
|
||||
if (u2 == 0)
|
||||
m = "[nsexec] umount usr/lib ld.so ok\n";
|
||||
else
|
||||
m = "[nsexec] umount usr/lib ld.so FAIL\n";
|
||||
(void)write(kfd, m, strlen(m));
|
||||
(void)pr;
|
||||
(void)u3;
|
||||
close(kfd);
|
||||
}
|
||||
execv(argv[2], argv + 2);
|
||||
wstr("nsexec: exec failed\n");
|
||||
return 127;
|
||||
}
|
||||
if (strcmp(argv[1], "umount") == 0 && argc >= 3) {
|
||||
if (umount(argv[2]) == 0)
|
||||
return 0;
|
||||
wstr("umount errno=");
|
||||
if (errno >= 10) {
|
||||
char d[2];
|
||||
d[0] = (char)('0' + (errno / 10));
|
||||
d[1] = (char)('0' + (errno % 10));
|
||||
(void)write(2, d, 2);
|
||||
} else {
|
||||
char d = (char)('0' + errno);
|
||||
(void)write(2, &d, 1);
|
||||
}
|
||||
wstr("\n");
|
||||
return 1;
|
||||
}
|
||||
if (strcmp(argv[1], "setinterp") == 0 && argc >= 4) {
|
||||
int fd;
|
||||
unsigned char eh[64];
|
||||
unsigned long phoff = 0, nlen;
|
||||
unsigned short phentsize = 0, phnum = 0, i;
|
||||
const char *ni = argv[3];
|
||||
nlen = strlen(ni) + 1;
|
||||
fd = open(argv[2], O_RDWR);
|
||||
if (fd < 0) {
|
||||
wstr("setinterp: open failed\n");
|
||||
return 1;
|
||||
}
|
||||
if (read(fd, eh, 64) != 64 || eh[0] != 0x7f || eh[4] != 2) {
|
||||
wstr("setinterp: not elf64\n");
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
memcpy(&phoff, eh + 32, 8);
|
||||
memcpy(&phentsize, eh + 54, 2);
|
||||
memcpy(&phnum, eh + 56, 2);
|
||||
for (i = 0; i < phnum; i++) {
|
||||
unsigned char ph[56];
|
||||
unsigned int type = 0;
|
||||
unsigned long off = 0, filesz = 0;
|
||||
if (lseek(fd, (off_t)(phoff + (unsigned long)i * phentsize),
|
||||
SEEK_SET) < 0)
|
||||
break;
|
||||
if (read(fd, ph, 56) != 56)
|
||||
break;
|
||||
memcpy(&type, ph, 4);
|
||||
if (type != 3)
|
||||
continue;
|
||||
memcpy(&off, ph + 8, 8);
|
||||
memcpy(&filesz, ph + 32, 8);
|
||||
if (nlen > filesz) {
|
||||
wstr("setinterp: path too long\n");
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
if (lseek(fd, (off_t)off, SEEK_SET) < 0) {
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
if (write(fd, ni, nlen) != (ssize_t)nlen) {
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
if (filesz > nlen) {
|
||||
unsigned char z[32];
|
||||
unsigned long pad = filesz - nlen;
|
||||
memset(z, 0, sizeof z);
|
||||
while (pad) {
|
||||
unsigned long c = pad > sizeof z ? sizeof z : pad;
|
||||
if (write(fd, z, c) != (ssize_t)c) {
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
pad -= c;
|
||||
}
|
||||
}
|
||||
close(fd);
|
||||
wstr("setinterp: ok\n");
|
||||
return 0;
|
||||
}
|
||||
wstr("setinterp: no PT_INTERP\n");
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
wstr("utsm_bindmount: bad args\n");
|
||||
return 2;
|
||||
}
|
||||
@@ -29,6 +29,8 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
#include <sched.h>
|
||||
#include <poll.h>
|
||||
|
||||
/* Shared IPC protocol — must match UTSM side */
|
||||
#include "../../../utsm-ipc/ipc_proto.h"
|
||||
@@ -57,6 +59,7 @@ struct utsm_ioctl_pool {
|
||||
};
|
||||
|
||||
static int g_utsm_fd = -1;
|
||||
static volatile sig_atomic_t g_async_live;
|
||||
|
||||
/* pool_write 定义在文件传输段(下文),exec 同步路径复用同一通道 */
|
||||
static int pool_write(unsigned int offset, const void *buf, unsigned int len);
|
||||
@@ -129,6 +132,48 @@ static void err_write(const char *s, unsigned int len)
|
||||
(void)n;
|
||||
}
|
||||
|
||||
static void kmsg_write(const char *s)
|
||||
{
|
||||
int fd;
|
||||
if (!s) return;
|
||||
fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
|
||||
if (fd < 0) return;
|
||||
(void)write(fd, s, strlen(s));
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static int path_needs_deshab_libs(const char *path)
|
||||
{
|
||||
if (!path || !path[0])
|
||||
return 0;
|
||||
if (strstr(path, "deshab-kde-session"))
|
||||
return 0;
|
||||
if (strstr(path, "/bin/sh") || strstr(path, "/bin/bash") ||
|
||||
strstr(path, "/usr/bin/bash") || strstr(path, "/usr/bin/env"))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Nested TSC is often marked unstable, so usleep() returns immediately
|
||||
* and async children never run on the only vCPU. A yield loop also
|
||||
* starves D-state I/O: PID 1 stays runnable and idle never runs.
|
||||
* poll(POLLIN, 10ms) on /dev/utsm sleeps interruptibly so Plasma/session
|
||||
* and virtio completions can run. Driver poll does not wait-queue, so
|
||||
* a new EXEC_REQUEST is seen at the next timeout. */
|
||||
static void donate_cpu(void)
|
||||
{
|
||||
struct pollfd pfd;
|
||||
|
||||
memset(&pfd, 0, sizeof(pfd));
|
||||
pfd.fd = g_utsm_fd;
|
||||
pfd.events = POLLIN;
|
||||
if (poll(&pfd, 1, 10) < 0) {
|
||||
unsigned i;
|
||||
for (i = 0; i < 64u; i++)
|
||||
sched_yield();
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== Exec request handler =====
|
||||
*
|
||||
* Fork a child to exec the requested program. Parent reads stdout/stderr
|
||||
@@ -178,6 +223,7 @@ static void handle_exec_request(const struct ipc_exec_request *req)
|
||||
return;
|
||||
}
|
||||
if (pid == 0) {
|
||||
char line[192];
|
||||
/* Child: new session, no controlling tty, stdio → /dev/null */
|
||||
setsid();
|
||||
int nullfd = open("/dev/null", O_RDWR);
|
||||
@@ -187,13 +233,29 @@ static void handle_exec_request(const struct ipc_exec_request *req)
|
||||
dup2(nullfd, STDERR_FILENO);
|
||||
if (nullfd > STDERR_FILENO) close(nullfd);
|
||||
}
|
||||
/* 与同步路径一致的库搜索路径;DISPLAY 指向 virtio-gpu X server */
|
||||
setenv("LD_LIBRARY_PATH", "/usr/lib/deshab", 1);
|
||||
/* DISPLAY 指向 virtio-gpu X server。不要把 LD_LIBRARY_PATH
|
||||
* 套到 bash/session:/usr/lib/deshab 会劫持 glibc。 */
|
||||
if (path_needs_deshab_libs(req->path))
|
||||
setenv("LD_LIBRARY_PATH", "/usr/lib/deshab", 1);
|
||||
setenv("DISPLAY", ":0", 1);
|
||||
snprintf(line, sizeof(line),
|
||||
"[deshab-kde] async exec %s\n", req->path);
|
||||
kmsg_write(line);
|
||||
execv(req->path, argv);
|
||||
snprintf(line, sizeof(line),
|
||||
"[deshab-kde] execv failed errno=%d path=%s\n",
|
||||
errno, req->path);
|
||||
kmsg_write(line);
|
||||
_exit(127); /* execv 仅失败时返回 */
|
||||
}
|
||||
/* Parent: report successful spawn immediately */
|
||||
g_async_live = 1;
|
||||
{
|
||||
char line[128];
|
||||
snprintf(line, sizeof(line),
|
||||
"[deshab-kde] async spawn pid=%d\n", (int)pid);
|
||||
kmsg_write(line);
|
||||
}
|
||||
struct ipc_exec_exit ex = { .exit_code = 0, .flags = 0,
|
||||
.stdout_len = 0, .stdout_total = 0 };
|
||||
send_exit_msg(&ex);
|
||||
@@ -227,8 +289,8 @@ static void handle_exec_request(const struct ipc_exec_request *req)
|
||||
dup2(pipefd[1], STDERR_FILENO);
|
||||
close(pipefd[1]);
|
||||
|
||||
/* 设置 LD_LIBRARY_PATH,让 ld-linux.so 在 /usr/lib/deshab 查找 .so */
|
||||
setenv("LD_LIBRARY_PATH", "/usr/lib/deshab", 1);
|
||||
if (path_needs_deshab_libs(req->path))
|
||||
setenv("LD_LIBRARY_PATH", "/usr/lib/deshab", 1);
|
||||
|
||||
execv(req->path, argv);
|
||||
/* execv only returns on failure */
|
||||
@@ -589,6 +651,16 @@ int main(void)
|
||||
/* Ignore other message types (HELLO, PING, etc.) */
|
||||
}
|
||||
|
||||
/* Single vCPU: PARK/HLT freezes the whole VM, including async
|
||||
* children and in-flight virtio-blk. Yields return immediately
|
||||
* when the child is in D-state, so a yield-then-HLT loop parks
|
||||
* before the virtio IRQ can be injected. Stay runnable; the
|
||||
* host returns from linux_resume() on a preemption timeslice. */
|
||||
if (g_async_live) {
|
||||
donate_cpu();
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Shebang trampoline: run a script with persist bash + persist glibc.
|
||||
* vdb /usr/bin/bash needs libreadline, and virtio often returns a bad
|
||||
* ext4 inode checksum for that file. persist userland is on vdc.
|
||||
*
|
||||
* Built like utsm_exec_daemon: -static -fcf-protection=none.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern char **environ;
|
||||
|
||||
static void kmsg(const char *s)
|
||||
{
|
||||
int fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return;
|
||||
(void)write(fd, s, strlen(s));
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static int exists(const char *p)
|
||||
{
|
||||
return p && access(p, X_OK) == 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
static const char *ld_cands[] = {
|
||||
"/mnt/persist/usr/lib/ld-linux-x86-64.so.2",
|
||||
"/mnt/persist/lib64/ld-linux-x86-64.so.2",
|
||||
"/mnt/persist/lib/ld-linux-x86-64.so.2",
|
||||
0
|
||||
};
|
||||
static const char *bash_cands[] = {
|
||||
"/mnt/persist/usr/bin/bash",
|
||||
"/mnt/persist/bin/bash",
|
||||
0
|
||||
};
|
||||
const char *libpath =
|
||||
"/mnt/persist/usr/lib:/mnt/persist/lib64:/mnt/persist/lib";
|
||||
const char *path =
|
||||
"/mnt/persist/usr/bin:/mnt/persist/bin:/usr/local/bin:/usr/bin:/bin";
|
||||
const char *ld = 0;
|
||||
const char *bash = 0;
|
||||
char *nargv[64];
|
||||
int n = 0;
|
||||
int i;
|
||||
char line[192];
|
||||
|
||||
kmsg("[utsm_run] start\n");
|
||||
if (argc < 2) {
|
||||
kmsg("[utsm_run] usage: utsm_run script [args]\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* Already inside persist chroot: /mnt/persist is gone, bash is local. */
|
||||
if (access("/etc/deshab-kde-root", F_OK) == 0 ||
|
||||
access("/mnt/persist/usr/bin/bash", X_OK) != 0) {
|
||||
kmsg("[utsm_run] inside persist, exec /usr/bin/bash\n");
|
||||
nargv[n++] = "/usr/bin/bash";
|
||||
for (i = 1; i < argc && n < 62; i++)
|
||||
nargv[n++] = argv[i];
|
||||
nargv[n] = 0;
|
||||
execv("/usr/bin/bash", nargv);
|
||||
kmsg("[utsm_run] inside bash execv failed\n");
|
||||
return 127;
|
||||
}
|
||||
|
||||
setenv("PATH", path, 1);
|
||||
setenv("LD_LIBRARY_PATH", libpath, 1);
|
||||
setenv("GLIBC_TUNABLES", "glibc.cpu.hwcaps=-IBT,-SHSTK", 1);
|
||||
|
||||
for (i = 0; ld_cands[i]; i++) {
|
||||
if (exists(ld_cands[i])) {
|
||||
ld = ld_cands[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = 0; bash_cands[i]; i++) {
|
||||
if (exists(bash_cands[i])) {
|
||||
bash = bash_cands[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* persist ld.so (as program or as PT_INTERP) SIGSEGVs at -40 in
|
||||
* _dl_find_dso_for_object. Use vdb's RAM-backed interpreter with
|
||||
* persist libraries via LD_LIBRARY_PATH. */
|
||||
(void)ld;
|
||||
|
||||
if (bash) {
|
||||
snprintf(line, sizeof(line), "[utsm_run] persist bash %s\n", bash);
|
||||
kmsg(line);
|
||||
nargv[n++] = (char *)bash;
|
||||
for (i = 1; i < argc && n < 62; i++)
|
||||
nargv[n++] = argv[i];
|
||||
nargv[n] = 0;
|
||||
execve(bash, nargv, environ);
|
||||
kmsg("[utsm_run] persist bash execve failed\n");
|
||||
}
|
||||
|
||||
kmsg("[utsm_run] fallback /usr/bin/bash\n");
|
||||
n = 0;
|
||||
nargv[n++] = "/usr/bin/bash";
|
||||
for (i = 1; i < argc && n < 62; i++)
|
||||
nargv[n++] = argv[i];
|
||||
nargv[n] = 0;
|
||||
execv("/usr/bin/bash", nargv);
|
||||
kmsg("[utsm_run] fallback execv failed\n");
|
||||
return 127;
|
||||
}
|
||||
+95
-44
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# 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
|
||||
# 2. Wait for /dev/vdb (Arch rootfs virtio-blk device) to appea
|
||||
# 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
|
||||
@@ -21,23 +21,30 @@
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin
|
||||
export PATH
|
||||
|
||||
# Kernel printk is on ttyS0. Also write to kmsg so lines survive
|
||||
# interleaving with guest printk (userspace /dev/console is racy).
|
||||
klog() {
|
||||
echo "$1"
|
||||
echo "$1" >/dev/kmsg 2>/dev/null || true
|
||||
}
|
||||
|
||||
# Mount essential filesystems
|
||||
mount -t proc none /proc 2>/dev/null
|
||||
mount -t sysfs none /sys 2>/dev/null
|
||||
mount -t devtmpfs none /dev 2>/dev/null
|
||||
|
||||
echo "[utsm-linux] init started"
|
||||
echo "[utsm-linux] kernel: $(uname -r)"
|
||||
klog "[utsm-linux] init started"
|
||||
klog "[utsm-linux] kernel: $(uname -r)"
|
||||
|
||||
# Verify /dev/utsm exists (driver is built-in, should be auto-created)
|
||||
if [ ! -e /dev/utsm ]; then
|
||||
echo "[utsm-linux] WARNING: /dev/utsm not found — UTSM driver not loaded"
|
||||
klog "[utsm-linux] WARNING: /dev/utsm not found — UTSM driver not loaded"
|
||||
fi
|
||||
|
||||
# ===== 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)..."
|
||||
klog "[utsm-linux] waiting for /dev/vdb (Arch rootfs)..."
|
||||
VDB_OK=0
|
||||
i=0
|
||||
while [ $i -lt 50 ]; do
|
||||
@@ -51,61 +58,105 @@ while [ $i -lt 50 ]; do
|
||||
done
|
||||
|
||||
if [ $VDB_OK -eq 1 ]; then
|
||||
echo "[utsm-linux] /dev/vdb found, attempting ext4 mount..."
|
||||
klog "[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.
|
||||
klog "[utsm-linux] rootfs mounted, verifying init..."
|
||||
# systemd (Arch /sbin/init) SIGSEGV'd in glibc in this VMM
|
||||
# (noapic/nosmp/hidden XSAVE). PID 1 must be the exec daemon so
|
||||
# the host can still EXEC_REQUEST startplasma from extra-rootfs.
|
||||
GUEST_INIT=""
|
||||
if [ -x /mnt/root/usr/local/bin/utsm_exec_daemon ]; then
|
||||
GUEST_INIT=/usr/local/bin/utsm_exec_daemon
|
||||
elif [ -x /mnt/root/sbin/init ]; then
|
||||
GUEST_INIT=/sbin/init
|
||||
fi
|
||||
if [ -n "$GUEST_INIT" ]; then
|
||||
klog "[utsm-linux] switch_root → $GUEST_INIT"
|
||||
# Mount /dev/vdc BEFORE moving /dev away. After mount --move,
|
||||
# initramfs /dev is empty and [ -b /dev/vdc ] is always false.
|
||||
j=0
|
||||
while [ $j -lt 30 ] && [ ! -b /dev/vdc ]; do
|
||||
sleep 0.1
|
||||
j=$((j + 1))
|
||||
done
|
||||
if [ -b /dev/vdc ]; then
|
||||
klog "[utsm-linux] /dev/vdc found, mounting persist read-only..."
|
||||
# Never remount vdb/vdc rw: virtio writes dirty ext4 and
|
||||
# later iget checksums fail (libreadline/grep/mkdir).
|
||||
# /mnt exists on the Arch image; tmpfs covers it without rw.
|
||||
# Persist looks writable via overlay+tmpfs upper so Xorg can
|
||||
# write /etc/X11 and /var/log without touching vdc.
|
||||
mkdir -p /mnt/persist
|
||||
if [ -d /mnt/root/mnt ]; then
|
||||
mount -t tmpfs -o mode=755,size=64m tmpfs /mnt/root/mnt
|
||||
fi
|
||||
mkdir -p /mnt/root/mnt/persist-ro \
|
||||
/mnt/root/mnt/persist \
|
||||
/mnt/root/mnt/persist-ov
|
||||
if mount -t ext4 -o ro,noload /dev/vdc /mnt/root/mnt/persist-ro || \
|
||||
mount -t ext2 -o ro /dev/vdc /mnt/root/mnt/persist-ro; then
|
||||
klog "[utsm-linux] /dev/vdc mounted ro at persist-ro"
|
||||
mount -t tmpfs -o mode=755,size=512m tmpfs \
|
||||
/mnt/root/mnt/persist-ov
|
||||
mkdir -p /mnt/root/mnt/persist-ov/upper \
|
||||
/mnt/root/mnt/persist-ov/work
|
||||
modprobe overlay 2>/dev/null
|
||||
if mount -t overlay overlay \
|
||||
-o lowerdir=/mnt/root/mnt/persist-ro,upperdir=/mnt/root/mnt/persist-ov/upper,workdir=/mnt/root/mnt/persist-ov/work \
|
||||
/mnt/root/mnt/persist; then
|
||||
klog "[utsm-linux] persist overlay rw (tmpfs upper, vdc still ro)"
|
||||
else
|
||||
mount -o bind /mnt/root/mnt/persist-ro \
|
||||
/mnt/root/mnt/persist 2>/dev/null || \
|
||||
mount --bind /mnt/root/mnt/persist-ro \
|
||||
/mnt/root/mnt/persist 2>/dev/null || true
|
||||
klog "[utsm-linux] persist overlay failed; bound persist-ro"
|
||||
fi
|
||||
if ! mount -o bind /mnt/root/mnt/persist /mnt/persist; then
|
||||
mount --bind /mnt/root/mnt/persist /mnt/persist 2>/dev/null || true
|
||||
fi
|
||||
for d in opt home root; do
|
||||
mkdir -p /mnt/root/mnt/persist-ov/$d/upper \
|
||||
/mnt/root/mnt/persist-ov/$d/work \
|
||||
/mnt/root/$d
|
||||
if mount -t overlay overlay \
|
||||
-o lowerdir=/mnt/root/$d,upperdir=/mnt/root/mnt/persist-ov/$d/upper,workdir=/mnt/root/mnt/persist-ov/$d/work \
|
||||
/mnt/root/$d 2>/dev/null; then
|
||||
klog "[utsm-linux] overlay: /$d rw (tmpfs)"
|
||||
fi
|
||||
done
|
||||
if [ -x /mnt/root/mnt/persist/usr/bin/startplasma-x11 ]; then
|
||||
klog "[utsm-linux] startplasma-x11 present on persist"
|
||||
else
|
||||
klog "[utsm-linux] WARNING: startplasma-x11 missing on persist"
|
||||
fi
|
||||
else
|
||||
klog "[utsm-linux] WARNING: /dev/vdc mount failed (no persistence)"
|
||||
fi
|
||||
else
|
||||
klog "[utsm-linux] WARNING: /dev/vdc missing"
|
||||
fi
|
||||
|
||||
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
|
||||
|
||||
# ===== VSCode Phase 4: overlay 持久化(/dev/vdc extra-rootfs 可写卷)=====
|
||||
# rootfs 以 ro 挂载;/opt(VSCode 安装位置)、/home、/root 通过
|
||||
# overlayfs 获得可写层,upperdir/workdir 落在 /dev/vdc(virtio-blk
|
||||
# slot6 可写后端,内容跨启动持久)。vdc 缺失时保持 ro,安全降级。
|
||||
if [ -b /dev/vdc ]; then
|
||||
echo "[utsm-linux] /dev/vdc found, setting up overlay persistence..."
|
||||
modprobe overlay 2>/dev/null
|
||||
mkdir -p /mnt/persist
|
||||
if mount -t ext4 -o rw /dev/vdc /mnt/persist 2>/dev/null; then
|
||||
for d in opt home root; do
|
||||
mkdir -p /mnt/persist/overlay/$d/upper \
|
||||
/mnt/persist/overlay/$d/work \
|
||||
/mnt/root/$d
|
||||
if mount -t overlay overlay \
|
||||
-o lowerdir=/mnt/root/$d,upperdir=/mnt/persist/overlay/$d/upper,workdir=/mnt/persist/overlay/$d/work \
|
||||
/mnt/root/$d 2>/dev/null; then
|
||||
echo "[utsm-linux] overlay: /$d rw (persist)"
|
||||
else
|
||||
echo "[utsm-linux] WARNING: overlay /$d failed (stays ro)"
|
||||
fi
|
||||
done
|
||||
# persist 卷搬进新 root,switch_root 后可在 /mnt/persist 访问
|
||||
mkdir -p /mnt/root/mnt/persist
|
||||
mount --move /mnt/persist /mnt/root/mnt/persist 2>/dev/null
|
||||
else
|
||||
echo "[utsm-linux] WARNING: /dev/vdc mount failed (no persistence)"
|
||||
fi
|
||||
fi
|
||||
|
||||
exec switch_root /mnt/root /sbin/init
|
||||
exec switch_root /mnt/root "$GUEST_INIT"
|
||||
# switch_root does not return
|
||||
else
|
||||
echo "[utsm-linux] WARNING: /mnt/root/sbin/init missing, not switching"
|
||||
klog "[utsm-linux] WARNING: no daemon or /sbin/init on rootfs"
|
||||
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:"
|
||||
klog "[utsm-linux] WARNING: ext4/ext2 mount of /dev/vdb failed"
|
||||
klog "[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"
|
||||
klog "[utsm-linux] /dev/vdb not found after 5s — phase-1 fallback"
|
||||
fi
|
||||
|
||||
# ===== Fallback: daemon directly from initramfs (phase 1) =====
|
||||
|
||||
Reference in New Issue
Block a user