Skip to content

Commit

Permalink
Save and restore es, fs, gs segments in interrupts and context switch.
Browse files Browse the repository at this point in the history
Remove workaround from sysstat for es register.
  • Loading branch information
dthain committed Dec 15, 2023
1 parent d7edf04 commit a92d816
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 25 deletions.
31 changes: 21 additions & 10 deletions kernel/kernelcore.S
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,12 @@ outb %al, $0x92
mov %eax, %cr0 # store the status word
# (we are now in protected mode)
mov $2*8, %ax # selector two is flat 4GB data data
mov %ax, %ds # set stack and data segments to selector two
mov %ax, %ds # set data, extra, and stack segments to selector two
mov %ax, %es
mov %ax, %ss
mov $5*8, %ax # set TSS to selector five
ltr %ax
mov $0, %ax # unused segments are nulled out
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov $INTERRUPT_STACK_TOP, %sp # set up initial C stack
Expand Down Expand Up @@ -404,34 +404,42 @@ intr47: pushl $0 ; pushl $47 ; jmp intr_handler
intr48: pushl $0 ; pushl $48 ; jmp intr_syscall

intr_handler:
pushl %ds
pushl %ebp # push regs
pushl %ds # push segment registers
pushl %es
pushl %fs
pushl %gs
pushl %ebp # push general regs
pushl %edi
pushl %esi
pushl %edx
pushl %ecx
pushl %ebx
pushl %eax
pushl 36(%esp) # push interrupt code
pushl 36(%esp) # push interrupt number
movl $2*8, %eax # switch to kernel data seg
pushl 48(%esp) # push interrupt code from above
pushl 48(%esp) # push interrupt number from above
movl $2*8, %eax # switch to kernel data seg and extra seg
movl %eax, %ds
movl %eax, %es
call interrupt_handler
addl $4, %esp # remove interrupt number
addl $4, %esp # remove interrupt code
jmp intr_return

intr_syscall:
pushl %ds
pushl %ebp # push regs
pushl %ds # push segment registers
pushl %es
pushl %fs
pushl %gs
pushl %ebp # push general regs
pushl %edi
pushl %esi
pushl %edx
pushl %ecx
pushl %ebx
pushl %eax # note these *are* the syscall args
movl $2*8, %eax # switch to kernel data seg
movl $2*8, %eax # switch to kernel data seg and extra seg
movl %eax, %ds
movl %eax, %es
call syscall_handler
addl $4, %esp # remove the old eax
jmp syscall_return
Expand All @@ -446,6 +454,9 @@ syscall_return:
popl %esi
popl %edi
popl %ebp
popl %gs
popl %fs
popl %es
popl %ds
addl $4, %esp # remove interrupt num
addl $4, %esp # remove detail code
Expand Down
3 changes: 3 additions & 0 deletions kernel/process.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ void process_kstack_reset(struct process *p, unsigned entry_point)
s->regs2.ebp = (uint32_t) (p->kstack_ptr + 28);
s->old_ebp = (uint32_t) (p->kstack_ptr + 32);
s->old_eip = (unsigned) intr_return;
s->fs = 0;
s->gs = 0;
s->es = X86_SEGMENT_USER_DATA;
s->ds = X86_SEGMENT_USER_DATA;
s->cs = X86_SEGMENT_USER_CODE;
s->eip = entry_point;
Expand Down
8 changes: 8 additions & 0 deletions kernel/x86.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,21 @@ struct x86_regs {
};

struct x86_stack {
/* Registers saved by process_switch */
struct x86_regs regs2;
/* Stack frame of process_switch */
int32_t old_ebp;
int32_t old_eip;
/* Pushed by intr_handler in kernelcore */
struct x86_regs regs1;
int32_t gs;
int32_t fs;
int32_t es;
int32_t ds;
/* Pushed by intrXX in kernelcore. */
int32_t intr_num;
int32_t intr_code;
/* Pushed by X86 CPU Hardware. */
int32_t eip;
int32_t cs;
struct x86_eflags eflags;
Expand Down
15 changes: 0 additions & 15 deletions user/sysstat.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,6 @@ See the file LICENSE for details.

int main(int argc, char *argv[])
{
/*
This demonstrates (and fixes) a kernel bug.
GCC implements the initialization of the structure on the stack like this:
rep stos %eax,%es:(%edi)
However, basekernel does not automatically set up (or save) the es segment register,
and so the operation crashes. The es register should be set up correctly in kernelcore,
saved and restored when processing interrupts/system calls, and also initialized correctly
using process_kstack_init.
*/

/* The workaround here is to explicitly set up the es register prior to using it.*/

asm("mov %ds, %ax");
asm("mov %ax, %es");

struct system_stats s = {0};

if (syscall_system_stats(&s)) {
Expand Down

0 comments on commit a92d816

Please sign in to comment.