I am writing a toy operating system. I am trying to implement memory protection for an operating system. All I want to do is create protection for the kernel from user space programs. I want to do this purely with segmentation and not paging.
Here is the GDT:
gdt_start:
dd 0x0 ; 4 byte
dd 0x0 ; 4 byte
gdt_code:
dw 0xfff7 ; segment length, bits 0-15
dw 0x0 ; segment base, bits 0-15
db 0x0 ; seg2ment base, bits 16-23
db 10011010b ; flags (8 bits)
db 11001111b ; flags (4 bits) + segment length, bits 16-19
db 0x0 ; segment base, bits 24-31
gdt_data:
dw 0xfff7
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
U_code:
dw 0xfff7 ; segment length, bits 0-15
dw 0x0008 ; segment base, bits 0-15
db 0x0 ; seg2ment base, bits 16-23
db 11111010b ; flags (8 bits)
db 11001111b ; flags (4 bits) + segment length, bits 16-19
db 0x0 ; segment base, bits 24-31
U_data:
dw 0xfff7
dw 0x0008
db 0x0
db 11110010b
db 11001111b
db 0x0
gdt_end:
U_data and U_code are going to be the user space (ring 3). When I am in kernel space (ring 0) and try switching data segments by executing:
mov ax, 0x20
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
I get a segmentation error (13).
What am I doing wrong? Any guidance would be greatly appreciated.
You are loading
sswith a data segment whose DPL is 3, while your CPL is 0. That's not allowed. Your stack always has to be at the same privilege level as you. (Also, it wouldn't make any sense to loadsswithout loadingespat the same time.)The switch to the user's stack needs to happen simultaneously with the transition to CPL 3. The main ways to do this are:
RETForIRET. When the destination segment is less privileged, it will popss:espfrom the (old) stack as well ascs:eip.A hardware task switch, in which case
ss:espis loaded from the TSS along with all the other registers.Call gates also perform a stack switch, but you can only use them to call more privileged code, not the other way around.
Also, as noted in comments, your base and limit fields seem to be mixed up. As it stands your user segments have a base of
0x00000008and all your segments have a limit of0xffff7000. Note that "bits 0-15" are the least significant bits. So if itU_datais supposed to be the upper half of memory, then you would wantNote that this is probably still not what you want, as there may or may not be any memory there (e.g. if your machine has less than 2 GB), and there may also be I/O mapped into that region which the user should not have access to. So you'd really need to inspect the memory map to decide where to put the user's segments.