damsel in distress (boot sector)
damsel in distress (boot sector)
Hi everyone,
I am new to the OS development world and have been working on a bootloader for about a week based on the tutorials on this website. I feel like I'm about 95% percent done with my bootsector but I just can't seem to finish it.
Everytime I try to far jump to my protected mode code everything goes down in flames. I have read the register values that bochs provides after each test run and it seems that when I jump to pmode the cs register is actually reloaded with the correct GDT selector value: index 0x08, code segment. To double check this I even tried loading the other seg registers with the data segment value (0x10) and it indexes correctly as well. However I do know that you are suppose to reload the seg regs AFTER the jump to protected mode but I never seem to make it that far But from this I believe that the GDT address is valid or why else would my indexes load the actual GDT selectors?
I have included my bootsector code at the bottom. If anyone has any ideas why this might be crashing I'm all ears
Thanx guys,
Nicky
;********************************************************************
ORG 0
[BITS 16]
jmp 0x7c0:start
start:
xor ebx, ebx
mov bx, cs
mov ds, bx ; load all of the
mov ss, bx ; segment regs with
mov es, bx ; real mode cs value
mov fs, bx ; for debugging
mov gs, bx
shl ebx, 4
lea eax, [gdt + ebx]
mov [gdtr + 2], eax ; put the physical address
; of the GDT at the gdtr base
cli
lgdt[ds:gdtr]
mov eax, cr0
or al, 1
mov cr0, eax
jmp SHORT jumper ;Some people have suggested a SHORT jmp
;before the far jump to pmode
jumper: jmp CODE_SEL:go_pm ;This will index into the GDT
;as a valid descriptor and
;resets CS to a 32 bit mode
;code segment but crashes as it
;enters go_pm:
[BITS 32]
go_pm:
mov edx, 512 ; debugging step
mov ax, DATA_SEL
mov es, ax
mov byte [es:dword 0xB8000], 'X'
loop1:
jmp loop1
[BITS 16]
gdtr: dw gdt_end - gdt - 1
dd gdt
gdt: dw 0
dw 0
db 0
db 0
db 0
db 0
CODE_SEL equ $-gdt
dw 0xFFFF
dw 0
db 0
db 0x9A
db 0xCF
db 0
DATA_SEL equ $-gdt
dw 0xFFFF
dw 0
db 0
db 0x92
db 0xCF
db 0
gdt_end:
times 510-($-$$) db 0
dw 0xaa55
I am new to the OS development world and have been working on a bootloader for about a week based on the tutorials on this website. I feel like I'm about 95% percent done with my bootsector but I just can't seem to finish it.
Everytime I try to far jump to my protected mode code everything goes down in flames. I have read the register values that bochs provides after each test run and it seems that when I jump to pmode the cs register is actually reloaded with the correct GDT selector value: index 0x08, code segment. To double check this I even tried loading the other seg registers with the data segment value (0x10) and it indexes correctly as well. However I do know that you are suppose to reload the seg regs AFTER the jump to protected mode but I never seem to make it that far But from this I believe that the GDT address is valid or why else would my indexes load the actual GDT selectors?
I have included my bootsector code at the bottom. If anyone has any ideas why this might be crashing I'm all ears
Thanx guys,
Nicky
;********************************************************************
ORG 0
[BITS 16]
jmp 0x7c0:start
start:
xor ebx, ebx
mov bx, cs
mov ds, bx ; load all of the
mov ss, bx ; segment regs with
mov es, bx ; real mode cs value
mov fs, bx ; for debugging
mov gs, bx
shl ebx, 4
lea eax, [gdt + ebx]
mov [gdtr + 2], eax ; put the physical address
; of the GDT at the gdtr base
cli
lgdt[ds:gdtr]
mov eax, cr0
or al, 1
mov cr0, eax
jmp SHORT jumper ;Some people have suggested a SHORT jmp
;before the far jump to pmode
jumper: jmp CODE_SEL:go_pm ;This will index into the GDT
;as a valid descriptor and
;resets CS to a 32 bit mode
;code segment but crashes as it
;enters go_pm:
[BITS 32]
go_pm:
mov edx, 512 ; debugging step
mov ax, DATA_SEL
mov es, ax
mov byte [es:dword 0xB8000], 'X'
loop1:
jmp loop1
[BITS 16]
gdtr: dw gdt_end - gdt - 1
dd gdt
gdt: dw 0
dw 0
db 0
db 0
db 0
db 0
CODE_SEL equ $-gdt
dw 0xFFFF
dw 0
db 0
db 0x9A
db 0xCF
db 0
DATA_SEL equ $-gdt
dw 0xFFFF
dw 0
db 0
db 0x92
db 0xCF
db 0
gdt_end:
times 510-($-$$) db 0
dw 0xaa55
RE:damsel in distress (boot sector)
"gdtr: dw gdt_end - gdt - 1
dd gdt"
The gdtr uses linear addresses (not effected by segmentation), but you are in segment 0x7c0. I would set the org at the top to 0x7c00 and then use segment 0. The other option is to change the above code to the following:
gdtr: dw gdt_end - gdt - 1
dd gdt + 0x7c00
That should also work, but I haven't tried it, nor have I looked for other bugs.
dd gdt"
The gdtr uses linear addresses (not effected by segmentation), but you are in segment 0x7c0. I would set the org at the top to 0x7c00 and then use segment 0. The other option is to change the above code to the following:
gdtr: dw gdt_end - gdt - 1
dd gdt + 0x7c00
That should also work, but I haven't tried it, nor have I looked for other bugs.
RE:damsel in distress (boot sector)
Okay, here's the problem I see. You're code starts at physical address 0x00007c00, which you've chose to represent in real mode as segmented address 0x07c0:0000, which works just fine while you're still in real mode, because segment 0x07c0 starts at physical address 0x00007c00. The problem is that your protected mode segment doesn't start at the same address. It starts at physical address 0x00000000. So lets assume that the physical address for go_pm is 0x00007c20 (32 bytes after the start of your code) just for example. Because you have org 0, the value for the symbol go_pm is 0x20, then in real mode with segment 0x07c0 that address would be computed as 0x00007c00+0x20 = 0x00007c20 which is what we wanted, but using your protected mode code segment we get 0x00000000+0x20 = 0x00000020, which isn't what we wanted. So you have two choices:
1) Make your protected mode segment's base address the same as your real mode base address, so it would look like this:
CODE_SEL equ $-gdt
dw 0xFFFF
dw 0x7c00
db 0
db 0x9A
db 0xCF
db 0
2) You can make your real mode segment's base address match your protected mode base address by starting your code like this instead of what you have:
ORG 0x7c00
[BITS 16]
jmp 0x0000:start
Personally, I would recommend the second option, so that your protected mode segment can map your segmented address directly to your physical address. (Unless that's not what you want.)
1) Make your protected mode segment's base address the same as your real mode base address, so it would look like this:
CODE_SEL equ $-gdt
dw 0xFFFF
dw 0x7c00
db 0
db 0x9A
db 0xCF
db 0
2) You can make your real mode segment's base address match your protected mode base address by starting your code like this instead of what you have:
ORG 0x7c00
[BITS 16]
jmp 0x0000:start
Personally, I would recommend the second option, so that your protected mode segment can map your segmented address directly to your physical address. (Unless that's not what you want.)
RE:damsel in distress (boot sector)
Hmm... Rexlunae's comment wasn't there when I started typing mine, but he/she mentioned something I forgot in option 1. If you keep your org 0, you have to change your descriptors AND your gdtr. Doing one without the other won't work. Also, I forgot to mention that you would need to change your data descriptor and not just the code descriptor. So that's another reason why you should use org 0x7c00: there's too much to keep track of if you don't.
RE:damsel in distress (boot sector)
One of your problems is that you are trying to acces 32-bit registers (ex. EAX) in 16-bit Real Mode. You must first switch to either 32-bit Real Mode or 32-bit Protected Mode. Another thing that I see is that you use "ORG 0". It should run if you use "ORG 0x7C00" and then jump to a label (not an address). I hope this helps.
~ TripleFault !)
~ TripleFault !)
RE:damsel in distress (boot sector)
You CAN use 32-bit register in real mode. Regarding the jump, a jump to a label is a jump to the address of the label. But the segment (0x0000) MUST be specified (on the jump to start) to ensure that you are in the right segment, because the BIOS may not have put you into the segment you think it did.
RE:damsel in distress (boot sector)
xSadar is right that you can use 32-bit registers in real mode, because most assemblers (nasm included) will insert the correct overrides as long as bits 16 is used. However, I would recommend that you not use them in a boot sector unless you actually need them, because the combination of a 1 or 2 byte override and 2 more bytes in immediates makes the 32-bit code larger, and space is generally an issue in boot sectors.
RE:damsel in distress (boot sector)
"xor ebx, ebx
mov bx, cs
shl ebx, 4
lea eax, [gdt + ebx]
mov [gdtr + 2], eax ; put the physical address
; of the GDT at the gdtr base "
Oop, I didn't look closely at your code when I posted at first, so I missed this. This will put the right address for the gdt in the gdtr. However, xSadar is correct that your pmode segments need to be at the same base as your real mode segments. My advice is the same, use org 0x7c00 and all your segments as 0, it will be easier. For one thing, you should be able to hardcode the location of the gdt without updating it at runtime.
mov bx, cs
shl ebx, 4
lea eax, [gdt + ebx]
mov [gdtr + 2], eax ; put the physical address
; of the GDT at the gdtr base "
Oop, I didn't look closely at your code when I posted at first, so I missed this. This will put the right address for the gdt in the gdtr. However, xSadar is correct that your pmode segments need to be at the same base as your real mode segments. My advice is the same, use org 0x7c00 and all your segments as 0, it will be easier. For one thing, you should be able to hardcode the location of the gdt without updating it at runtime.
RE:damsel in distress (boot sector)
One more thing. This is from the NASM manual.
"LGDT and LIDT both take a 6-byte memory area as an operand: they load a 32-bit linear address and a 16-bit size limit from that area (in the opposite order) into the GDTR (global descriptor table register) or IDTR (interrupt descriptor table register). These are the only instructions which directly use linear addresses, rather than segment/offset pairs."
I take this to mean that not only is the gdtr uneffected by segmentation, but also the lgdt instruction's operand is as well. If that is correct, the the segment override in this instruction will not be used "lgdt[ds:gdtr]".
I hope some of what we have said is useful, and maybe even correct.
"LGDT and LIDT both take a 6-byte memory area as an operand: they load a 32-bit linear address and a 16-bit size limit from that area (in the opposite order) into the GDTR (global descriptor table register) or IDTR (interrupt descriptor table register). These are the only instructions which directly use linear addresses, rather than segment/offset pairs."
I take this to mean that not only is the gdtr uneffected by segmentation, but also the lgdt instruction's operand is as well. If that is correct, the the segment override in this instruction will not be used "lgdt[ds:gdtr]".
I hope some of what we have said is useful, and maybe even correct.
RE:damsel in distress (boot sector)
Only the content of [gdtr] is a linear address + limit, it is still addressed by normal segment:offset calculation. So lgdt[ds:gdtr] or lgdt[es:gdtr] or whatever should work as usual.
I hope that this is correct...
I hope that this is correct...