From e818cab3f1fc4a3d4d0b8e2c9424fb2e7b1dff70 Mon Sep 17 00:00:00 2001 From: dzwdz Date: Fri, 2 Aug 2024 01:20:40 +0200 Subject: *: use a generic UserRegs type everywhere I'm storing registers mostly inspired by Plan 9's Ureg, probably obvious from the name --- src/kernel/arch/amd64/regs.c | 26 ++++++++++++++++++++++++++ src/kernel/arch/amd64/sysenter.c | 13 ++++++++----- src/kernel/arch/amd64/sysenter.h | 11 ++++++++--- src/kernel/arch/generic.h | 7 +++++-- src/kernel/proc.c | 23 ++--------------------- src/kernel/proc.h | 3 ++- src/kernel/syscalls.c | 33 +-------------------------------- src/libk/include/camellia/types.h | 11 ++++++----- 8 files changed, 58 insertions(+), 69 deletions(-) create mode 100644 src/kernel/arch/amd64/regs.c diff --git a/src/kernel/arch/amd64/regs.c b/src/kernel/arch/amd64/regs.c new file mode 100644 index 0000000..bb4e5aa --- /dev/null +++ b/src/kernel/arch/amd64/regs.c @@ -0,0 +1,26 @@ +#include +#include + +void +regs_safecopy(UserRegs *dst, const UserRegs *src) +{ + /* preserved flags: (see intel vol 1 3.4.3.) + * 1<<0 = CF, carry flag + * 1<<2 = PF, parity flag + * 1<<4 = AC, auxilary carry + * 1<<6 = ZF, zero flag + * 1<<7 = SF, sign flag + * 1<<11 = OF, overflow flag + * 1<<10 = DF, direction flag + */ + static_assert( + ((1<<0) | (1<<2) | (1<<4) | (1<<6) | (1<<7) | (1<<10) | (1<<11)) + == 0xCD5 + ); + + uint64_t newflags = (src->flags & 0xCD5) | (dst->flags & ~0xCD5); + /* I "spelled it out" directly instead of using memcpy hoping that gcc + * maybe would notice it can skip copying the last 8 bytes. It didn't :( */ + *dst = *src; + dst->flags = newflags; +} diff --git a/src/kernel/arch/amd64/sysenter.c b/src/kernel/arch/amd64/sysenter.c index 5a96e33..2f83e3d 100644 --- a/src/kernel/arch/amd64/sysenter.c +++ b/src/kernel/arch/amd64/sysenter.c @@ -2,17 +2,20 @@ #include #include #include +#include -CpuRegs _sysexit_regs; +SysexitRegs _sysexit_regs; -_Noreturn void sysexit(CpuRegs regs) { - _sysexit_regs = regs; +_Noreturn void sysexit(UserRegs *regs, char sse[512]) { + _sysexit_regs.u = *regs; + memcpy(_sysexit_regs.sse, sse, 512); _sysexit_real(); } _Noreturn void sysenter_stage2(void) { - CpuRegs *regs = &proc_cur->regs; - *regs = _sysexit_regs; + UserRegs *regs = &proc_cur->regs; + *regs = _sysexit_regs.u; + memcpy(proc_cur->sse, _sysexit_regs.sse, 512); _syscall(regs->rdi, regs->rsi, regs->rdx, regs->r10, regs->r8, regs->r9); proc_switch_any(); } diff --git a/src/kernel/arch/amd64/sysenter.h b/src/kernel/arch/amd64/sysenter.h index 03a9f45..65c46f8 100644 --- a/src/kernel/arch/amd64/sysenter.h +++ b/src/kernel/arch/amd64/sysenter.h @@ -1,9 +1,14 @@ #pragma once #include -// sysenter.c -extern CpuRegs _sysexit_regs; +typedef struct SysexitRegs SysexitRegs; +struct SysexitRegs { + UserRegs u; + char sse[512]; +}; +_Static_assert(sizeof(SysexitRegs) == 18 * 8 + 512); + +extern SysexitRegs _sysexit_regs; _Noreturn void sysenter_stage2(void); -// sysenter.s _Noreturn void _sysexit_real(void); diff --git a/src/kernel/arch/generic.h b/src/kernel/arch/generic.h index a6a14c9..c9431eb 100644 --- a/src/kernel/arch/generic.h +++ b/src/kernel/arch/generic.h @@ -27,8 +27,7 @@ uint64_t uptime_ns(void); void timer_schedule(Proc *p, uint64_t ns); void timer_deschedule(Proc *p); -// src/arch/i386/sysenter.s -_Noreturn void sysexit(CpuRegs); +_Noreturn void sysexit(UserRegs *, char sse[512]); // all of those can allocate memory Pagedir *pagedir_new(void); @@ -52,3 +51,7 @@ int kprintf(const char *fmt, ...); void *debug_caller(size_t depth); void debug_stacktrace(void); + +/** "Applies" an UserRegs struct received from userland over another UserRegs + * without overriding privileged registers. */ +void regs_safecopy(UserRegs *, const UserRegs *); diff --git a/src/kernel/proc.c b/src/kernel/proc.c index d85384f..abe0d04 100644 --- a/src/kernel/proc.c +++ b/src/kernel/proc.c @@ -393,27 +393,8 @@ static void proc_intr_deliver(Proc *p) { } /* save old registers */ - // TODO separate types for normal registers and sse? - // then i could reuse the normal register type here struct intr_data d; - d.r15 = p->regs.r15; - d.r14 = p->regs.r14; - d.r13 = p->regs.r13; - d.r12 = p->regs.r12; - d.r11 = p->regs.r11; - d.r10 = p->regs.r10; - d.r9 = p->regs.r9; - d.r8 = p->regs.r8; - d.rdi = p->regs.rdi; - d.rsi = p->regs.rsi; - d.rbp = p->regs.rbp; - d.rsp = p->regs.rsp; - d.rbx = p->regs.rbx; - d.rdx = p->regs.rdx; - d.rcx = p->regs.rcx; - d.rax = p->regs.rax; - d.rip = p->regs.rip; - d.flags = p->regs.flags; + d.reg = p->regs; // one more because the buffer itself isn't NUL terminated // TODO ensure NUL termination @@ -486,7 +467,7 @@ _Noreturn void proc_switch_any(void) { if (p->execbuf.buf) { execbuf_run(p); } else { - sysexit(p->regs); + sysexit(&p->regs, p->sse); } } else { cpu_pause(); diff --git a/src/kernel/proc.h b/src/kernel/proc.h index a371e04..bd4c961 100644 --- a/src/kernel/proc.h +++ b/src/kernel/proc.h @@ -34,7 +34,8 @@ struct Proc { /* if NULL, refcount == 1. kmalloc'd */ uint64_t *pages_refcount; - CpuRegs regs; + UserRegs regs; + _Alignas(16) char sse[512]; Proc *sibling, *child, *parent; enum proc_state state; diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c index 42f548e..e94f886 100644 --- a/src/kernel/syscalls.c +++ b/src/kernel/syscalls.c @@ -488,43 +488,12 @@ long _sys_duplex(hid_t from, hid_t to, int flags) { } void _sys_intr_return(struct intr_data __user *intr, int flags) { - CpuRegs *r = &proc_cur->regs; struct intr_data d; if (flags != 0 || pcpy_from(proc_cur, &d, intr, sizeof(d)) < sizeof(d)) { proc_kill(proc_cur, EFAULT); proc_switch_any(); /* doesn't return */ } - r->r15 = d.r15; - r->r14 = d.r14; - r->r13 = d.r13; - r->r12 = d.r12; - r->r11 = d.r11; - r->r10 = d.r10; - r->r9 = d.r9; - r->r8 = d.r8; - r->rdi = d.rdi; - r->rsi = d.rsi; - r->rbp = d.rbp; - r->rsp = d.rsp; - r->rbx = d.rbx; - r->rdx = d.rdx; - r->rcx = d.rcx; - r->rax = d.rax; - r->rip = d.rip; - /* preserved flags: (see intel vol 1 3.4.3.) - * 1<<0 = CF, carry flag - * 1<<2 = PF, parity flag - * 1<<4 = AC, auxilary carry - * 1<<6 = ZF, zero flag - * 1<<7 = SF, sign flag - * 1<<11 = OF, overflow flag - * 1<<10 = DF, direction flag - */ - static_assert( - ((1<<0) | (1<<2) | (1<<4) | (1<<6) | (1<<7) | (1<<10) | (1<<11)) - == 0xCD5 - ); - r->flags = (d.flags & 0xCD5) | (r->flags & ~0xCD5); + regs_safecopy(&proc_cur->regs, &d.reg); } long _sys_execbuf(void __user *ubuf, size_t len) { diff --git a/src/libk/include/camellia/types.h b/src/libk/include/camellia/types.h index 6fee578..0790db3 100644 --- a/src/libk/include/camellia/types.h +++ b/src/libk/include/camellia/types.h @@ -32,15 +32,16 @@ struct ufs_request { int flags; }; -struct intr_data { - /* same order as CpuRegs. - * notable omissions: - * - SSE registers (usually there's no need to save them, userland can do - * that itself if it wants) */ +typedef struct UserRegs UserRegs; +struct UserRegs { uint64_t r15, r14, r13, r12, r11, r10, r9, r8; uint64_t rdi, rsi, rbp, rsp, rbx, rdx, rcx, rax; uint64_t rip, flags; +}; +_Static_assert(sizeof(UserRegs) == 8 * 18); /* for asm */ +struct intr_data { + UserRegs reg; char msg[]; /* variable size, NUL terminated */ }; -- cgit v1.2.3