diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d9088ef --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# EditorConfig is awesome: http://EditorConfig.org + +root = true + +[*] +end_of_line = lf +# Do not trim everywhere: makefiles, configs are not usually follows this policy +#trim_trailing_whitespace = true + +[c,h] +indent_style = tab +indent_size = 8 +trim_trailing_whitespace = true + diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1dbf9a0..88ec0f1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,8 +13,7 @@ jobs: fail-fast: false matrix: test: - - batch - - pool + - multithread steps: - uses: actions/checkout@v2 diff --git a/.gitignore b/.gitignore index d81c570..38898e8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ *.o *.o.d /main +/rootfs.cpio +*.app # IDE and tooling /tags diff --git a/Makefile b/Makefile index 5b1aba7..e59a7b7 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,26 @@ -CFLAGS = -g +CFLAGS = -g -MMD -MT $@ -MF $@.d +ASFLAGS = $(CFLAGS) -all : main +KERNEL_START := 0xf00000000 +USERSPACE_START := 0x400000 -main : $(patsubst %.c,%.o,$(wildcard *.c)) +CFLAGS += -DIKERNEL_START=$(KERNEL_START) -DIUSERSPACE_START=$(USERSPACE_START) + +all : main rootfs.cpio + +main : $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(filter-out %.app.c,$(wildcard *.[cS])))) + $(CC) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@ + +APPS = $(patsubst %.app.c,%.app,$(wildcard *.app.c)) + +$(APPS) : %.app : %.app.c + $(CC) -fno-stack-protector -fno-pic -Wl,-Ttext-segment=$(USERSPACE_START) -nostdlib -e main -static -x c $< -o $@ + +rootfs.cpio : $(APPS) + ls -1 $^ | cpio -o -H bin > $@ clean : - rm -f *.o main + rm -f *.[od] main rootfs.cpio *.app + +-include $(wildcard *.d) + diff --git a/main.c b/apps.c similarity index 71% rename from main.c rename to apps.c index 089e052..9c63ec7 100644 --- a/main.c +++ b/apps.c @@ -1,9 +1,16 @@ #include +#include #include #include #include +#include +#include +#include + +#include "sched.h" +#include "usyscall.h" #include "pool.h" static int g_retcode; @@ -11,8 +18,6 @@ static int g_retcode; #define APPS_X(X) \ X(echo) \ X(retcode) \ - X(pooltest) \ - #define DECLARE(X) static int X(int, char *[]); APPS_X(DECLARE) @@ -27,15 +32,26 @@ static const struct app { #undef ELEM }; +static int os_printf(const char *fmt, ...) { + char buf[128]; + va_list ap; + va_start(ap, fmt); + int ret = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + return os_write(1, buf, ret); +} + static int echo(int argc, char *argv[]) { for (int i = 1; i < argc; ++i) { printf("%s%c", argv[i], i == argc - 1 ? '\n' : ' '); } + fflush(stdout); return argc - 1; } static int retcode(int argc, char *argv[]) { printf("%d\n", g_retcode); + fflush(stdout); return 0; } @@ -57,26 +73,6 @@ static int exec(int argc, char *argv[]) { return g_retcode; } -static int pooltest(int argc, char *argv[]) { - struct obj { - void *field1; - void *field2; - }; - static struct obj objmem[4]; - static struct pool objpool = POOL_INITIALIZER_ARRAY(objmem); - - if (!strcmp(argv[1], "alloc")) { - struct obj *o = pool_alloc(&objpool); - printf("alloc %d\n", o ? (o - objmem) : -1); - return 0; - } else if (!strcmp(argv[1], "free")) { - int iobj = atoi(argv[2]); - printf("free %d\n", iobj); - pool_free(&objpool, objmem + iobj); - return 0; - } -} - int shell(int argc, char *argv[]) { char line[256]; while (fgets(line, sizeof(line), stdin)) { @@ -84,12 +80,12 @@ int shell(int argc, char *argv[]) { char *stcmd; char *cmd = strtok_r(line, comsep, &stcmd); while (cmd) { - const char *argsep = " "; + const char *argsep = " \t"; char *starg; char *arg = strtok_r(cmd, argsep, &starg); char *argv[256]; int argc = 0; - while (arg) { + while (arg && arg[0] != '#') { argv[argc++] = arg; arg = strtok_r(NULL, argsep, &starg); } @@ -106,8 +102,3 @@ int shell(int argc, char *argv[]) { } return 0; } - - -int main(int argc, char *argv[]) { - shell(0, NULL); -} diff --git a/ctx.c b/ctx.c new file mode 100644 index 0000000..d27434d --- /dev/null +++ b/ctx.c @@ -0,0 +1,13 @@ +#include + +#include "ctx.h" + +void ctx_make(struct ctx *ctx, void *entry, void *stack, int alignment) { + memset(ctx, 0, sizeof(*ctx)); + + if(alignment == STANDARD) stack = (void*)(((unsigned long)stack & ~0xf) - 8); + ctx->rsp = (unsigned long) stack; + ctx->rsp -= 8; + *(unsigned long *)ctx->rsp = (unsigned long) entry; +} + diff --git a/ctx.h b/ctx.h new file mode 100644 index 0000000..5803118 --- /dev/null +++ b/ctx.h @@ -0,0 +1,23 @@ +#pragma once + +enum Alignment { + NONE, + STANDARD +}; + + +struct ctx { + unsigned long rbx; + unsigned long r12; + unsigned long r13; + unsigned long r14; + unsigned long r15; + unsigned long rsp; + unsigned long rbp; + unsigned long rip; +}; + +extern void ctx_make(struct ctx *ctx, void *entry, void *stack, int alignment); + +extern void ctx_switch(struct ctx *old, struct ctx *new); + diff --git a/ctx_switch.S b/ctx_switch.S new file mode 100644 index 0000000..770313f --- /dev/null +++ b/ctx_switch.S @@ -0,0 +1,21 @@ + +.text +.global ctx_switch +ctx_switch: + mov %rbx, 0*8(%rdi) + mov %r12, 1*8(%rdi) + mov %r13, 2*8(%rdi) + mov %r14, 3*8(%rdi) + mov %r15, 4*8(%rdi) + mov %rsp, 5*8(%rdi) + mov %rbp, 6*8(%rdi) + + mov 0*8(%rsi), %rbx + mov 1*8(%rsi), %r12 + mov 2*8(%rsi), %r13 + mov 3*8(%rsi), %r14 + mov 4*8(%rsi), %r15 + mov 5*8(%rsi), %rsp + mov 6*8(%rsi), %rbp + + ret diff --git a/grep.app.c b/grep.app.c new file mode 100644 index 0000000..472bc9f --- /dev/null +++ b/grep.app.c @@ -0,0 +1,62 @@ +#include "usyscall.h" + +char *strstr(const char *where, const char *what) { + int a = 0; + int b; + + do { + b = 0; + while (what[b] && what[b] == where[a + b]) { + ++b; + } + if (!what[b]) { + return (char*)(where + a); + } + } while (where[a++ + b]); + return (char*)0; +} + +void *memchr(const void *str, int c, long unsigned n) { + const char *s = str; + const char *e = str + n; + while (s < e) { + if (*s == c) { + return (void*)s; + } + ++s; + } + return (void*)0; +} + +void *memmove(void *dst, const void *src, long unsigned n) { + const char *f = src; + char *t = dst; + while (n--) { + *t++ = *f++; + } + return dst; +} + +int main(int argc, char* argv[]) { + char buf[5]; + int len; + int o = 0; + while (0 < (len = os_read(0, buf + o, sizeof(buf) - o))) { + len += o; + char *p = buf; + char *p2 = memchr(p, '\n', len); + while (p2) { + *p2 = '\0'; + if (strstr(p, argv[1])) { + os_write(1, p, p2 - p); + os_write(1, "\n", 1); + } + p = p2 + 1; + p2 = memchr(p, '\n', len - (p - buf)); + } + o = len - (p - buf); + memmove(buf, p, o); + } + + os_exit(0); +} diff --git a/init.app.c b/init.app.c new file mode 100644 index 0000000..afb2e92 --- /dev/null +++ b/init.app.c @@ -0,0 +1,40 @@ +#include "usyscall.h" + +long unsigned strlen(const char *str) { + const char *e = str; + while (*e) { + ++e; + } + return e - str; +} + +int os_print(int fd, const char *str) { + int len = strlen(str); + return os_write(fd, str, len); +} + +int main(int argc, char* argv[]) { + os_write(1, "start\n", 6); + + int pipe[2]; + int ret = os_pipe(pipe); + if (ret < 0) { + os_print(2, "cannot create pipe\n"); + os_exit(1); + } + int pid = os_fork(); + if (pid) { + os_close(1); + os_dup(pipe[1]); + const char *arg[] = { "seq", "100", (char*)0 }; + os_exec("seq", (char**)arg); + } else { + os_close(0); + os_dup(pipe[0]); + const char *arg[] = { "grep", "2", (char*)0 }; + os_exec("grep", (char**)arg); + } + + os_print(2, "should not reach here\n"); + os_exit(1); +} diff --git a/pool.c b/pool.c index b5d39c9..3f29c82 100644 --- a/pool.c +++ b/pool.c @@ -4,11 +4,31 @@ #include "pool.h" void pool_init(struct pool *p, void *mem, unsigned long nmemb, unsigned long membsz) { + p->mem = mem; + p->membsz = membsz; + p->freestart = mem; + p->freeend = p->freestart + nmemb * membsz; + p->freehead = NULL; } void *pool_alloc(struct pool *p) { + if (p->freestart < p->freeend) { + void *r = p->freestart; + p->freestart += p->membsz; + return r; + } + + struct pool_free_block *fb = p->freehead; + if (fb) { + p->freehead = fb->next; + return fb; + } + return NULL; } void pool_free(struct pool *p, void *ptr) { + struct pool_free_block *fb = ptr; + fb->next = p->freehead; + p->freehead = fb; } diff --git a/pool.h b/pool.h index 4906f2d..8fd6807 100644 --- a/pool.h +++ b/pool.h @@ -2,11 +2,24 @@ #include "util.h" -struct pool { +struct pool_free_block { + struct pool_free_block *next; +}; +struct pool { + char *mem; + unsigned long membsz; + char *freestart; + char *freeend; + struct pool_free_block *freehead; }; #define POOL_INITIALIZER(_mem, _nmemb, _membsz) { \ + .mem = (char*)(_mem), \ + .membsz = (_membsz), \ + .freehead = NULL, \ + .freestart = (char*)(_mem), \ + .freeend = (char*)(_mem) + (_nmemb) * (_membsz), \ } #define POOL_INITIALIZER_ARRAY(_array) \ diff --git a/sched.c b/sched.c new file mode 100644 index 0000000..5b66acc --- /dev/null +++ b/sched.c @@ -0,0 +1,854 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sched.h" +#include "timer.h" +#include "pool.h" +#include "ctx.h" +#include "syscall.h" +#include "usyscall.h" + +/* AMD64 Sys V ABI, 3.2.2 The Stack Frame: + The 128-byte area beyond the location pointed to by %rsp is considered to + be reserved and shall not be modified by signal or interrupt handlers */ +#define SYSV_REDST_SZ 128 + +#define TICK_PERIOD 100 + +#define MEM_PAGES 1024 +#define PAGE_SIZE 4096 + +#define USER_PAGES 1024 +#define USER_START ((void*)IUSERSPACE_START) +#define USER_STACK_PAGES 2 + +#define FD_MAX 16 + +#define offsetof(s, f) ((unsigned long)(&((s*)0)->f)) + +extern int shell(int argc, char *argv[]); + +extern void tramptramp(void); +extern void exittramp(void); + +struct vmctx { + unsigned map[USER_PAGES]; + unsigned brk; + unsigned stack; +}; + +struct fileops { + int (*read)(int fd, void *buf, unsigned sz); + int (*write)(int fd, const void *buf, unsigned sz); + int (*close)(int fd); +}; + +struct file { + const struct fileops *ops; + int usecnt; +}; + +struct task { + char stack[8192]; + struct vmctx vm; + + union { + struct ctx ctx; + struct { + int(*main)(int, char**); + int argc; + char **argv; + }; + }; + + struct file *fd[FD_MAX]; + + void (*entry)(void *as); + void *as; + int priority; + + // timeout support + int waketime; + + // policy support + struct task *next; +} __attribute__((aligned(16))); + +struct savedctx { + unsigned long rbp; + unsigned long r15; + unsigned long r14; + unsigned long r13; + unsigned long r12; + unsigned long r11; + unsigned long r10; + unsigned long r9; + unsigned long r8; + unsigned long rdi; + unsigned long rsi; + unsigned long rdx; + unsigned long rcx; + unsigned long rbx; + unsigned long rax; + unsigned long rflags; + unsigned long bottom; + unsigned long stack; + unsigned long sig; + unsigned long oldsp; + unsigned long rip; +}; + +struct pipe { + char buf[1024]; + unsigned long rd, wr; + struct file rdend, wrend; + struct task *q; + unsigned rdclose : 1; + unsigned wrclose : 1; +}; +static struct pipe pipearray[4]; +static struct pool pipepool = POOL_INITIALIZER_ARRAY(pipearray); + +static void syscallbottom(unsigned long sp); +static int do_fork(unsigned long sp); +static void set_fd(struct task *t, int fd, struct file *newf); +static int pipe_read(int fd, void *buf, unsigned sz); + +static int time; + +static int current_start; +static struct task *current; +static struct task *idle; +static struct task *runq; +static struct task *waitq; + +static struct task *pendingq; +static struct task *lastpending; + +static int (*policy_cmp)(struct task *t1, struct task *t2); + +static struct task taskarray[16]; +static struct pool taskpool = POOL_INITIALIZER_ARRAY(taskarray); + +static sigset_t irqs; + +static int memfd = -1; +#define LONG_BITS (sizeof(unsigned long) * CHAR_BIT) +static unsigned long bitmap_pages[MEM_PAGES / LONG_BITS]; + +static void *rootfs; +static unsigned long rootfs_sz; + +void irq_disable(void) { + sigprocmask(SIG_BLOCK, &irqs, NULL); +} + +void irq_enable(void) { + sigprocmask(SIG_UNBLOCK, &irqs, NULL); +} + +static int bitmap_alloc(unsigned long *bitmap, size_t size) { + unsigned n = size / sizeof(*bitmap); + unsigned long *w = NULL; + for (int i = 0; i < n; ++i) { + if (bitmap[i] != -1) { + w = &bitmap[i]; + break; + } + } + if (!w) { + fprintf(stderr, "cannot find free page\n"); + abort(); + return -1; + } + int v = ffsl(*w + 1) - 1; + *w |= 1 << v; + return v + (w - bitmap) * LONG_BITS; +} + +static void bitmap_free(unsigned long *bitmap, size_t size, unsigned v) { + bitmap[v / LONG_BITS] &= ~(1 << (v % LONG_BITS)); +} + +static void policy_run(struct task *t) { + struct task **c = &runq; + + while (*c && (t == idle || policy_cmp(*c, t) <= 0)) { + c = &(*c)->next; + } + t->next = *c; + *c = t; +} + +static void push_task(struct task **q, struct task *t) { + t->next = *q; + *q = t; +} + +static struct task *pop_task(struct task **q) { + struct task *t = *q; + if (t) { + *q = t->next; + } + return t; +} + +static void vmctx_make(struct vmctx *vm, size_t stack_size) { + vm->stack = USER_PAGES - stack_size / PAGE_SIZE; + memset(vm->map, -1, sizeof(vm->map)); + for (int i = 0; i < stack_size / PAGE_SIZE; ++i) { + int mempage = bitmap_alloc(bitmap_pages, sizeof(bitmap_pages)); + if (mempage == -1) { + abort(); + } + vm->map[USER_PAGES - 1 - i] = mempage; + } +} + +static void vmctx_apply(struct vmctx *vm) { + munmap(USER_START, USER_PAGES * PAGE_SIZE); + for (int i = 0; i < USER_PAGES; ++i) { + if (vm->map[i] == -1) { + continue; + } + void *addr = mmap(USER_START + i * PAGE_SIZE, + PAGE_SIZE, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_SHARED | MAP_FIXED, + memfd, vm->map[i] * PAGE_SIZE); + if (addr == MAP_FAILED) { + perror("mmap"); + abort(); + } + + if (addr != USER_START + i * PAGE_SIZE) { + abort(); + } + } +} + +static void doswitch(void) { + struct task *old = current; + current = pop_task(&runq); + + current_start = sched_gettime(); + vmctx_apply(¤t->vm); + ctx_switch(&old->ctx, ¤t->ctx); +} + +static void tasktramp(void) { + irq_enable(); + current->entry(current->as); + irq_disable(); + doswitch(); +} + +struct task *sched_new(void (*entrypoint)(void *), void *aspace, int priority, int alignment) { + + struct task *t = pool_alloc(&taskpool); + t->entry = entrypoint; + t->as = aspace; + t->priority = priority; + t->next = NULL; + + ctx_make(&t->ctx, tasktramp, t->stack + sizeof(t->stack), alignment); + + return t; +} + +void sched_sleep(unsigned ms) { + + if (!ms) { + irq_disable(); + policy_run(current); + doswitch(); + irq_enable(); + return; + } + + current->waketime = sched_gettime() + ms; + + int curtime; + while ((curtime = sched_gettime()) < current->waketime) { + irq_disable(); + struct task **c = &waitq; + while (*c && (*c)->waketime < current->waketime) { + c = &(*c)->next; + } + current->next = *c; + *c = current; + + doswitch(); + irq_enable(); + } +} + +static int fifo_cmp(struct task *t1, struct task *t2) { + return -1; +} + +static int prio_cmp(struct task *t1, struct task *t2) { + return t2->priority - t1->priority; +} + +static void hctx_push(greg_t *regs, unsigned long val) { + regs[REG_RSP] -= sizeof(unsigned long); + *(unsigned long *) regs[REG_RSP] = val; +} + +static void timerbottom() { + time += TICK_PERIOD; + + while (waitq && waitq->waketime <= sched_gettime()) { + struct task *t = waitq; + waitq = waitq->next; + policy_run(t); + } + + if (TICK_PERIOD <= sched_gettime() - current_start) { + irq_disable(); + policy_run(current); + doswitch(); + irq_enable(); + } +} + +static unsigned long bottom(unsigned long sp, int sig) { + if (sig == SIGALRM) { + timerbottom(); + } else if (sig == SIGSEGV) { + syscallbottom(sp); + } + return sp; +} + +static void top(int sig, siginfo_t *info, void *ctx) { + ucontext_t *uc = (ucontext_t *) ctx; + greg_t *regs = uc->uc_mcontext.gregs; + + if (sig == SIGSEGV) { + uint16_t insn = *(uint16_t*)regs[REG_RIP]; + if (insn != 0x81cd) { + abort(); + } + } + + unsigned long oldsp = regs[REG_RSP]; + regs[REG_RSP] -= SYSV_REDST_SZ; + hctx_push(regs, regs[REG_RIP]); + hctx_push(regs, oldsp); + hctx_push(regs, sig); + hctx_push(regs, (unsigned long) (current->stack + sizeof(current->stack) - 16)); + hctx_push(regs, (unsigned long) bottom); + regs[REG_RIP] = (greg_t) tramptramp; +} + +long sched_gettime(void) { + int cnt1 = timer_cnt() / 1000; + int time1 = time; + int cnt2 = timer_cnt() / 1000; + int time2 = time; + + return (cnt1 <= cnt2) ? + time1 + cnt2 : + time2 + cnt2; +} + +void sched_run(void) { + + sigemptyset(&irqs); + sigaddset(&irqs, SIGALRM); + + /*timer_init(TICK_PERIOD, top);*/ + + irq_disable(); + + idle = pool_alloc(&taskpool); + memset(&idle->vm.map, -1, sizeof(idle->vm.map)); + + current = idle; + + sigset_t none; + sigemptyset(&none); + + while (runq || waitq) { + if (runq) { + policy_run(current); + doswitch(); + } else { + sigsuspend(&none); + } + + } + + irq_enable(); +} + +static void syscallbottom(unsigned long sp) { + struct savedctx *sc = (struct savedctx *)sp; + + uint16_t insn = *(uint16_t*)sc->rip; + if (insn != 0x81cd) { + abort(); + } + + sc->rip += 2; + + if (sc->rax == os_syscall_nr_fork) { + sc->rax = do_fork(sp); + } else { + sc->rax = syscall_do(sc->rax, sc->rbx, + sc->rcx, sc->rdx, + sc->rsi, (void *) sc->rdi); + } +} + +static int vmctx_brk(struct vmctx *vm, void *addr) { + int newbrk = (addr - USER_START + PAGE_SIZE - 1) / PAGE_SIZE; + if ((newbrk < 0) || (USER_PAGES <= newbrk)) { + fprintf(stderr, "Out-of-mem\n"); + abort(); + } + + for (unsigned i = vm->brk; i < newbrk; ++i) { + vm->map[i] = bitmap_alloc(bitmap_pages, sizeof(bitmap_pages)); + } + for (unsigned i = newbrk; i < vm->brk; ++i) { + bitmap_free(bitmap_pages, sizeof(bitmap_pages), vm->map[i]); + } + vm->brk = newbrk; + + return 0; +} + +int vmprotect(void *start, unsigned len, int prot) { +#if 0 + if (mprotect(start, len, prot)) { + perror("mprotect"); + return -1; + } +#endif + return 0; +} + +static void exectramp(void) { + irq_enable(); + current->main(current->argc, current->argv); + irq_disable(); + abort(); +} + +int sys_exec(const char *path, char **argv) { + char elfpath[32]; + snprintf(elfpath, sizeof(elfpath), "%s.app", path); + + fprintf(stderr, "FIXME: find elf content in `rootfs`\n"); + abort(); + void *rawelf = NULL; + + if (strncmp(rawelf, "\x7f" "ELF" "\x2", 5)) { + printf("ELF header mismatch\n"); + return 1; + } + + // https://linux.die.net/man/5/elf + // + // Find Elf64_Ehdr -- at the very start + // Elf64_Phdr -- find one with PT_LOAD, load it for execution + // Find entry point (e_entry) + + const Elf64_Ehdr *ehdr = (const Elf64_Ehdr *) rawelf; + if (!ehdr->e_phoff || + !ehdr->e_phnum || + !ehdr->e_entry || + ehdr->e_phentsize != sizeof(Elf64_Phdr)) { + printf("bad ehdr\n"); + return 1; + } + const Elf64_Phdr *phdrs = (const Elf64_Phdr *) (rawelf + ehdr->e_phoff); + + void *maxaddr = USER_START; + for (int i = 0; i < ehdr->e_phnum; ++i) { + const Elf64_Phdr *ph = phdrs + i; + if (ph->p_type != PT_LOAD) { + continue; + } + if (ph->p_vaddr < IUSERSPACE_START) { + printf("bad section\n"); + return 1; + } + void *phend = (void*)(ph->p_vaddr + ph->p_memsz); + if (maxaddr < phend) { + maxaddr = phend; + } + } + + char **copyargv = USER_START + (USER_PAGES - 1) * PAGE_SIZE; + char *copybuf = (char*)(copyargv + 32); + char *const *arg = argv; + char **copyarg = copyargv; + while (*arg) { + *copyarg++ = strcpy(copybuf, *arg++); + copybuf += strlen(copybuf) + 1; + } + *copyarg = NULL; + + if (vmctx_brk(¤t->vm, maxaddr)) { + printf("vmctx_brk fail\n"); + return 1; + } + + vmctx_apply(¤t->vm); + + if (vmprotect(USER_START, maxaddr - USER_START, PROT_READ | PROT_WRITE)) { + printf("vmprotect RW failed\n"); + return 1; + } + + for (int i = 0; i < ehdr->e_phnum; ++i) { + const Elf64_Phdr *ph = phdrs + i; + if (ph->p_type != PT_LOAD) { + continue; + } + memcpy((void*)ph->p_vaddr, rawelf + ph->p_offset, ph->p_filesz); + int prot = (ph->p_flags & PF_X ? PROT_EXEC : 0) | + (ph->p_flags & PF_W ? PROT_WRITE : 0) | + (ph->p_flags & PF_R ? PROT_READ : 0); + if (vmprotect((void*)ph->p_vaddr, ph->p_memsz, prot)) { + printf("vmprotect section failed\n"); + return 1; + } + } + + struct ctx dummy; + struct ctx new; + ctx_make(&new, exectramp, (char*)copyargv, STANDARD); + + irq_disable(); + current->main = (void*)ehdr->e_entry; + current->argv = copyargv; + current->argc = copyarg - copyargv; + ctx_switch(&dummy, &new); +} + +static void inittramp(void* arg) { + char *args = { NULL }; + sys_exec("init", &args); +} + +static void forktramp(void* arg) { + vmctx_apply(¤t->vm); + + struct savedctx *sc = arg; + sc->rax = 0; + + struct ctx dummy; + struct ctx new; + ctx_make(&new, exittramp, arg, NONE); + ctx_switch(&dummy, &new); +} + +static void copyrange(struct vmctx *vm, unsigned from, unsigned to) { + for (unsigned i = from; i < to; ++i) { + vm->map[i] = bitmap_alloc(bitmap_pages, sizeof(bitmap_pages)); + if (vm->map[i] == -1) { + abort(); + } + if (-1 == pwrite(memfd, + USER_START + i * PAGE_SIZE, + PAGE_SIZE, + vm->map[i] * PAGE_SIZE)) { + perror("pwrite"); + abort(); + } + } +} + +static void vmctx_copy(struct vmctx *dst, struct vmctx *src) { + dst->brk = src->brk; + dst->stack = src->stack; + copyrange(dst, 0, src->brk); + copyrange(dst, src->stack, USER_PAGES - 1); +} + +static int do_fork(unsigned long sp) { + struct task *t = sched_new(forktramp, (void *) sp, 0, NONE); + vmctx_copy(&t->vm, ¤t->vm); + for (int i = 0; i < FD_MAX; ++i) { + set_fd(t, i, current->fd[i]); + } + policy_run(t); + return t - taskarray + 1; +} + +int sys_exit(int code) { + doswitch(); +} + +int sys_read(int fd, void *str, unsigned len) { + struct file *f = current->fd[fd]; + if (!f || !f->ops->read) { + return -1; + } + return f->ops->read(fd, str, len); +} + +int sys_write(int fd, const void *str, unsigned len) { + struct file *f = current->fd[fd]; + if (!f || !f->ops->write) { + return -1; + } + return f->ops->write(fd, str, len); +} + +static void set_fd(struct task *t, int fd, struct file *newf) { + + if (newf) { + ++newf->usecnt; + } + + struct file *f = t->fd[fd]; + if (f) { + if (--f->usecnt == 0 && f->ops->close) { + f->ops->close(fd); + } + } + t->fd[fd] = newf; +} + +int sys_close(int fd) { + set_fd(current, fd, NULL); +} + +static int find_fd(int from) { + for (int i = from; i < FD_MAX; ++i) { + if (!current->fd[i]) { + return i; + } + } + return -1; +} + +int sys_dup(int fd) { + struct file *f = current->fd[fd]; + int newfd = find_fd(0); + if (0 <= newfd) { + set_fd(current, newfd, f); + } + return newfd; +} + +static struct pipe *fd2pipe(int fd, bool *read) { + struct file *f = current->fd[fd]; + struct pipe *p; + bool r = f->ops->read == pipe_read; + if (read) { + *read = r; + } + int off = r ? offsetof(struct pipe, rdend) : offsetof(struct pipe, wrend); + return (struct pipe *)((char*)f - off); +} + +static int min(int a, int b) { + return a < b ? a : b; +} + +static int pipe_read(int fd, void *buf, unsigned sz) { + struct pipe *p = fd2pipe(fd, NULL); + + void *rdbuf = buf; + do { + int data; + while (!(data = (p->wr < p->rd) ? sizeof(p->buf) - p->rd : p->wr - p->rd)) { + push_task(&p->q, current); + doswitch(); + } + + if (sz < data) { + data = sz; + } + memcpy(rdbuf, p->buf + p->rd, data); + p->rd = (p->rd + data) % sizeof(p->buf); + rdbuf += data; + sz -= data; + struct task *t = pop_task(&p->q); + if (t) { + policy_run(t); + } + } while (sz && !p->wrclose); + return rdbuf - buf; +} + +static int pipe_write(int fd, const void *buf, unsigned sz) { + struct pipe *p = fd2pipe(fd, NULL); + + const void *wrbuf = buf; + do { + int data; + while (!(data = (p->wr < p->rd) ? p->rd - p->wr - 1 : sizeof(p->buf) - p->wr)) { + push_task(&p->q, current); + doswitch(); + } + + if (sz < data) { + data = sz; + } + memcpy(p->buf + p->wr, wrbuf, data); + p->wr = (p->wr + data) % sizeof(p->buf); + wrbuf += data; + sz -= data; + struct task *t = pop_task(&p->q); + if (t) { + policy_run(t); + } + } while (sz && !p->rdclose); + return wrbuf - buf; +} + +static int pipe_close(int fd) { + struct file *f = current->fd[fd]; + + bool read; + struct pipe *p = fd2pipe(fd, &read); + if (read) { + p->rdclose = 1; + } else { + p->wrclose = 1; + } + + struct task *t; + while ((t = pop_task(&p->q))) { + policy_run(t); + } + + if (p->rdclose && p->wrclose) { + pool_free(&pipepool, p); + } +} + +static const struct fileops pipe_rd_ops = { + .read = pipe_read, + .close = pipe_close, +}; +static const struct fileops pipe_wr_ops = { + .write = pipe_write, + .close = pipe_close, +}; + +static void init_file(struct file *f, const struct fileops *ops) { + f->ops = ops; + f->usecnt = 0; +} + +int sys_pipe(int *pipe) { + struct pipe *p = pool_alloc(&pipepool); + if (!p) { + goto err; + } + + int fdr = find_fd(0); + if (fdr < 0) { + goto err_clean; + } + int fdw = find_fd(fdr + 1); + if (fdw < 0) { + goto err_clean; + } + + p->rd = p->wr = 0; + p->q = NULL; + + init_file(&p->rdend, &pipe_rd_ops); + init_file(&p->wrend, &pipe_wr_ops); + + set_fd(current, fdr, &p->rdend); + set_fd(current, fdw, &p->wrend); + + pipe[0] = fdr; + pipe[1] = fdw; + return 0; + +err_clean: + pool_free(&pipepool, p); +err: + return -1; +} + +static int fd_term_read(int fd, void *buf, unsigned sz) { + return read(0, buf, sz); +} + +static int fd_term_write(int fd, const void *buf, unsigned sz) { + return write(1, buf, sz); +} + +int main(int argc, char *argv[]) { + struct sigaction act = { + .sa_sigaction = top, + .sa_flags = SA_RESTART, + }; + sigemptyset(&act.sa_mask); + + if (-1 == sigaction(SIGSEGV, &act, NULL)) { + perror("signal set failed"); + return 1; + } + + memfd = memfd_create("mem", 0); + if (memfd < 0) { + perror("memfd_create"); + return 1; + } + + if (ftruncate(memfd, PAGE_SIZE * MEM_PAGES) < 0) { + perror("ftrucate"); + return 1; + } + + struct stat st; + if (stat("rootfs.cpio", &st)) { + perror("stat rootfs"); + return 1; + } + + int fd = open("rootfs.cpio", O_RDONLY); + if (fd < 0) { + perror("open rootfs"); + return 1; + } + + rootfs_sz = st.st_size; + rootfs = mmap(NULL, rootfs_sz, PROT_READ, MAP_PRIVATE, fd, 0); + if (rootfs == MAP_FAILED) { + perror("mmap rootfs"); + return 1; + } + + policy_cmp = prio_cmp; + struct task *t = sched_new(inittramp, NULL, 0, STANDARD); + vmctx_make(&t->vm, 4 * PAGE_SIZE); + + struct file term; + struct fileops termops = { + .read = fd_term_read, + .write = fd_term_write, + }; + init_file(&term, &termops); + set_fd(t, 0, &term); + set_fd(t, 1, &term); + set_fd(t, 2, &term); + + policy_run(t); + sched_run(); +} diff --git a/sched.h b/sched.h new file mode 100644 index 0000000..96a80f0 --- /dev/null +++ b/sched.h @@ -0,0 +1,12 @@ +#pragma once + +// Pause execution for specified amount of msec +extern void sched_sleep(unsigned ms); + +// milliseconds since system start +extern long sched_gettime(void); + +// DANGEROUS: only for tests +extern void irq_disable(void); +extern void irq_enable(void); + diff --git a/seq.app.c b/seq.app.c new file mode 100644 index 0000000..b692128 --- /dev/null +++ b/seq.app.c @@ -0,0 +1,38 @@ +#include "usyscall.h" + + +int atoi(const char *str) { + int v = 0; + while (*str) { + v = v * 10 + (*str++ - '0'); + } + return v; +} + +int itoa(int v, char *d) { + char *p = d; + while (v) { + *p++ = (v % 10) + '0'; + v /= 10; + } + int len = p - d; + + --p; + while (d < p) { + char t = *d; + *d++ = *p; + *p-- = t; + } + return len; +} + +int main(int argc, char* argv[]) { + int n = atoi(argv[1]); + char buf[32]; + for (int i = 1; i <= n; ++i) { + int l = itoa(i, buf); + buf[l] = '\n'; + os_write(1, buf, l + 1); + } + os_exit(0); +} diff --git a/syscall.c b/syscall.c new file mode 100644 index 0000000..587c2d8 --- /dev/null +++ b/syscall.c @@ -0,0 +1,63 @@ + +#include "syscall.h" +#include "usyscall.h" + +#include +#include + +typedef unsigned long (*sys_call_t)( + unsigned long arg1, unsigned long arg2, + unsigned long arg3, unsigned long arg4, + void *rest); + +#define SC_TRAMPOLINE0(ret, name) \ + static unsigned long sys_tr_ ## name(unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, void *rest) { \ + return (ret) sys_ ## name(); \ + } +#define SC_TRAMPOLINE1(ret, name, type1, name1) \ + static unsigned long sys_tr_ ## name(unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, void *rest) { \ + return (ret) sys_ ## name((type1)arg1); \ + } +#define SC_TRAMPOLINE2(ret, name, type1, name1, type2, name2) \ + static unsigned long sys_tr_ ## name(unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, void *rest) { \ + return (ret) sys_ ## name((type1)arg1, (type2)arg2); \ + } +#define SC_TRAMPOLINE3(ret, name, type1, name1, type2, name2, type3, name3) \ + static unsigned long sys_tr_ ## name(unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, void *rest) { \ + return (ret) sys_ ## name((type1)arg1, (type2)arg2, (type3)arg3); \ + } +#define SC_TRAMPOLINE4(ret, name, type1, name1, type2, name2, type3, name3, type4, name4) \ + static unsigned long sys_tr_ ## name(unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, void *rest) { \ + return (ret) sys_ ## name((type1)arg1, (type2)arg2, (type3)arg3, (type4)arg4); \ + } +#define SC_TRAMPOLINE5(ret, name, type1, name1, type2, name2, type3, name3, type4, name4, type5, name5) \ + static unsigned long sys_tr_ ## name(unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4, void *rest) { \ + return (ret) sys_ ## name((type1)arg1, (type2)arg2, (type3)arg3, (type4)arg4, rest); \ + } +#define SC_TRAMPOLINE(name, ret, n, ...) \ + SC_TRAMPOLINE ## n (ret, name, ## __VA_ARGS__) +SYSCALL_X(SC_TRAMPOLINE) +#undef SC_TRAMPOLINE0 +#undef SC_TRAMPOLINE1 +#undef SC_TRAMPOLINE2 +#undef SC_TRAMPOLINE3 +#undef SC_TRAMPOLINE4 +#undef SC_TRAMPOLINE5 +#undef SC_TRAMPOLINE + +#define SC_TABLE_ITEM(name, ...) sys_tr_## name, +static const sys_call_t sys_table[] = { + SYSCALL_X(SC_TABLE_ITEM) +}; +#undef SC_TABLE_ITEM + +unsigned long syscall_do(int sysnum, + unsigned long arg1, unsigned long arg2, + unsigned long arg3, unsigned long arg4, + void *rest) { + return sys_table[sysnum](arg1, arg2, arg3, arg4, rest); +} + +int sys_fork(void) { + abort(); +} diff --git a/syscall.h b/syscall.h new file mode 100644 index 0000000..7e6a7c4 --- /dev/null +++ b/syscall.h @@ -0,0 +1,31 @@ +#pragma once + +#include "usyscall.h" + +extern unsigned long syscall_do(int sysnum, + unsigned long arg1, unsigned long arg2, + unsigned long arg3, unsigned long arg4, + void *rest); + +#define SC_DECLARE0(ret, name) \ + ret sys_ ## name(void); +#define SC_DECLARE1(ret, name, type1, name1) \ + ret sys_ ## name(type1); +#define SC_DECLARE2(ret, name, type1, name1, type2, name2) \ + ret sys_ ## name(type1, type2); +#define SC_DECLARE3(ret, name, type1, name1, type2, name2, type3, name3) \ + ret sys_ ## name(type1, type2, type3); +#define SC_DECLARE4(ret, name, type1, name1, type2, name2, type3, name3, type4, name4) \ + ret sys_ ## name(type1, type2, type3, type4); +#define SC_DECLARE5(ret, name, type1, name1, type2, name2, type3, name3, type4, name4, type5, name5) \ + ret sys_ ## name(type1, type2, type3, type4, void*); +#define SC_DECLARE(name, ret, n, ...) \ + SC_DECLARE ## n (ret, name, ## __VA_ARGS__) +SYSCALL_X(SC_DECLARE) +#undef SC_DECLARE0 +#undef SC_DECLARE1 +#undef SC_DECLARE2 +#undef SC_DECLARE3 +#undef SC_DECLARE4 +#undef SC_DECLARE5 +#undef SC_DECLARE diff --git a/test/batch/1.in b/test/batch/1.in deleted file mode 100644 index 286ae4a..0000000 --- a/test/batch/1.in +++ /dev/null @@ -1,9 +0,0 @@ - -echo test1 -retcode - -echo test2 ; -retcode - -echo test3;echo test4 test5;;;retcode - diff --git a/test/batch/1.out b/test/batch/1.out deleted file mode 100644 index c98a022..0000000 --- a/test/batch/1.out +++ /dev/null @@ -1,7 +0,0 @@ -test1 -1 -test2 -1 -test3 -test4 test5 -2 diff --git a/test/batch/run-test.sh b/test/batch/run-test.sh deleted file mode 100755 index 145f39a..0000000 --- a/test/batch/run-test.sh +++ /dev/null @@ -1,2 +0,0 @@ - -map_inputs checkdiff diff --git a/test/exec/1.out b/test/exec/1.out new file mode 100644 index 0000000..0db9302 --- /dev/null +++ b/test/exec/1.out @@ -0,0 +1,20 @@ +start +2 +12 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +32 +42 +52 +62 +72 +82 +92 diff --git a/test/exec/run-test.sh b/test/exec/run-test.sh new file mode 100755 index 0000000..6959a54 --- /dev/null +++ b/test/exec/run-test.sh @@ -0,0 +1 @@ +$MAIN $B/../init.app | diffout 1 diff --git a/test/multithread/1.fn b/test/multithread/1.fn new file mode 100644 index 0000000..ba8ef31 --- /dev/null +++ b/test/multithread/1.fn @@ -0,0 +1,57 @@ +#!/usr/bin/awk -f + +1 # echo everything + +BEGIN { + pid1 = 0 + pid2 = 0 + pid3 = 0 + pid0 = 0 + ecode = 0 +} + +function error(msg) { + print msg + ecode = 1 +} + +$1 == 0 { + pid0 = pid0 + 1 +} + +$1 == 1 { + pid1 = pid1 + 1 +} + +$1 == 2 { + pid2 = pid2 + 1 +} + +$1 == 3 { + pid3 = pid3 + 1 +} + +END { + sum = pid0 + pid1 + pid2 + pid3 + r0 = pid0 / sum + r1 = pid1 / sum + r2 = pid2 / sum + r3 = pid3 / sum + print pid0, pid1, pid2, pid3 + print r0, r1, r2, r3 + d = 0.08 + if (r0 < 0.25 - d || 0.25 + d < r0) { + ecode = 1 + } + if (r1 < 0.25 - d || 0.25 + d < r1) { + ecode = 1 + } + if (r2 < 0.25 - d || 0.25 + d < r2) { + ecode = 1 + } + if (r3 < 0.25 - d || 0.25 + d < r3) { + ecode = 1 + } + + exit(ecode) +} diff --git a/test/multithread/1.in b/test/multithread/1.in new file mode 100644 index 0000000..e69de29 diff --git a/test/multithread/run-test.sh b/test/multithread/run-test.sh new file mode 100755 index 0000000..939828b --- /dev/null +++ b/test/multithread/run-test.sh @@ -0,0 +1,2 @@ +cd $B/.. +timeout 6s ./main | awk -f ./test/multithread/1.fn \ No newline at end of file diff --git a/test/pool/1.fn b/test/pool/1.fn deleted file mode 100755 index aded2fb..0000000 --- a/test/pool/1.fn +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/awk -f - -1 # echo everything - -BEGIN { - NOBJ = 4 - ecode = 0 -} - -function error(msg) { - print msg - ecode = 1 -} - -$1 == "alloc" && $2 == -1 { - for (i = 0; i < NOBJ; ++i) - if (!o[i]) { - error("cannot alloc but " i " is free") - break; - } -} - -$1 == "alloc" && 0 <= $2 { - if (o[$2]) - error($2 " already allocated!") - o[$2] = 1 -} - -$1 == "free" { - if (!o[$2]) - error($2 " should be allocated at this point") - o[$2] = 0 - -} - -END { - exit(ecode) -} - diff --git a/test/pool/1.in b/test/pool/1.in deleted file mode 100644 index 7505cf6..0000000 --- a/test/pool/1.in +++ /dev/null @@ -1,27 +0,0 @@ -pooltest alloc -pooltest alloc -pooltest alloc -pooltest alloc - -pooltest alloc -pooltest alloc - -pooltest free 0 -pooltest alloc -pooltest free 0 -pooltest alloc -pooltest free 0 -pooltest alloc - -pooltest free 0 -pooltest free 1 -pooltest free 2 -pooltest free 3 - -pooltest alloc -pooltest alloc -pooltest alloc -pooltest alloc - -pooltest alloc -pooltest alloc diff --git a/test/pool/run-test.sh b/test/pool/run-test.sh deleted file mode 100644 index f471660..0000000 --- a/test/pool/run-test.sh +++ /dev/null @@ -1,2 +0,0 @@ - -map_inputs checkfn diff --git a/test/run.sh b/test/run.sh index 91fbe95..8a2c20b 100755 --- a/test/run.sh +++ b/test/run.sh @@ -1,7 +1,11 @@ #!/bin/bash B=$(readlink -f $(dirname $0)) -MAIN=$B/../main +MAIN=runmain + +runmain() { + ( cd $B/.. ; ./main ) +} map() { local c="$1" i r @@ -43,6 +47,11 @@ checkdifftimeout() { checkpipe 124 0 } +checkfntimeout() { + < $1.in timeout 10 $MAIN | ./$1.fn + checkpipe 124 0 +} + runtest() { local r cd $B/$1 diff --git a/timer.c b/timer.c new file mode 100644 index 0000000..0070dde --- /dev/null +++ b/timer.c @@ -0,0 +1,46 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#include "timer.h" + +static struct timeval initv; + +int timer_cnt(void) { + struct itimerval it; + getitimer(ITIMER_REAL, &it); + return 1000000 * (initv.tv_sec - it.it_value.tv_sec) + + (initv.tv_usec - it.it_value.tv_usec); +} + +void timer_init(int ms, void (*hnd)(int sig, siginfo_t *info, void *ctx)) { + + initv.tv_sec = ms / 1000; + initv.tv_usec = (ms % 1000) * 1000; + + const struct itimerval setup_it = { + .it_value = initv, + .it_interval = initv, + }; + + if (-1 == setitimer(ITIMER_REAL, &setup_it, NULL)) { + perror("setitimer"); + } + + struct sigaction act = { + .sa_sigaction = hnd, + .sa_flags = SA_RESTART, + }; + sigemptyset(&act.sa_mask); + + if (-1 == sigaction(SIGALRM, &act, NULL)) { + perror("signal set failed"); + exit(1); + } +} diff --git a/timer.h b/timer.h new file mode 100644 index 0000000..0a25236 --- /dev/null +++ b/timer.h @@ -0,0 +1,8 @@ +#pragma once + +// returns number of microseconds (usec) since the latests interrupt +extern int timer_cnt(void); + +extern void timer_init(int ms, void (*hnd)(int sig, siginfo_t *info, void *ctx)); + + diff --git a/tramp.S b/tramp.S new file mode 100644 index 0000000..96e345b --- /dev/null +++ b/tramp.S @@ -0,0 +1,50 @@ + +.text +.global tramptramp, exittramp + +tramptramp: + pushfq + push %rax + push %rbx + push %rcx + push %rdx + push %rsi + push %rdi + push %r8 + push %r9 + push %r10 + push %r11 + push %r12 + push %r13 + push %r14 + push %r15 + push %rbp + /* FIXME vfp, xmm ...*/ + + mov %rsp, %rdi + mov 18*8(%rsp), %rsi + mov 17*8(%rsp), %rsp + call *16*8(%rdi) + mov %rax, %rsp + +exittramp: + + pop %rbp + pop %r15 + pop %r14 + pop %r13 + pop %r12 + pop %r11 + pop %r10 + pop %r9 + pop %r8 + pop %rdi + pop %rsi + pop %rdx + pop %rcx + pop %rbx + pop %rax + popfq + + mov 3*8(%rsp), %rsp + jmp *-(128/*red_zone*/ + 8/*rip push*/)(%rsp) diff --git a/usyscall.h b/usyscall.h new file mode 100644 index 0000000..6233208 --- /dev/null +++ b/usyscall.h @@ -0,0 +1,75 @@ +#pragma once + +#define SYSCALL_X(x) \ + x(read, int, 3, int, fd, void *, buf, unsigned, len) \ + x(write, int, 3, int, fd, const void *, buf, unsigned, len) \ + x(close, int, 1, int, fd) \ + x(dup, int, 1, int, fd) \ + x(pipe, int, 1, int *, pipe) \ + x(fork, int, 0) \ + x(exit, int, 1, int, code) \ + x(exec, int, 2, const char *, name, char **, argv) \ + +#define SC_NR(name, ...) os_syscall_nr_ ## name, +enum syscalls_num { + SYSCALL_X(SC_NR) +}; +#undef SC_NR + +static inline long os_syscall(int syscall, + unsigned long arg1, unsigned long arg2, + unsigned long arg3, unsigned long arg4, + void *rest) { + long ret; + __asm__ __volatile__( + "int $0x81\n" + : "=a"(ret) + : "a"(syscall), // rax + "b"(arg1), // rbx + "c"(arg2), // rcx + "d"(arg3), // rdx + "S"(arg4), // rsi + "D"(rest) // rdi + : + ); + return ret; +} + +#define DEFINE0(ret, name) \ + static inline ret os_ ## name (void) { \ + return (ret) os_syscall(os_syscall_nr_ ## name, 0, 0, 0, 0, (void *) 0); \ + } +#define DEFINE1(ret, name, type1, name1) \ + static inline ret os_ ## name (type1 name1) { \ + return (ret) os_syscall(os_syscall_nr_ ## name, (unsigned long) name1, 0, 0, 0, (void *) 0); \ + } +#define DEFINE2(ret, name, type1, name1, type2, name2) \ + static inline ret os_ ## name (type1 name1, type2 name2) { \ + return (ret) os_syscall(os_syscall_nr_ ## name, (unsigned long) name1, (unsigned long) name2, 0, 0, (void *) 0); \ + } +#define DEFINE3(ret, name, type1, name1, type2, name2, type3, name3) \ + static inline ret os_ ## name (type1 name1, type2 name2, type3 name3) { \ + return (ret) os_syscall(os_syscall_nr_ ## name, (unsigned long) name1, (unsigned long) name2, \ + (unsigned long) name3, 0, (void *) 0); \ + } +#define DEFINE4(ret, name, type1, name1, type2, name2, type3, name3, type4, name4) \ + static inline ret os_ ## name (type1 name1, type2 name2, type3 name3, type4 name4) { \ + return (ret) os_syscall(os_syscall_nr_ ## name, (unsigned long) name1, (unsigned long) name2, \ + (unsigned long) name3, (unsigned long) name4, (void *) 0); \ + } +#define DEFINE5(ret, name, type1, name1, type2, name2, type3, name3, type4, name4, type5, name5) \ + static inline ret os_ ## name (type1 name1, type2 name2, type3 name3, type4 name4) { \ + return (ret) os_syscall(os_syscall_nr_ ## name, (unsigned long) name1, (unsigned long) name2, \ + (unsigned long) name3, (unsigned long) name4, (void *) name5); \ + } +#define DEFINE(name, ret, n, ...) \ + DEFINE ## n (ret, name, ## __VA_ARGS__) +SYSCALL_X(DEFINE) +#undef DEFINE0 +#undef DEFINE1 +#undef DEFINE2 +#undef DEFINE3 +#undef DEFINE4 +#undef DEFINE5 +#undef DEFINE +