Zur Zeit habe ich syscalls (software interrupt 0x79) und switch_to_user_mode() implementiert. Der Ablauf funktioniert bisher nur im Kernel Mode (Privileg 0).
Mein Aufbau zum Testen der beiden Modes:
in os.h:
//#define USERMODE
#define KERNELMODE
in gdt.c:
void gdt_install()
{
/* Setup the GDT pointer and limit */
gdt_register.limit = (sizeof(struct gdt_entry) * NUMBER_GDT_GATES)-1;
gdt_register.base = (ULONG) &gdt;
/* GDT GATES - desriptors with pointers to the linear memory address */
gdt_set_gate(0, 0, 0, 0, 0); // NULL descriptor
gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // CODE, privilege level 0 for kernel code
gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // DATA, privilege level 0 for kernel code
#ifdef KERNELMODE // in os.h
gdt_set_gate(3, 0, 0xFFFFFFFF, 0x9A, 0xCF); // CODE, privilege level 0 for kernel code
gdt_set_gate(4, 0, 0xFFFFFFFF, 0x92, 0xCF); // DATA, privilege level 0 for kernel code
#endif
#ifdef USERMODE // in os.h
//gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); // User mode code segment
//gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); // User mode data segment
#endif
write_tss(5, 0x10, 0x0); // num, ss0, esp0
gdt_flush((ULONG)&gdt_register); // inclusive gdt_load() in assembler code
tss_flush(); //in flush.asm: privilege level 0 for kernel mode 0x28
in task.c:
void switch_to_user_mode()
{
set_kernel_stack(current_task->kernel_stack+KERNEL_STACK_SIZE); //tss_entry.esp0 = ...;
// Set up a stack structure for switching to user mode (ring 3 => RPL = 0x3)
// 0x200 is IF flag (enabling interrupts)
// 0x20|0x3=0x23 // 0x18|0x3=0x1B
// $1F means "the address of the next label '1:', searching forward"
#ifdef USERMODE // in os.h
asm volatile(" \
cli; \
mov $0x23, %ax; \
mov %ax, %ds; \
mov %ax, %es; \
mov %ax, %fs; \
mov %ax, %gs; \
\
mov %esp, %eax; \
pushl $0x23; \
pushl %esp; \
pushf; \
pop %eax; \
or $0x200, %eax; \
push %eax; \
pushl $0x1B; \
push $1f; \
iret; \
1: \
");
#endif
#ifdef KERNELMODE // in os.h
asm volatile(" \
cli; \
mov $0x20, %ax; \
mov %ax, %ds; \
mov %ax, %es; \
mov %ax, %fs; \
mov %ax, %gs; \
\
mov %esp, %eax; \
pushl $0x20; \
pushl %esp; \
pushf; \
pop %eax; \
or $0x200, %eax; \
push %eax; \
pushl $0x18; \
push $1f; \
iret; \
1: \
");
#endif
}
in flush.asm:
_tss_flush:
mov ax, 0x2B ; Load the index of our TSS structure - The index is
; 0x28, as it is the 5th selector and each is 8 bytes
; long, but we set the bottom two bits (making 0x2B)
; so that it has an RPL of 3, not zero.
ltr ax ; Load 0x2B into the task state register.
ret
Problem:
Wenn ich in main() switch_to_user_mode()
aufrufe, endet das Programm sofort mit einem GPF. Das nachfolgende syscall_puts("Hello, user world!\n");
wird nicht mehr erreicht.
Fragen:
Ob ich in tss_flush auf 0x2B oder 0x28 (RPL 3 oder 0) setze, spielt keine Rolle. Könnt ihr zur Bedeutung des Privilegs im TSS bitte etwas erläutern?
Was muss ich ändern, damit syscall_puts(...) erreicht und ausgeführt wird?
Was muss man in create_task(...) beachten, damit eine Task im User Mode lauffähig ist?