summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordzwdz2024-08-02 01:20:40 +0200
committerdzwdz2024-08-02 01:20:40 +0200
commite818cab3f1fc4a3d4d0b8e2c9424fb2e7b1dff70 (patch)
tree5d59d2e0ea74253f5b265cdd542b74daca6dce5a /src
parent1890868af4b8cf2593a4b0831e2fc69a44ec680b (diff)
*: use a generic UserRegs type everywhere I'm storing registers
mostly inspired by Plan 9's Ureg, probably obvious from the name
Diffstat (limited to 'src')
-rw-r--r--src/kernel/arch/amd64/regs.c26
-rw-r--r--src/kernel/arch/amd64/sysenter.c13
-rw-r--r--src/kernel/arch/amd64/sysenter.h11
-rw-r--r--src/kernel/arch/generic.h7
-rw-r--r--src/kernel/proc.c23
-rw-r--r--src/kernel/proc.h3
-rw-r--r--src/kernel/syscalls.c33
-rw-r--r--src/libk/include/camellia/types.h11
8 files changed, 58 insertions, 69 deletions
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 <kernel/arch/generic.h>
+#include <kernel/panic.h>
+
+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 <kernel/arch/amd64/sysenter.h>
#include <kernel/arch/generic.h>
#include <kernel/proc.h>
+#include <shared/mem.h>
-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 <kernel/types.h>
-// 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 */
};