Skip to content

Commit

Permalink
pass args to command (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
tombl authored Jan 18, 2025
1 parent f7a46b8 commit c54b53a
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 17 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
- [x] `binfmt_wasm`
- [x] Jumping into userspace
- [x] Expose syscall dispatch
- [x] [Port musl](https://github.com/tombl/musl)
- [ ] Port busybox

## Eventually:

- signals
- virtio-rng backed by `crypto.getRandomValues()`
- virtio-net to connect multiple machines
- and service worker loopback
- virtio-console to support multiple terminals
- enhanced virtio-console to support terminal resizing and multiple terminals
- virtio-fs backed by the [File System API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_API)
- vsock to implement custom javascript integrations
- unrestricted vscode in the browser?
Expand Down
21 changes: 16 additions & 5 deletions arch/wasm/include/asm/thread_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,30 @@
#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER)
#define THREAD_SHIFT (PAGE_SHIFT << THREAD_SIZE_ORDER)

struct wasm_process_args {
int len, envc, argc;
char **argv, **envp;
char data[];
};

struct thread_info {
unsigned long flags;
unsigned long syscall_work; /* SYSCALL_WORK_ flags */
unsigned long syscall_work; /* SYSCALL_WORK_ flags */
int preempt_count;
int cpu; // this is for the kernel
atomic_t running_cpu; // negative means unscheduled
unsigned long tp_value;
struct wasm_process_args *args;
};

#define INIT_THREAD_INFO(tsk) \
{ \
.flags = 0, .preempt_count = INIT_PREEMPT_COUNT, .cpu = 0, \
.running_cpu = ATOMIC_INIT(0), .tp_value = U32_MAX, \
#define INIT_THREAD_INFO(tsk) \
{ \
.flags = 0, \
.preempt_count = INIT_PREEMPT_COUNT, \
.cpu = 0, \
.running_cpu = ATOMIC_INIT(0), \
.tp_value = U32_MAX, \
.args = NULL, \
}

#define TIF_SYSCALL_TRACE 0 /* syscall trace active */
Expand Down
File renamed without changes.
93 changes: 93 additions & 0 deletions arch/wasm/kernel/binfmt_wasm.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/binfmts.h>
#include <linux/highmem.h>
#include <linux/mman.h>
#include <linux/personality.h>
#include <linux/ptrace.h>
#include <linux/syscalls.h>

static int load_wasm_binary(struct linux_binprm *bprm);

Expand All @@ -23,6 +25,93 @@ static int file_get_size(struct file *f, loff_t *size)
return 0;
}

static int copy_args(struct linux_binprm *bprm)
{
struct wasm_process_args *args;
int stop = bprm->p >> PAGE_SHIFT;
int len = ((bprm->argc + bprm->envc + 2) * sizeof(char *)) +
(PAGE_SIZE * (MAX_ARG_PAGES - stop)) + (bprm->p & ~PAGE_MASK);
char *data;
int ret;

args = kmalloc(sizeof(*args) + len, GFP_KERNEL);
if (!args)
return -ENOMEM;

args->argc = bprm->argc;
args->envc = bprm->envc;
args->len = len;

data = args->data + len;
for (int index = MAX_ARG_PAGES - 1; index >= stop; index--) {
unsigned int offset = index == stop ? bprm->p & ~PAGE_MASK : 0;
char *src = kmap_local_page(bprm->page[index]) + offset;
data -= PAGE_SIZE - offset;
memcpy(data, src, PAGE_SIZE - offset);
kunmap_local(src);
}

args->argv = (char **)(args->data);
for (int i = bprm->argc; i > 0; i--) {
len = strnlen(data, MAX_ARG_STRLEN);
if (!len || len > MAX_ARG_STRLEN) {
ret = -EINVAL;
goto err;
}
args->argv[i - 1] = data;
data += len + 1;
}
args->argv[bprm->argc] = NULL;

args->envp = args->argv + bprm->argc + 1;
for (int i = bprm->envc; i > 0; i--) {
len = strnlen(data, MAX_ARG_STRLEN);
if (!len || len > MAX_ARG_STRLEN) {
ret = -EINVAL;
goto err;
}
args->envp[i - 1] = data;
data += len + 1;
}
args->envp[bprm->envc] = NULL;

current_thread_info()->args = args;
return 0;
err:
kfree(args);
return ret;
}

__attribute__((export_name("get_args_length"))) int get_args_length(void)
{
struct wasm_process_args *args = current_thread_info()->args;
return args ? sizeof(*args) + args->len : -EINVAL;
}

__attribute__((export_name("get_args"))) int get_args(void *buf)
{
struct wasm_process_args *args = current_thread_info()->args;
long offset = ((long)buf - (long)args);
if (!args)
return -EINVAL;

for (int i = 0; i < args->argc; i++)
args->argv[i] += offset;
for (int i = 0; i < args->envc; i++)
args->envp[i] += offset;

args->argv += offset / sizeof(void *);
args->envp += offset / sizeof(void *);

if (copy_to_user(buf, args, sizeof(*args) + args->len))
return -EFAULT;

kfree(args);
current_thread_info()->args = NULL;

return 0;
}

static int load_wasm_binary(struct linux_binprm *bprm)
{
int ret;
Expand Down Expand Up @@ -52,6 +141,10 @@ static int load_wasm_binary(struct linux_binprm *bprm)
if (ret)
goto err;

ret = copy_args(bprm);
if (ret)
goto err;

ret = begin_new_exec(bprm);
if (ret)
goto err;
Expand Down
18 changes: 11 additions & 7 deletions arch/wasm/kernel/process.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,17 +130,21 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
// this is probably a bad idea
if (args->idle) return 0;

if (!args->fn)
panic("can't copy userspace thread"); // yet

bootstrap_args =
kzalloc(sizeof(struct task_bootstrap_args), GFP_KERNEL);
if (!bootstrap_args)
panic("can't allocate bootstrap args");

return -ENOMEM;
bootstrap_args->task = p;
bootstrap_args->fn = args->fn;
bootstrap_args->fn_arg = args->fn_arg;

if (unlikely(args->fn)) {
bootstrap_args->fn = args->fn;
bootstrap_args->fn_arg = args->fn_arg;
} else {
// TODO: hmm this is a userspace thread
// we need to copy the instance and the memory from the current worker
// into the new one.
pr_warn("currently unsupported: a userspace thread called clone()\n");
}

name_len = snprintf(name, ARRAY_SIZE(name), "%s (%d)", p->comm, p->pid);

Expand Down
3 changes: 2 additions & 1 deletion justfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
j := `nproc`
cmdline := "no_hash_pointers"

build:
just make -C tools/wasm
Expand All @@ -7,7 +8,7 @@ watch:
watchexec -r -f '**/*.c' -f '**/*.h' -f '**/Makefile*' -f 'tools/wasm/src/**/*.ts' just build

run:
tools/wasm/run.js -j4
tools/wasm/run.js -j4 -c '{{cmdline}}'
watchrun:
watchexec -r -w tools/wasm/dist --ignore-nothing --debounce=200ms just run

Expand Down
Binary file removed tools/wasm/public/initramfs.cpio
Binary file not shown.
2 changes: 2 additions & 0 deletions tools/wasm/src/wasm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export interface Instance extends WebAssembly.Instance {
arg5: number,
): number;
get_thread_area(): number;
get_args_length(): number;
get_args(buf: number): number;
};
}

Expand Down
7 changes: 5 additions & 2 deletions tools/wasm/src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ self.onmessage = (event: MessageEvent<InitMessage>) => {
// TODO: read the real initial size from the module.
// TOOD: enforce rlimit via maximum.
user_memory = new WebAssembly.Memory({
initial: 10,
maximum: 100,
initial: 2048,
maximum: 2048,
});
user_memory_buffer = new Uint8Array(user_memory.buffer);

Expand All @@ -63,6 +63,8 @@ self.onmessage = (event: MessageEvent<InitMessage>) => {
linux: {
syscall: instance.exports.syscall,
get_thread_area: instance.exports.get_thread_area,
get_args_length: instance.exports.get_args_length,
get_args: instance.exports.get_args,
},
});

Expand Down Expand Up @@ -126,4 +128,5 @@ self.onmessage = (event: MessageEvent<InitMessage>) => {
const { _start } = user_instance.exports;
assert(typeof _start === "function", "_start not found");
_start();
instance.exports.syscall(60, 37, 0, 0, 0, 0, 0); // exit(37)
};

0 comments on commit c54b53a

Please sign in to comment.