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
A Basic PMode Kernel with Paging
- Pype.Clicker
- Member
- Posts: 5964
- Joined: Wed Oct 18, 2006 2:31 am
- Location: In a galaxy, far, far away
- Contact:
Re:A Basic PMode Kernel with Paging
That's just the wy it work: the LG/IDT instruction assume they're given the memory reference of a 6 bytes "structure" made of the limit and base. The assembler does nothing magic (esp. Nasm *never* does anything magic): if just uses your [gdt_limit] to issue the EffectiveAddress computation that will be used by the CPU to load GDTR.kernel_journeyman wrote: 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.
much like when doing mov eax,[somevar], the content of "somevar" is nowhere in the instruction...
yes. most of us remap IRQs to 0x20..0x2f. so timer calls IRQ0 -- mapped to int 0x20 (if unmasked), which is IDT entry .. number 32 ...Do I need to re-program the PIC because of the system using Intel processor reserved IRQ vectors for other things?
yes and yes (unless you want them for SomethingElse(tm))... 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?
yes and yes (when in real mode). Though if you manage to have DS==0 while setting things up in real mode, it'll be easier for you.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?
Pointers within the IDT descriptors are offset within the given code segment
Re:A Basic PMode Kernel with Paging
Thankyou very much Pype, I greatly appreciate your help.
I am trying to load like this:
[BITS 16]
[SECTION .text]
[ORG 0]
start:
db 0xEA
jmp 0x7C00:main
main:
push cs
pop ds ; setup data seg to 7C00:0000
push cs
pop es ; ditto extra seg
mov cx, 256 ; 256 words for this bootsector size
mov si, 0x7C00 ; move this
mov di, 0x1000 ; to here
cld
rep movsw ; do it
; presumably we are now at 1000:0000
; XXX do we need to far jump to reset seg registers???
setup:
cli
xor ax, ax
mov ss, ax
mov sp, 0xFFFF ; stack seg is 0000:0000 to 0000:FFFF
mov cx, 5 ; try 5 times
mov bx, 0x1200 ; 0x1000 + 512 bytes
; After the bootsector, which is 512 bytes and starts at
; 1000:0000, I want the "kernel" to sit directly after it.
; Presumably this would be 0000:1200h ?
again:
call reset_floppy
call load_kernel
dec cx ; note that the calls preserve cx
cmp cx, 0 ; five times? We're done... die.
je die
jc again ; less than five times, but failed? retry
sti ; OK, we're done. It worked...
; Jump into kernel
db 0xEA
jmp 0000:0x1200 ; Jump into the "kernel"
; Put an E in video memory to tell about the error
; Not enough room for a full error message!
die:
mov ax, 0xB800
mov ds, ax
mov byte [ds:0x00], 'E' ; XXX address correct?
hang:
hlt
jmp hang
load_kernel:
push cx
; Get ready to read in sectors 2 to 63
mov ah, 2 ; read sector(s) into memory
mov al, 63 ; number of sectors, here 63 = a whole track
mov ch, 1 ; cylinder number
mov cl, 2 ; starting sector
mov dh, 1 ; XXX head number ???
mov dl, 0 ; drive number
int 13h
pop cx
ret
reset_floppy:
mov ah, 0h
mov dl, 0h
int 13h
ret
times 510-($-$$) db 0
dw 0xAA55
Now the "kernel":
[BITS 16]
[SECTION .text]
[ORG 0x1200]
start_kernel:
push cs
pop ds
push 0xB800
pop es
mov si, message
mov di, 0x0
mov cx, [count]
cld ; err...
rep movsb
[SECTION .data]
message: db 'H e l l o W o r l d ! '
count: dw $-message
--------------------------------------------------------------------------
I am a little confused at the direction flag. Anyway, I'll look it up in the Intel manual and figure out whether set is up or down and give it another try.
One thing that really confuses me is heads, sectors and cylinders. I understand that heads is synonymous with tracks. How do I know how many cylinders, heads/tracks and sectors are on a floppy disk? How do I know, once reading 63 sectors which seems to be a whole track/head, what to switch to in terms of cylinders and heads/tracks? For instance, sectors are numbered starting with 1, but cylinders and heads/tracks from 0?
If I want to read from the whole floppy, supposing I had a 1440KB boot image, how would I do it? Are the cylinders/heads/sectors the same for a 2.88MB floppy image (e.g. a bootable CDROM?)
Is it the case that 63 sectors (sectors 1 to 63) is head 0, cylinder 0? And sectors 64 to 127 is head 1, cylinder 0? When do cylinders change? Do the heads simply change every 63 sectors? Are they numbered 0 up to however many sectors there are on a floppy? E.g. 1.44MB / 512b / 63 = number of heads?
Thanks...
I am trying to load like this:
[BITS 16]
[SECTION .text]
[ORG 0]
start:
db 0xEA
jmp 0x7C00:main
main:
push cs
pop ds ; setup data seg to 7C00:0000
push cs
pop es ; ditto extra seg
mov cx, 256 ; 256 words for this bootsector size
mov si, 0x7C00 ; move this
mov di, 0x1000 ; to here
cld
rep movsw ; do it
; presumably we are now at 1000:0000
; XXX do we need to far jump to reset seg registers???
setup:
cli
xor ax, ax
mov ss, ax
mov sp, 0xFFFF ; stack seg is 0000:0000 to 0000:FFFF
mov cx, 5 ; try 5 times
mov bx, 0x1200 ; 0x1000 + 512 bytes
; After the bootsector, which is 512 bytes and starts at
; 1000:0000, I want the "kernel" to sit directly after it.
; Presumably this would be 0000:1200h ?
again:
call reset_floppy
call load_kernel
dec cx ; note that the calls preserve cx
cmp cx, 0 ; five times? We're done... die.
je die
jc again ; less than five times, but failed? retry
sti ; OK, we're done. It worked...
; Jump into kernel
db 0xEA
jmp 0000:0x1200 ; Jump into the "kernel"
; Put an E in video memory to tell about the error
; Not enough room for a full error message!
die:
mov ax, 0xB800
mov ds, ax
mov byte [ds:0x00], 'E' ; XXX address correct?
hang:
hlt
jmp hang
load_kernel:
push cx
; Get ready to read in sectors 2 to 63
mov ah, 2 ; read sector(s) into memory
mov al, 63 ; number of sectors, here 63 = a whole track
mov ch, 1 ; cylinder number
mov cl, 2 ; starting sector
mov dh, 1 ; XXX head number ???
mov dl, 0 ; drive number
int 13h
pop cx
ret
reset_floppy:
mov ah, 0h
mov dl, 0h
int 13h
ret
times 510-($-$$) db 0
dw 0xAA55
Now the "kernel":
[BITS 16]
[SECTION .text]
[ORG 0x1200]
start_kernel:
push cs
pop ds
push 0xB800
pop es
mov si, message
mov di, 0x0
mov cx, [count]
cld ; err...
rep movsb
[SECTION .data]
message: db 'H e l l o W o r l d ! '
count: dw $-message
--------------------------------------------------------------------------
I am a little confused at the direction flag. Anyway, I'll look it up in the Intel manual and figure out whether set is up or down and give it another try.
One thing that really confuses me is heads, sectors and cylinders. I understand that heads is synonymous with tracks. How do I know how many cylinders, heads/tracks and sectors are on a floppy disk? How do I know, once reading 63 sectors which seems to be a whole track/head, what to switch to in terms of cylinders and heads/tracks? For instance, sectors are numbered starting with 1, but cylinders and heads/tracks from 0?
If I want to read from the whole floppy, supposing I had a 1440KB boot image, how would I do it? Are the cylinders/heads/sectors the same for a 2.88MB floppy image (e.g. a bootable CDROM?)
Is it the case that 63 sectors (sectors 1 to 63) is head 0, cylinder 0? And sectors 64 to 127 is head 1, cylinder 0? When do cylinders change? Do the heads simply change every 63 sectors? Are they numbered 0 up to however many sectors there are on a floppy? E.g. 1.44MB / 512b / 63 = number of heads?
Thanks...
Re:A Basic PMode Kernel with Paging
Hi,
The "db 0xEA" things are will make it crash! Just remove them
Yes - you would need at least a far jump and reloading segments would be a good idea, but... is there any reason for this relocation?
Everything is numbered starting with 0, except for sectors. A standard "1.44 Mb" floppy disk has 2 heads/sides, 80 tracks/cylinders and 18 sectors per track. Each sector is 512 bytes, so the floppy will hold 2 * 80 * 18 * 512 bytes (or 1440 Kb) of data.
Cylinders change after every sector in the current cyclinder is read (ie. each sector in each track under each each head). This is because electronically selecting which head to use is fast, and actually moving the heads takes the longest.
Taking all that into account:
Cheers,
Brendan
Code: Select all
start:
db 0xEA
jmp 0x7C00:main
...
db 0xEA
jmp 0000:0x1200 ; Jump into the "kernel"
Code: Select all
mov cx, 256 ; 256 words for this bootsector size
mov si, 0x7C00 ; move this
mov di, 0x1000 ; to here
cld
rep movsw ; do it
; presumably we are now at 1000:0000
; XXX do we need to far jump to reset seg registers???
The "CLD" instruction will make addresses increase. Normally you don't wan't STDskernel_journeyman wrote: I am a little confused at the direction flag. Anyway, I'll look it up in the Intel manual and figure out whether set is up or down and give it another try.
Imagine that a disc consists of a set of rings ranging in size so that each ring fits inside the next largest ring. Each ring is a "cylinder" which has a top and a bottom, where the top is one track and the bottom is another track. These tracks are read/written via heads (one head on the top of the ring/cylinder and another on the bottom). The heads are mounted on a moving arm that can move from the outside ring/cylinder/track to the inside ring/cylinder/track. A sector is a part of a track, so there's multiple sectors around each track. This description is for 2 sided floppy disks only - single sided floppies don't have a head on the bottom, and hard drives have multiple discs with a head on the top and bottom of each disc.kernel_journeyman wrote: One thing that really confuses me is heads, sectors and cylinders. I understand that heads is synonymous with tracks. How do I know how many cylinders, heads/tracks and sectors are on a floppy disk? How do I know, once reading 63 sectors which seems to be a whole track/head, what to switch to in terms of cylinders and heads/tracks? For instance, sectors are numbered starting with 1, but cylinders and heads/tracks from 0?
Everything is numbered starting with 0, except for sectors. A standard "1.44 Mb" floppy disk has 2 heads/sides, 80 tracks/cylinders and 18 sectors per track. Each sector is 512 bytes, so the floppy will hold 2 * 80 * 18 * 512 bytes (or 1440 Kb) of data.
A 2880 Kb floppy is the same as a 1440 Kb floppy, except that it has twice as many sectors per track (36 of them).kernel_journeyman wrote: If I want to read from the whole floppy, supposing I had a 1440KB boot image, how would I do it? Are the cylinders/heads/sectors the same for a 2.88MB floppy image (e.g. a bootable CDROM?)
For a 1440 Kb floppy sectors 1 to 18 are head 0, cylinder 0. Sectors 19 to 36 are head 1, cylinder 0, and sectors 37 to 54 are head 0, cylinder 1.kernel_journeyman wrote: Is it the case that 63 sectors (sectors 1 to 63) is head 0, cylinder 0? And sectors 64 to 127 is head 1, cylinder 0? When do cylinders change? Do the heads simply change every 63 sectors? Are they numbered 0 up to however many sectors there are on a floppy? E.g. 1.44MB / 512b / 63 = number of heads?
Cylinders change after every sector in the current cyclinder is read (ie. each sector in each track under each each head). This is because electronically selecting which head to use is fast, and actually moving the heads takes the longest.
Taking all that into account:
Code: Select all
; Get ready to read in sectors 2 to 18 (8.5 Kb)
mov ah, 2 ; read sector(s) into memory
mov al, 18 ; number of sectors, here 18 = a whole track
mov ch, 0 ; cylinder number
mov cl, 2 ; starting sector
mov dh, 0 ; head number
mov dl, 0 ; drive number
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Re:A Basic PMode Kernel with Paging
Uhm, no, tracks synonymous with cylinders.One thing that really confuses me is heads, sectors and cylinders. I understand that heads is synonymous with tracks.
They're fixed.How do I know how many cylinders, heads/tracks and sectors are on a floppy disk?
For instance a 1.44 MB floppy has 80 cylinders, 18 sectors per track, and 2 heads.
720 kb: 40/2/18 (C/H/S)
2.88 MB: 80/2/36 (don't know sure, someone ?) (C/H/S)
Yeah, correct, every HD/FD has that "numbering" systemFor instance, sectors are numbered starting with 1, but cylinders and heads/tracks from 0?
HTH a bit
DennisCGc
Re:A Basic PMode Kernel with Paging
Almost.DennisCGc wrote:Uhm, no, tracks synonymous with cylinders.One thing that really confuses me is heads, sectors and cylinders. I understand that heads is synonymous with tracks.
A track is a unique combination of a head and a cylinder. It's the cylinder-part of one side of one platter. A floppy disk (1.44M) thus has 160 tracks of 18 sectors each, total of 2880 sectors.