Reputation: 11
I am creating a 32 bit operating system. I have enabled paging and it works fine, until the kernel_main function returns to the assembly code (which calls the kernel_main function). The assembly code is like this:
call kernel_main
jmp $
But when bochs runs the jmp $
instruction, it triple faults with a GPF. Apparently, the code section in the GDT got corrupted.
Bochs output:
interrupt(): not accessible or not code segment cs=0x0008
interrupt(): not accessible or not code segment cs=0x0008
interrupt(): not accessible or not code segment cs=0x0008
CPU is in protected mode (active)
CS.mode = 32 bit
SS.mode = 32 bit
EFER = 0x00000000
| EAX=00000001 EBX=00007d76 ECX=00000014 EDX=00000000
| ESP=0008fff8 EBP=00090000 ESI=000e0000 EDI=0000ffac
| IOPL=0 id vip vif ac vm RF nt of df IF tf sf zf af PF cf
| SEG sltr(index|ti|rpl) base limit G D
| CS:0008( 0001| 0| 0) 00000000 ffffffff 1 1
| DS:00010 0002| 0| 0) 00000000 ffffffff 1 1
| SS:00010 0002| 0| 0) 00000000 ffffffff 1 1
| ES:00010 0002| 0| 0) 00000000 ffffffff 1 1
| FS:00010 0002| 0| 0) 00000000 ffffffff 1 1
| GS:00010 0002| 0| 0) 00000000 ffffffff 1 1
| EIP=00001021 (00001021)
| CR0=0xe0000011 CR2=0x00000000
| CR3=0x00006000 CR4=0x00000000
0008:0000000000001021 (unk. ctxt): jmp .-2 (0x00001021) ; ebfe
exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
bx_pc_system_c::Reset(HARDWARE) called
cpu hardware reset
I put a memory watchpoint using the bochs debugger, and found out that the code segment in the GDT gets corrupted at least once. Using the bochs debugger, I found the corrupting instruction:
mov ax, DATA_SEG
DATA_SEG being the corresponding entry in the GDT. It's value is 0x10. I want to understand, how does setting a register change a value somewhere else completely?
Thanks for the help!
Some more info I thought would help.
The instruction before mov ax, DATA_SEG
is jmp CODE_SEG:init_pm
. init_pm
is the label right before mov ax, DATA_SEG
. The full code of that file is below:
[bits 16]
switch_to_pm:
cli ; 1. disable interrupts
lgdt [gdt_descriptor] ; 2. load the GDT descriptor
mov eax, cr0
or eax, 0x1 ; 3. set 32-bit mode bit in cr0
mov cr0, eax
jmp CODE_SEG:init_pm ; 4. far jump by using a different segment
[bits 32]
init_pm: ; we are now using 32-bit instructions
mov ax, DATA_SEG ; 5. update the segment registers
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x90000 ; 6. update the stack right at the top of the free space
mov esp, ebp
call BEGIN_PM ; 7. Call a well-known label with useful code
EDIT:
GDT definition:
gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps 0x7CCA
; the GDT starts with a null 8-byte
dd 0x0 ; 4 byte
dd 0x0 ; 4 byte
; GDT for code segment. base = 0x00000000, length = 0xfffff
; for flags, refer to os-dev.pdf document, page 36
gdt_code:
dw 0xffff ; segment length, bits 0-15
dw 0x0 ; segment base, bits 0-15
db 0x0 ; segment 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 for data segment. base and length identical to code segment
; some flags changed, again, refer to os-dev.pdf
gdt_data:
dw 0xffff
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
gdt_end: ; 0x7CE2
; GDT descriptor
gdt_descriptor:
dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size
dd gdt_start ; address (32 bit)
; define some constants for later use
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
EDIT 2:
The link to the github is here: https://github.com/aharsi1298/devOS
Upvotes: 1
Views: 101