A Basic PMode Kernel with Paging
Posted: Mon Sep 06, 2004 1:26 pm
I've figured out so far that I start out in real mode in the boot sector, and load in the rest of the kernel with the BIOS routines (e.g. floppy disk, or CD-ROM if using floppy emulation), set the stack pointer to some arbitrary location (0x9000 in my case) and load the kernel at 4KB (0000:1000). I then far jump to 0000:1000.
At this point, I want to start in protected mode and enable paging. First, I must have a GDT and IDT ready. I have my GDT like this (expressed in NASM syntax):
align 8
gdt:
dd 0
dd 0
; Kernel code segment
dd 0x0000FFFF
db 0x00, 11001111b, 10001000b, 0x00
; Kernel data/stack segment
dd 0x0000FFFF
db 0x00, 11001111b, 10000010b, 0x00
end_gdt:
gdt_limit:
dw gdt_end - gdt - 1
gdt_location:
dd gdt
The first selector is the null selector. Is what I have above correct? I.e. the selector is always two dwords?
To load the GDT in NASM syntax:
lgdt [gdt_limit]
This seems funny to me... I thought I would have had to lgdt with a pointer to a word then a dword for the limit and location respectively. But here I am derefencing to the 6 bytes that lgdt needs instead. Presumably it works, but I don't understand why. Maybe internally the assembler arranges for the six bytes to be encoded in the instruction. I used ndisasm to have a look, but couldn't decipher it!
Anyway, what shall I do for my IDT? Do I need to re-program the PIC because of the system using Intel processor reserved IRQ vectors for other things? When the PIC is reprogrammed, does this mean it will cause the processor to fetch the correct IRQ vector -- i.e. the first hard disk is IRQ 14 but after reprogramming it will be something else because Intel reserves 14 for the page fault exception?
So presumably for my IDT I need to look in the Intel Manual System Programming Guide for the correct IRQ vector numbers, and install my handlers for them with IRQ 1 corresponding to the first descriptor in the IDT, IRQ 2 the second descriptor in the IDT, etc.?
If I want my syscall vector to be 0x60, e.g. in software:
mov eax, 1 ; syscall number 1 for read (just an example)
push dword [count] ; number of bytes to read
push dword buffer ; pointer to buffer for read
push dword 0 ; file descriptor 0 for stdin
int 0x60 ; call kernel sys_read
add esp, 12
... then I need to set up location 0x60 in the IDT to point to my syscall handler?
All the rest of the unused descriptors marked as not present?
Because the addresses for the GDT need to be linear, and in real mode they are set as logical addresses, how do I calculate this? Simply shift left the ds register (assuming it points correctly to data segment where GDT is) by 4 to multiply it by 16, then add the offset of the GDT from the ds register? Do I need to do this for the lidt instruction, too? (The Intel manual doesn't say so...) Do the pointers in the IDT need to be linear address pointers? I would assume so.
Once this is done, and I have done a lgdt and a lidt, I can enable the A20 gate and set the pmode bit and switch into pmode. Do I jump forward with a far jump to the next lot of instructions to force use of the selectors and such in the GDT? How does the processor know what selector in the GDT to use, since I have two, one for code and one for the stack/data area?
Okay, now assuming that I have gotten into pmode, and I want to enable paging, what are the minimum page tables that I need to setup? I want to map the kernel into the top 1GB of memory. This means, presumably, that I need to use a linker script to link the kernel at the correct physical and virtual addresses. Is that right?
Phew! That's all for the moment. Thanks for your help. ;D
At this point, I want to start in protected mode and enable paging. First, I must have a GDT and IDT ready. I have my GDT like this (expressed in NASM syntax):
align 8
gdt:
dd 0
dd 0
; Kernel code segment
dd 0x0000FFFF
db 0x00, 11001111b, 10001000b, 0x00
; Kernel data/stack segment
dd 0x0000FFFF
db 0x00, 11001111b, 10000010b, 0x00
end_gdt:
gdt_limit:
dw gdt_end - gdt - 1
gdt_location:
dd gdt
The first selector is the null selector. Is what I have above correct? I.e. the selector is always two dwords?
To load the GDT in NASM syntax:
lgdt [gdt_limit]
This seems funny to me... I thought I would have had to lgdt with a pointer to a word then a dword for the limit and location respectively. But here I am derefencing to the 6 bytes that lgdt needs instead. Presumably it works, but I don't understand why. Maybe internally the assembler arranges for the six bytes to be encoded in the instruction. I used ndisasm to have a look, but couldn't decipher it!
Anyway, what shall I do for my IDT? Do I need to re-program the PIC because of the system using Intel processor reserved IRQ vectors for other things? When the PIC is reprogrammed, does this mean it will cause the processor to fetch the correct IRQ vector -- i.e. the first hard disk is IRQ 14 but after reprogramming it will be something else because Intel reserves 14 for the page fault exception?
So presumably for my IDT I need to look in the Intel Manual System Programming Guide for the correct IRQ vector numbers, and install my handlers for them with IRQ 1 corresponding to the first descriptor in the IDT, IRQ 2 the second descriptor in the IDT, etc.?
If I want my syscall vector to be 0x60, e.g. in software:
mov eax, 1 ; syscall number 1 for read (just an example)
push dword [count] ; number of bytes to read
push dword buffer ; pointer to buffer for read
push dword 0 ; file descriptor 0 for stdin
int 0x60 ; call kernel sys_read
add esp, 12
... then I need to set up location 0x60 in the IDT to point to my syscall handler?
All the rest of the unused descriptors marked as not present?
Because the addresses for the GDT need to be linear, and in real mode they are set as logical addresses, how do I calculate this? Simply shift left the ds register (assuming it points correctly to data segment where GDT is) by 4 to multiply it by 16, then add the offset of the GDT from the ds register? Do I need to do this for the lidt instruction, too? (The Intel manual doesn't say so...) Do the pointers in the IDT need to be linear address pointers? I would assume so.
Once this is done, and I have done a lgdt and a lidt, I can enable the A20 gate and set the pmode bit and switch into pmode. Do I jump forward with a far jump to the next lot of instructions to force use of the selectors and such in the GDT? How does the processor know what selector in the GDT to use, since I have two, one for code and one for the stack/data area?
Okay, now assuming that I have gotten into pmode, and I want to enable paging, what are the minimum page tables that I need to setup? I want to map the kernel into the top 1GB of memory. This means, presumably, that I need to use a linker script to link the kernel at the correct physical and virtual addresses. Is that right?
Phew! That's all for the moment. Thanks for your help. ;D