Talk:Entering Long Mode Directly
Only put the code that switches you into long mode?
Maybe we shouldn't dump a bootloader here; maybe we put the code to do all of this... Imate900 20:24, 23 April 2009 (UTC)
Version fitting in a single boot sector
Thanks for the code! However, for me, it needed some changes to work: - move the padding and the 0xaa55 signature to the end of the file; this enables the whole example fit in a boot sector, - this eliminates the need for the BIOS 13h call, so I removed it.
[ORG 0x00007C00] [BITS 16] boot_loader: ;Parameter from BIOS: dl = boot drive ;Set default state cli xor bx,bx mov es,bx mov fs,bx mov gs,bx mov ds,bx mov ss,bx mov sp,0x7C00 sti jmp 0:.clear_cs .clear_cs: ;Enable A20 via port 92h in al,92h or al,02h out 92h,al ;Build page tables ;The page tables will look like this: ;PML4: ;dq 0x000000000000b00f = 00000000 00000000 00000000 00000000 00000000 00000000 10010000 00001111 ;times 511 dq 0x0000000000000000 ;PDP: ;dq 0x000000000000c00f = 00000000 00000000 00000000 00000000 00000000 00000000 10100000 00001111 ;times 511 dq 0x0000000000000000 ;PD: ;dq 0x000000000000018f = 00000000 00000000 00000000 00000000 00000000 00000000 00000001 10001111 ;times 511 dq 0x0000000000000000 ;This defines one 2MB page at the start of memory, so we can access the first 2MBs as if paging was disabled ; build the necessary tables xor bx,bx mov es,bx cld mov di,0xa000 mov ax,0xb00f stosw xor ax,ax mov cx,0x07ff rep stosw mov ax,0xc00f stosw xor ax,ax mov cx,0x07ff rep stosw mov ax,0x018f stosw xor ax,ax mov cx,0x07ff rep stosw ;Enter long mode mov eax,10100000b ;Set PAE and PGE mov cr4,eax mov edx, 0x0000a000 ;Point CR3 at PML4 mov cr3,edx mov ecx,0xC0000080 ;Specify EFER MSR rdmsr ;Enable Long Mode or eax,0x00000100 wrmsr mov ebx,cr0 ;Activate long mode or ebx,0x80000001 ;by enabling paging and protection simultaneously mov cr0,ebx ;skipping protected mode entirely lgdt [gdt.pointer] ;load 80-bit gdt.pointer below jmp gdt.code:startLongMode ;Load CS with 64 bit segment and flush the instruction cache ;Global Descriptor Table gdt: dq 0x0000000000000000 ;Null Descriptor .code equ $ - gdt dq 0x0020980000000000 .data equ $ - gdt dq 0x0000900000000000 .pointer: dw $-gdt-1 ;16-bit Size (Limit) dq gdt ;64-bit Base Address ;Changed from "dd gdt" ;Ref: Intel System Programming Manual V1 - 188.8.131.52 [BITS 64] startLongMode: cli ;Interupts are disabled because no IDT has been set up mov edi,0x00b8000 ;Display:"Put long mode kernel here.__" mov rax,0x0720077407750750 ;at the top left corner of screen mov [edi],rax mov rax,0x0767076e076f076c mov [edi+8],rax mov rax,0x0764076f076d0720 mov [edi+16],rax mov rax,0x0765076b07200765 mov [edi+24],rax mov rax,0x076c0765076e0772 mov [edi+32],rax mov rax,0x0772076507680720 mov [edi+40],rax mov rax,0x07200720072e0765 mov [edi+48],rax jmp $ ;Hang the system times 510-($-$$) db 0 ;Fill boot sector dw 0xAA55 ;Boot loader signature
To use (in Linux):
$ nasm -o boot boot.asm $ qemu-system-x86_64 -hda boot
Oculusfervoris 06:31, 5 June 2011 (UTC)
Not to put 64bit code in MBR
Your version is a good example i thinnk, but, there are some problems with the idea of fitting 64bit code into MBR:
- 64bit code is usually quite large and you won't want to fit a portion
(just a little little section of your code) into a 512byte trunk
- even you can run 64bit right from MBR, you still have to load more
code from hard disk, and since you can't access BIOS interrupts, problems arise
- 64bit code is a whole different thing than 16bit, 32bit codes, so
people usually let it start at an even memory address, like being right after MBR (at 7E00h), or wherever else.
--Paul84 11:48, 5 June 2011 (UTC)
- This is proof-of-concept code, not an OS kernel or anything like that.Oculusfervoris 15:58, 10 June 2011 (UTC)
- I agree. It should be "as minimal as possible" (so that the main concept is shown without any unnecessary stuff in the way); but it should also have warnings saying that it's deliberately "as minimal as possible", potentially including reasons why it's unusable "as is" for an OS (as per Paul84's comments). With this in mind, I'd be tempted to rip out the (unnecessary) "enable A20" code and most of the unnecessary segment loads from Oculusfervoris' version and use that. Brendan 22:43, 10 June 2011 (UTC)
- I tried fixing the article - making it as minimal as possible, and even including a example boot sector. --Shikhin 12:38, 10 July 2011 (UTC)
It seems the SS selector must be writable (at least on some CPUs)
My CPU crashed on the "MOV SS,AX" instruction in long mode. I found out the reason was that the section
.data equ $ - gdt dq 0x0000900000000000 ^
was not writeable in the GDT!!! Changing the code to:
.data equ $ - gdt dq 0x0000930000000000 ^
resolved the problem. Maybe the code in the article must be changed in this way.
-- Martinro 18:35, 10 November 2011 (UTC)
Setting CR4.PAE before loading CR3
The Intel manual (http://www.intel.com/Assets/ja_JP/PDF/manual/253668.pdf) says on page 4-17:
- If PAE paging would be in use following an execution of MOV to CR0 or MOV to CR4 (see Section 4.1.1) and the instruction is modifying any of CR0.CD, CR0.NW, CR0.PG, CR4.PAE, CR4.PGE, or CR4.PSE; then the PDPTEs are loaded from the address in CR3
- If MOV to CR3 is executed while the logical processor is using PAE paging, the PDPTEs are loaded from the address being loaded into CR3.
Since you don't know the current value in CR3, wouldn't it be better to load CR3 before changing CR4 to enable PAE? Else the CPU could e.g. be accessing a non-existing physical memory location