1 ; =============================================================================
2 ; Pure64 -- a 64-bit OS loader written in Assembly for x86-64 systems
3 ; Copyright (C) 2008-2011 Return Infinity -- see LICENSE.TXT
5 ; Loaded from the first stage. Gather information about the system while
6 ; in 16-bit mode (BIOS is still accessable), setup a minimal 64-bit
7 ; enviroment, load the 64-bit kernel from the filesystem into memory and
9 ; =============================================================================
15 cli ; Disable all interrupts
28 mov esp, 0x8000 ; Set a known free location for the stack
31 jmp start16
; This command will be overwritten with 'NOP's before the AP's are started
33 %include "init_smp_ap.asm" ; Our AP code is at 0x8000
44 mov ax, [0x07FE] ; MBR sector is copied to 0x0600
45 cmp ax, 0xAA55 ; Check if the word at 0x07FE is set to 0xAA55 (Boot sector marker)
47 mov byte [cfg_mbr
], 1 ; Set for booting from a disk with a MBR
50 ; Make sure the screen is set to 80x25 color text mode
51 mov ax, 0x0003 ; Set to normal (80x25 text) video mode
60 mov si, initStartupMsg
63 ; Check to make sure the CPU supports 64-bit mode... If not then bail out
64 mov eax, 0x80000000 ; Extended-function 8000000h.
65 cpuid
; Is largest extended function
66 cmp eax, 0x80000000 ; any function > 80000000h?
67 jbe near no_long_mode
; If not, no long mode.
68 mov eax, 0x80000001 ; Extended-function 8000001h.
69 cpuid
; Now EDX = extended-features flags.
70 bt edx, 29 ; Test if long mode is supported.
71 jnc near no_long_mode
; Exit if not supported.
73 call isa_setup
; Setup legacy hardware
75 ; At this point we are done with real mode and BIOS interrupts. Jump to 32-bit mode.
76 lgdt [cs:GDTR32
] ; Load GDT register
79 or al, 0x01 ; Set protected mode bit
82 jmp 8:start32
; Jump to 32-bit protected mode
84 ; 16-bit Function to print a sting to the screen
85 print_string_16: ; Output string in SI to screen
87 mov ah, 0x0E ; int 0x10 teletype function
89 lodsb ; Get char from string
91 je .done
; If char is zero, end of string
92 int 0x10 ; Otherwise, print it
98 ; Display an error message that the CPU does not support 64-bit mode
104 %include "init_isa.asm"
107 GDTR32: ; Global Descriptors Table Register
108 dw gdt32_end
- gdt32
- 1 ; limit of GDT (size minus one)
109 dq gdt32
; linear address of GDT
113 dw 0x0000, 0x0000, 0x0000, 0x0000 ; Null desciptor
114 dw 0xFFFF, 0x0000, 0x9A00, 0x00CF ; 32-bit code desciptor
115 dw 0xFFFF, 0x0000, 0x9200, 0x008F ; 32-bit data desciptor
123 ; =============================================================================
128 mov eax, 16 ; load 4 GB data descriptor
129 mov ds, ax ; to all data segment registers
141 mov esp, 0x8000 ; Set a known free location for the stack
144 mov al, '1' ; Now in 32-bit protected mode
149 ; Clear out the first 4096 bytes of memory. This will store the 64-bit IDT, GDT, PML4, and PDP
155 ; Clear memory for the Page Descriptor Entries (0x10000 - 0x4FFFF)
160 ; Copy the GDT to its final location in memory
162 mov edi, 0x00001000 ; GDT address
163 mov ecx, (gdt64_end
- gdt64
)
164 rep movsb ; Move it to final pos.
166 ; Create the Level 4 Page Map. (Maps 4GBs of 2MB pages)
167 ; First create a PML4 entry.
168 ; PML4 is stored at 0x0000000000002000, create the first entry there
169 ; A single PML4 entry can map 512GB with 2MB pages.
171 mov edi, 0x00002000 ; Create a PML4 entry for the first 4GB of RAM
177 mov edi, 0x00002800 ; Create a PML4 entry for higher half (starting at 0xFFFF800000000000)
178 mov eax, 0x00003007 ; The higher half is identity mapped to the lower half
183 ; Create the PDP entries.
184 ; The first PDP is stored at 0x0000000000003000, create the first entries there
185 ; A single PDP entry can map 1GB with 2MB pages
186 mov ecx, 64 ; number of PDPE's to make.. each PDPE maps 1GB of physical memory
188 mov eax, 0x00010007 ; location of first PD
195 add eax, 0x00001000 ; 4K later (512 records x 8 bytes)
200 ; Create the PD entries.
201 ; PD entries are stored starting at 0x0000000000010000 and ending at 0x000000000004FFFF (256 KiB)
202 ; This gives us room to map 64 GiB with 2 MiB pages
204 mov eax, 0x0000008F ; Bit 7 must be set to 1 as we have 2 MiB pages
206 pd_again: ; Create a 2 MiB page
215 jne pd_again
; Create 2048 2 MiB page maps.
220 ; Enable physical-address extensions (set CR4.PAE=1)
222 or eax, 0x000000020 ; PAE (Bit 5)
226 mov eax, 0x00002008 ; Write-thru (Bit 3)
229 ; Enable long mode (set EFER.LME=1)
230 mov ecx, 0xC0000080 ; EFER MSR number
232 or eax, 0x00000100 ; LME (Bit 8)
236 mov al, '1' ; About to make the jump into 64-bit mode
241 ; Enable paging to activate long mode (set CR0.PG=1)
243 or eax, 0x80000000 ; PG (Bit 31)
246 jmp SYS64_CODE_SEL:start64
; Jump to 64-bit mode
253 ; =============================================================================
259 mov al, '2' ; Now in 64-bit mode
268 xor rax
, rax
; aka r0
269 xor rbx
, rbx
; aka r3
270 xor rcx
, rcx
; aka r1
271 xor rdx
, rdx
; aka r2
272 xor rsi
, rsi
; aka r6
273 xor rdi
, rdi
; aka r7
274 xor rbp
, rbp
; aka r5
275 mov rsp
, 0x8000 ; aka r4
285 mov ds, ax ; Clear the legacy segment registers
291 mov rax
, clearcs64
; Do a proper 64-bit jump. Should not be needed as the ...
292 jmp rax
; ... jmp SYS64_CODE_SEL:start64 would have sent us ...
293 nop ; .. out of compatibilty mode and into 64-bit mode
297 lgdt [GDTR64
] ; Reload the GDT
305 ; Patch Pure64 AP code ; The AP's will be told to start execution at 0x8000
306 mov edi, 0x00008030 ; We need to remove the BSP Jump call to get the AP's
307 mov eax, 0x90909090 ; to fall through to the AP Init code
310 ; Build the rest of the page tables (4GiB+)
311 mov rcx
, 0x0000000000000000
312 mov rax
, 0x000000010000008F
313 mov rdi
, 0x0000000000014000
316 add rax
, 0x0000000000200000
318 cmp rcx
, 30720 ; Another 60 GiB (We already mapped 4 GiB)
320 ; We have 64 GiB mapped now
322 ; Build a temporary IDT
323 xor rdi
, rdi
; create the 64-bit IDT (at linear address 0x0000000000000000)
326 make_exception_gates: ; make gates for exception handlers
327 mov rax
, exception_gate
328 push rax
; save the exception gate to the stack for later use
329 stosw ; store the low word (15..0) of the address
330 mov ax, SYS64_CODE_SEL
331 stosw ; store the segment selector
333 stosw ; store exception gate marker
334 pop rax
; get the exception gate back
336 stosw ; store the high word (31..16) of the address
338 stosd ; store the extra high dword (63..32) of the address.
342 jnz make_exception_gates
345 make_interrupt_gates: ; make gates for the other interrupts
346 mov rax
, interrupt_gate
347 push rax
; save the interrupt gate to the stack for later use
348 stosw ; store the low word (15..0) of the address
349 mov ax, SYS64_CODE_SEL
350 stosw ; store the segment selector
352 stosw ; store interrupt gate marker
353 pop rax
; get the interrupt gate back
355 stosw ; store the high word (31..16) of the address
357 stosd ; store the extra high dword (63..32) of the address.
361 jnz make_interrupt_gates
363 ; Set up the exception gates for all of the CPU exceptions
364 ; The following code will be seriously busted if the exception gates are moved above 16MB
365 mov word [0x00*16], exception_gate_00
366 mov word [0x01*16], exception_gate_01
367 mov word [0x02*16], exception_gate_02
368 mov word [0x03*16], exception_gate_03
369 mov word [0x04*16], exception_gate_04
370 mov word [0x05*16], exception_gate_05
371 mov word [0x06*16], exception_gate_06
372 mov word [0x07*16], exception_gate_07
373 mov word [0x08*16], exception_gate_08
374 mov word [0x09*16], exception_gate_09
375 mov word [0x0A*16], exception_gate_10
376 mov word [0x0B*16], exception_gate_11
377 mov word [0x0C*16], exception_gate_12
378 mov word [0x0D*16], exception_gate_13
379 mov word [0x0E*16], exception_gate_14
380 mov word [0x0F*16], exception_gate_15
381 mov word [0x10*16], exception_gate_16
382 mov word [0x11*16], exception_gate_17
383 mov word [0x12*16], exception_gate_18
384 mov word [0x13*16], exception_gate_19
386 mov rdi
, 0x20 ; Set up Timer IRQ handler
390 lidt [IDTR64
] ; load IDT register
398 ; Clear memory 0xf000 - 0xf7ff for the infomap (2048 bytes)
401 mov rdi
, 0x000000000000F000
408 call init_cpu
; Setup CPU
411 mov al, '2' ; CPU Init complete
416 ; Make sure exceptions are working.
423 call hdd_setup
; Gather Hard Drive information
426 mov al, '2' ; HDD Init complete
432 ; mov rbx, configname
435 ; je near noconfig ; If the config file was not found we just use the default settings.
437 mov byte [cfg_default
], al ; We have a config file
439 ; Read in the first cluster of init64.cfg
440 ; mov rdi, 0x0000000000100000
452 ; Reset the stack to the proper location (was set to 0x8000 previously)
453 mov rsi
, [os_LocalAPICAddress
] ; We would call os_smp_get_id here but the stack is not ...
454 add rsi
, 0x20 ; ... yet defined. It is safer to find the value directly.
455 lodsd ; Load a 32-bit value. We only want the high 8 bits
456 shr rax
, 24 ; Shift to the right and AL now holds the CPU's APIC ID
457 shl rax
, 10 ; shift left 10 bits for a 1024byte stack
458 add rax
, 0x0000000000050400 ; stacks decrement when you "push", start at 1024 bytes in
459 mov rsp
, rax
; Pure64 leaves 0x50000-0x9FFFF free so we use that
462 mov al, '3' ; SMP Init complete
467 ; Calculate amount of usable RAM from Memory Map
469 mov rsi
, 0x0000000000004000 ; E820 Map location
474 cmp eax, 0 ; Are we at the end?
476 cmp eax, 1 ; Usuable RAM
478 cmp eax, 3 ; ACPI Reclaimable
480 cmp eax, 6 ; BIOS Reclaimable
494 shr rcx
, 20 ; Value is in bytes so do a quick divide by 1048576 to get MiB's
495 add cx, 1 ; The BIOS will usually report actual memory minus 1
496 and cx, 0xFFFE ; Make sure it is an even number (in case we added 1 to an even number)
497 mov word [mem_amount
], cx
499 ; Convert CPU speed value to string
502 mov rdi
, speedtempstring
503 call os_int_to_string
505 ; Convert CPU amount value to string
507 mov ax, [cpu_activated
]
508 mov rdi
, cpu_amount_string
509 call os_int_to_string
511 ; Convert RAM amount value to string
514 mov rdi
, memtempstring
515 call os_int_to_string
518 mov rdi
, 0x0000000000005000
519 mov rax
, [os_LocalAPICAddress
]
521 mov rax
, [os_IOAPICAddress
]
524 mov rdi
, 0x0000000000005010
527 mov ax, [cpu_activated
]
529 mov ax, [cpu_detected
]
532 mov rdi
, 0x0000000000005020
536 mov rdi
, 0x0000000000005030
540 mov rdi
, 0x0000000000005040
541 mov rax
, [os_ACPITableAddress
]
544 mov rdi
, 0x0000000000005050
545 mov eax, [VBEModeInfoBlock.PhysBasePtr
]
547 mov ax, [VBEModeInfoBlock.XResolution
]
549 mov ax, [VBEModeInfoBlock.YResolution
]
552 ; Initialization is now complete... write a message to the screen
556 ; Write an extra message if we are using the default config
557 cmp byte [cfg_default
], 1
562 mov rsi
, msg_noconfig
566 ; Print info on CPU, MEM, and HD
571 mov rsi
, speedtempstring
575 mov rsi
, cpu_amount_string
580 mov rsi
, memtempstring
587 mov rsi
, hdtempstring
592 ; =============================================================================
593 ; Chainload the kernel attached to the end of the pure64.sys binary
594 ; Windows - copy /b pure64.sys + kernel64.sys
595 ; Unix - cat pure64.sys kernel64.sys > pure64.sys
596 ; Max size of the resulting pure64.sys is 28672 bytes
597 ; Uncomment the following 5 lines if you are chainloading
598 ; mov rsi, 0x8000+6144 ; Memory offset to end of pure64.sys
599 ; mov rdi, 0x100000 ; Destination address at the 1MiB mark
600 ; mov rcx, 0x800 ; For a 16KiB kernel (2048 x 8)
601 ; rep movsq ; Copy 8 bytes at a time
602 ; jmp fini ; Print starting message and jump to kernel
603 ; =============================================================================
605 ; Print a message that the kernel is being loaded
608 mov rsi
, msg_loadingkernel
611 ; Find the kernel file
617 ; Load 64-bit kernel from drive to 0x0000000000010000
618 mov rdi
, 0x0000000000100000
621 ; mov al, '.' ; Show loading progress
624 call readcluster
; store in memory
625 cmp ax, 0xFFFF ; Value for end of cluster chain.
626 jne readfile_getdata
; Are there more clusters? If so then read again.. if not fall through.
628 ; Print a message that the kernel has been loaded
632 fini: ; For chainloading
634 ; Print a message that the kernel is being started
637 mov rsi
, msg_startingkernel
641 mov al, ' ' ; Clear the debug messages
646 ; Clear all registers (skip the stack pointer)
647 xor rax
, rax
; aka r0
648 xor rbx
, rbx
; aka r3
649 xor rcx
, rcx
; aka r1
650 xor rdx
, rdx
; aka r2
651 xor rsi
, rsi
; aka r6
652 xor rdi
, rdi
; aka r7
653 xor rbp
, rbp
; aka r5
663 jmp 0x0000000000100000 ; Jump to the kernel
673 %include "init_cpu.asm"
674 %include "init_hdd.asm"
675 %include "init_smp.asm"
676 %include "syscalls.asm"
677 %include "interrupt.asm"
679 %include "sysvar.asm"
681 ; Pad to an even KB file (6 KiB)
682 times
6144-($
-$$
) db 0x90
684 ; =============================================================================