Page 1 of 2
x64 Bootloader questions
Posted: Mon Apr 08, 2013 3:08 pm
by PearOs
Hello, I have been writing a 32bit x86 Assembly Operating System in Nasm. Its been working great, I can't remember off hand but I am pretty sure my bootloader is ISOLinux or SYSLinux. ISOLinux sounds more familiar. Anyways, so I decided to try to write in x64 using Nasm. I got the code compiling but do I need to do something to get into long mode? Like get a 64bit boot loader that will load my kernel and put me in long mode or will isolinux do that for me? Thanks, Matt
Re: x64 Bootloader questions
Posted: Mon Apr 08, 2013 4:11 pm
by Brendan
Hi,
PearOs wrote:Hello, I have been writing a 32bit x86 Assembly Operating System in Nasm. Its been working great, I can't remember off hand but I am pretty sure my bootloader is ISOLinux or SYSLinux. ISOLinux sounds more familiar. Anyways, so I decided to try to write in x64 using Nasm. I got the code compiling but do I need to do something to get into long mode? Like get a 64bit boot loader that will load my kernel and put me in long mode or will isolinux do that for me? Thanks, Matt
That sounds like you're using the
the Linux/x86 boot protocol. In this case it won't switch to long mode/64-bit for you, and you will need to write your own 32-bit code to
enable to long mode and jump to a 64-bit code segment.
The only boot loader that I can think of that will switch to 64-bit for you is
Pure64.
Cheers,
Brendan
Re: x64 Bootloader questions
Posted: Mon Apr 08, 2013 5:27 pm
by PearOs
Thanks, so now I know I just need to switch to Long Mode.. But I don't know how to do that in Assembly, and there isn't a lot of documentation on it. Right now I am sitting in ProtectedMode.
I could keep writing my code in protected mode and just keep using 32bit if I could find code for Long support. Because I need to work with longs where eax:edx is the long. But I can't find much code for Adding, Subtracting, Multiplying, Dividing, BitShiftLeft, BitShiftRight, Or, And ect.. That works anyways. It would have to be in Assembly. Thanks, Matt
Re: x64 Bootloader questions
Posted: Mon Apr 08, 2013 6:40 pm
by Brendan
Hi,
PearOs wrote:Thanks, so now I know I just need to switch to Long Mode.. But I don't know how to do that in Assembly, and there isn't a lot of documentation on it. Right now I am sitting in ProtectedMode.
Near the end of the wiki page I posted earlier, there's a "see also" link to
this page. If you can't figure it out from that (and the Intel/AMD manuals) then you probably shouldn't be allowed to use long mode..
PearOs wrote:I could keep writing my code in protected mode and just keep using 32bit if I could find code for Long support. Because I need to work with longs where eax:edx is the long. But I can't find much code for Adding, Subtracting, Multiplying, Dividing, BitShiftLeft, BitShiftRight, Or, And ect.. That works anyways. It would have to be in Assembly. Thanks, Matt
Addition is "add" then "adc". Subtraction is "sub" then "sbb". Shifting left and right is done with "shld" then "shl" or "shrd" then "shr". For the bitwise boolean operations there's no dependencies between bits, so you just use twice as many of the same instruction (e.g. to OR edx:eax with ecx:ebx you'd just do "or eax,ebx" then "or edx,ecx").
Multiplying is the same as you'd do on paper (pretend each dword is a digit). For example (multiplying 64-bit numbers to get a 128-bit result):
Code: Select all
; Do lowest dwords
mov eax,[value1]
mul dword [value2]
mov [result],eax
mov [result+4],edx
; Do highest dwords
mov eax,[value1+4]
mul dword [value2+4]
mov [result+8],eax
mov [result+12],edx
; Do middle dwords
mov eax,[value1+4]
mul dword [value2]
add [result+4],eax
adc [result+8],edx
adc dword [result+12],0
mov eax,[value1]
mul dword [value2+4]
add [result+4],eax
adc [result+8],edx
adc dword [result+12],0
Division is easy if the divisor is 32-bit (or smaller) - you just divide each dword and append the remainder, like this:
Code: Select all
mov eax,[value+4]
mov edx,0
div dword [divisor]
mov [result+4],eax
mov eax,[value]
div dword [divisor]
mov [result],eax ;[result] = 64-bit quotient, edx = 32-bit remainder
For division with a 64-bit divisor it gets a little messy - you need to break it up into a "shift, compare, subtract if lower" loop (although there are more complex ways). It's probably best to see the
corresponding wikipedia article.
Cheers,
Brendan
Re: x64 Bootloader questions
Posted: Mon Apr 08, 2013 8:59 pm
by PearOs
Wow thanks for the info! I looked at the page on entering long mode, and came up with this simple NASM code. Though it crashes after jmp far. I booted it in Boches and that's what it says as well, and when I try to enable paging it crashes too. Any idea's?
Code:
Code: Select all
%ifndef ELF_COMPILATION
use32
org 0x200000
[map all main.map]
%endif
%ifndef ELF_COMPILATION
MultibootSignature dd 464367618
MultibootFlags dd 65539
MultibootChecksum dd -464433157
MultibootHeaderAddr dd MultibootSignature
MultibootLoadAddr dd MultibootSignature
MultibootLoadEndAddr dd EndCode
MultibootBSSEndAddr dd EndCode
MultibootEntryAddr dd START
%endif
%ifdef ELF_COMPILATION
MultibootSignature dd 464367618
MultibootFlags dd 3
MultibootChecksum dd -464367621
%endif
MultibootGraphicsRuntime_VbeModeInfoAddr dd 2147483647
MultibootGraphicsRuntime_VbeControlInfoAddr dd 2147483647
MultibootGraphicsRuntime_VbeMode dd 2147483647
MultiBootInfo_Memory_High dd 0
MultiBootInfo_Memory_Low dd 0
MultiBootInfo_Structure dd 0
GDT64: ; Global Descriptor Table (64-bit).
.Null: equ $ - GDT64 ; The null descriptor.
dw 0 ; Limit (low).
dw 0 ; Base (low).
db 0 ; Base (middle)
db 0 ; Access.
db 0 ; Granularity.
db 0 ; Base (high).
.Code: equ $ - GDT64 ; The code descriptor.
dw 0 ; Limit (low).
dw 0 ; Base (low).
db 0 ; Base (middle)
db 10011000b ; Access.
db 00100000b ; Granularity.
db 0 ; Base (high).
.Data: equ $ - GDT64 ; The data descriptor.
dw 0 ; Limit (low).
dw 0 ; Base (low).
db 0 ; Base (middle)
db 10010000b ; Access.
db 00000000b ; Granularity.
db 0 ; Base (high).
.Pointer: ; The GDT-pointer.
dw $ - GDT64 - 1 ; Limit.
dq GDT64
jmp START ; Call our Start label to begin the OS.
;--------------START OF THE OS-----------------
START:
cli ;First halt interrupts.
;Setup our multi boot info Bascily we are grabbing, the amount of Ram
;and a few other important things. We need them for future use.
mov dword[MultiBootInfo_Structure], ebx
add dword ebx, 0x4
mov dword eax, [ebx]
mov dword[MultiBootInfo_Memory_Low], eax
add dword ebx, 0x4
mov dword eax, [ebx]
mov dword[MultiBootInfo_Memory_High], eax
pushfd ; Store the FLAGS-register.
pop eax ; Restore the A-register.
mov ecx, eax ; Set the C-register to the A-register.
xor eax, 1 << 21 ; Flip the ID-bit, which is bit 21.
push eax ; Store the A-register.
popfd ; Restore the FLAGS-register.
pushfd ; Store the FLAGS-register.
pop eax ; Restore the A-register.
push ecx ; Store the C-register.
popfd ; Restore the FLAGS-register.
xor eax, ecx ; Do a XOR-operation on the A-register and the C-register.
jz NoCPUID ; The zero flag is set, no CPUID.
mov eax, 0x80000000 ; Set the A-register to 0x80000000.
cpuid ; CPU identification.
cmp eax, 0x80000001 ; Compare the A-register with 0x80000001.
jb NoLongMode ; It is less, there is no long mode.
mov eax, 0x80000001 ; Set the A-register to 0x80000001.
cpuid ; CPU identification.
test edx, 1 << 29 ; Test if the LM-bit, which is bit 29, is set in the D-register.
jz NoLongMode ; They aren't, there is no long mode.
mov eax, cr0 ; Set the A-register to control register 0.
and eax, 01111111111111111111111111111111b ; Clear the PG-bit, which is bit 31.
mov cr0, eax ; Set control register 0 to the A-register.
mov edi, 0x1000 ; Set the destination index to 0x1000.
mov cr3, edi ; Set control register 3 to the destination index.
xor eax, eax ; Nullify the A-register.
mov ecx, 4096 ; Set the C-register to 4096.
rep stosd ; Clear the memory.
mov edi, cr3 ; Set the destination index to control register 3.
mov DWORD [edi], 0x2003 ; Set the double word at the destination index to 0x2003.
add edi, 0x1000 ; Add 0x1000 to the destination index.
mov DWORD [edi], 0x3003 ; Set the double word at the destination index to 0x3003.
add edi, 0x1000 ; Add 0x1000 to the destination index.
mov DWORD [edi], 0x4003 ; Set the double word at the destination index to 0x4003.
add edi, 0x1000 ; Add 0x1000 to the destination index.
mov ebx, 0x00000003 ; Set the B-register to 0x00000003.
mov ecx, 512 ; Set the C-register to 512.
.SetEntry:
mov DWORD [edi], ebx ; Set the double word at the destination index to the B-register.
add ebx, 0x1000 ; Add 0x1000 to the B-register.
add edi, 8 ; Add eight to the destination index.
loop .SetEntry ; Set the next entry.
mov eax, cr4 ; Set the A-register to control register 4.
or eax, 1 << 5 ; Set the PAE-bit, which is the 6th bit (bit 5).
mov cr4, eax ; Set control register 4 to the A-register.
mov ecx, 0xC0000080 ; Set the C-register to 0xC0000080, which is the EFER MSR.
rdmsr ; Read from the model-specific register.
or eax, 1 << 8 ; Set the LM-bit which is the 9th bit (bit 8).
wrmsr ; Write to the model-specific register.
;mov eax, cr0 ; Set the A-register to control register 0.
;or eax, 1 << 31 ; Set the PG-bit, which is the 32nd bit (bit 31).
;mov cr0, eax ; Set control register 0 to the A-register.
lgdt [GDT64.Pointer] ; Load the 64-bit global descriptor table.
jmp GDT64.Code:Realm64 ; Set the code segment and enter 64-bit long mode.
NoCPUID:
mov eax, 0xB8000
mov byte[eax], 'C'
jmp os_hang
NoLongMode:
mov eax, 0xB8000
mov byte[eax], 'L'
jmp os_hang
os_hang:
jmp os_hang
[BITS 64]
Realm64:
jmp os_hang
cli ; Clear the interrupt flag.
mov ax, GDT64.Data ; Set the A-register to the data descriptor.
mov ds, ax ; Set the data segment to the A-register.
mov es, ax ; Set the extra segment to the A-register.
mov fs, ax ; Set the F-segment to the A-register.
mov gs, ax ; Set the G-segment to the A-register.
mov edi, 0xB8000 ; Set the destination index to 0xB8000.
mov rax, 0x1F201F201F201F20 ; Set the A-register to 0x1F201F201F201F20.
mov ecx, 500 ; Set the C-register to 500.
rep movsq ; Clear the screen.
hlt ; Halt the processor.
EndCode:
isolinux is my bootloader. Thanks a ton. - Matt
Re: x64 Bootloader questions
Posted: Tue Apr 09, 2013 12:26 am
by Combuster
PearOs wrote:MultibootSignature dd 464367618
(...)
isolinux is my bootloader. Thanks a ton. - Matt
There's at least one lie in there...
Other than that: you don't enable paging so you can't end up in long mode, and the GDT is bogus.
Re: x64 Bootloader questions
Posted: Tue Apr 09, 2013 4:18 am
by xenos
Just a detail I found and which I would like to add to what Combuster said: I guess this should be stosq, not movsq. This is not related to your long mode problem. I'd suggest that you should go through your code carefully and check for errors like this one.
Re: x64 Bootloader questions
Posted: Tue Apr 09, 2013 7:16 am
by PearOs
Combuster wrote:PearOs wrote:MultibootSignature dd 464367618
(...)
isolinux is my bootloader. Thanks a ton. - Matt
There's at least one lie in there...
Other than that: you don't enable paging so you can't end up in long mode, and the GDT is bogus.
Ok so I uncommented:
Code: Select all
mov eax, cr0 ; Set the A-register to control register 0.
or eax, 1 << 31 ; Set the PG-bit, which is the 32nd bit (bit 31).
mov cr0, eax ; Set control register 0 to the A-register.
And now Bochs says "Instruction not available page not present", What exactly does that mean? And how do I go about fixing it. Thank you for looking into this, Matt
Re: x64 Bootloader questions
Posted: Tue Apr 09, 2013 7:17 am
by PearOs
XenOS wrote:
Just a detail I found and which I would like to add to what Combuster said: I guess this should be stosq, not movsq. This is not related to your long mode problem. I'd suggest that you should go through your code carefully and check for errors like this one.
Thanks for the tip, ill have to go through and check for bugs. Thanks, Matt
Re: x64 Bootloader questions
Posted: Tue Apr 09, 2013 7:50 am
by PearOs
Combuster wrote:PearOs wrote:MultibootSignature dd 464367618
(...)
isolinux is my bootloader. Thanks a ton. - Matt
There's at least one lie in there...
Other than that: you don't enable paging so you can't end up in long mode, and the GDT is bogus.
Also, what you see came from:
http://wiki.osdev.org/User:Stephanvansc ... _Long_Mode
If the GDT is bogus then why did that person post it? If it didn't work why release it?
Re: x64 Bootloader questions
Posted: Tue Apr 09, 2013 8:03 am
by xenos
PearOs wrote:And now Bochs says "Instruction not available page not present", What exactly does that mean? And how do I go about fixing it. Thank you for looking into this, Matt
That means that there is some problem with your page tables, i.e., your code is not identity mapped correctly. Bochs's debugger can print information on the page tables, I think the debugger command is "info page" or similar.
PearOs wrote:If the GDT is bogus then why did that person post it? If it didn't work why release it?
Maybe Combuster was a bit quick on this
In long mode the segment base and limit are ignored, so I guess the GDT entries should be fine (I haven't checked them completely, though - so there might be some error in there which I haven't seen).
Re: x64 Bootloader questions
Posted: Tue Apr 09, 2013 8:07 am
by JAAman
PearOs wrote:
Ok so I uncommented:
Code: Select all
mov eax, cr0 ; Set the A-register to control register 0.
or eax, 1 << 31 ; Set the PG-bit, which is the 32nd bit (bit 31).
mov cr0, eax ; Set control register 0 to the A-register.
And now Bochs says "Instruction not available page not present", What exactly does that mean? And how do I go about fixing it. Thank you for looking into this, Matt
if i had to guess, that probably means your current code is not in an identity mapped page
before you enable paging, you must have some basic page tables set up (and cr3 loaded correctly)
as soon as you set that bit in cr0 (with the mov cr0, eax line) the CPU enters paging mode, and tries to translate all addresses using the page tables -- but if your current eIP is in an unmapped page (marked as not present) then what happens? the CPU tries to locate the next instruction, but the page is not present, and thus it cannot continue -- ordinarily you should get an immediate page fault, but my guess is you don't have your IDT set up yet, (or a page fault handler mapped in anywhere) so basically this should cause a triple-fault (CPU generates a page fault, cannot call the page fault handler, which triggers a double fault, which also cannot be processed... so the CPU enters ERROR_STATE and (usually) the chipset responds to this by resetting the computer
so before you enable paging, make sure you have at least set up the tables pointing to the current code (and make sure they are filled out correctly)
Re: x64 Bootloader questions
Posted: Tue Apr 09, 2013 8:08 am
by Combuster
In long mode the segment base and limit are ignored
But that condition wasn't met and the CPU tried using a GDT it couldn't understand
Re: x64 Bootloader questions
Posted: Tue Apr 09, 2013 11:17 am
by xenos
Combuster wrote:But that condition wasn't met and the CPU tried using a GDT it couldn't understand
Agreed. I thought you meant the GDT is bogus
in general, not just in the current situation (= not getting into long mode).
Re: x64 Bootloader questions
Posted: Tue Apr 09, 2013 2:46 pm
by PearOs
Ok so what I take from all of this is that I need to setup paging, and maybe look into the GDT, even though it seems to work, the system only crashes when I try to do a far jump. But i'll play around. Any help/suggestions and setting up paging? Thanks, Matt
P.S. Now I do have an Protected Mode IDT that I wrote, and it works great, I have been using interrupts, I just would like to move to 64bit. Shoul I enable interrupts and setup the IDT before all of this?