1 ; =============================================================================
2 ; Pure64 -- a 64-bit OS loader written in Assembly for x86-64 systems
3 ; Copyright (C) 2008-2012 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 ; =============================================================================
12 ; %define PURE64_CHAIN_LOADING
13 ; If this is defined, Pure64 will chainload the kernel attached to the end of the pure64.sys binary
14 ; Windows - copy /b pure64.sys + kernel64.sys
15 ; Unix - cat pure64.sys kernel64.sys > pure64.sys
16 ; Max size of the resulting pure64.sys is 28672 bytes
22 cli ; Disable all interrupts
35 mov esp, 0x8000 ; Set a known free location for the stack
38 jmp start16
; This command will be overwritten with 'NOP's before the AP's are started
40 %include "init_smp_ap.asm" ; Our AP code is at 0x8000
51 mov ax, [0x07FE] ; MBR sector is copied to 0x0600
52 cmp ax, 0xAA55 ; Check if the word at 0x07FE is set to 0xAA55 (Boot sector marker)
54 mov byte [cfg_mbr
], 1 ; Set for booting from a disk with a MBR
57 ; Configure serial port
58 xor dx, dx ; First serial port
59 mov ax, 0000000011100011b ; 9600 baud, no parity, 1 stop bit, 8 data bits
70 ; Make sure the screen is set to 80x25 color text mode
71 mov ax, 0x0003 ; Set to normal (80x25 text) video mode
75 mov si, initStartupMsg
78 ; Check to make sure the CPU supports 64-bit mode... If not then bail out
79 mov eax, 0x80000000 ; Extended-function 8000000h.
80 cpuid
; Is largest extended function
81 cmp eax, 0x80000000 ; any function > 80000000h?
82 jbe no_long_mode
; If not, no long mode.
83 mov eax, 0x80000001 ; Extended-function 8000001h.
84 cpuid
; Now EDX = extended-features flags.
85 bt edx, 29 ; Test if long mode is supported.
86 jnc no_long_mode
; Exit if not supported.
91 call isa_setup
; Setup legacy hardware
96 ; At this point we are done with real mode and BIOS interrupts. Jump to 32-bit mode.
97 lgdt [cs:GDTR32
] ; Load GDT register
100 ; call serial_send_16
103 or al, 0x01 ; Set protected mode bit
106 jmp 8:start32
; Jump to 32-bit protected mode
108 ; 16-bit function to print a sting to the screen
109 print_string_16: ; Output string in SI to screen
111 mov ah, 0x0E ; int 0x10 teletype function
112 print_string_16_repeat:
113 lodsb ; Get char from string
115 je print_string_16_done
; If char is zero, end of string
116 int 0x10 ; Otherwise, print it
117 jmp print_string_16_repeat
118 print_string_16_done:
122 ; 16-bit function to send a char our via serial
125 ; push eax ; Save EAX since the serial line status check clobbers AL
126 ; mov dx, 0x03FD ; Serial Line Status register
127 ;serial_send_wait_16:
129 ; bt ax, 5 ; Copy bit 5 (THR is empty) to the Carry Flag
130 ; jnc serial_send_wait_16 ; If the bit is not set then the queue isn't ready for another byte
131 ; pop eax ; Get the byte back from the stack
132 ; mov dx, 0x03F8 ; Serial data register
133 ; out dx, al ; Send the byte
137 ; Display an error message that the CPU does not support 64-bit mode
150 in al, 0x64 ; wait for key pressed
154 int 0xff ; reboot by causing a triple fault
157 %include "init_isa.asm"
160 GDTR32: ; Global Descriptors Table Register
161 dw gdt32_end
- gdt32
- 1 ; limit of GDT (size minus one)
162 dq gdt32
; linear address of GDT
166 dw 0x0000, 0x0000, 0x0000, 0x0000 ; Null desciptor
167 dw 0xFFFF, 0x0000, 0x9A00, 0x00CF ; 32-bit code desciptor
168 dw 0xFFFF, 0x0000, 0x9200, 0x00CF ; 32-bit data desciptor
176 ; =============================================================================
181 mov eax, 16 ; load 4 GB data descriptor
182 mov ds, ax ; to all data segment registers
194 mov esp, 0x8000 ; Set a known free location for the stack
197 mov al, '2' ; Now in 32-bit protected mode
202 ; call serial_send_32
204 ; Clear out the first 4096 bytes of memory. This will store the 64-bit IDT, GDT, PML4, and PDP
210 ; Clear memory for the Page Descriptor Entries (0x10000 - 0x4FFFF)
215 ; Copy the GDT to its final location in memory
217 mov edi, 0x00001000 ; GDT address
218 mov ecx, (gdt64_end
- gdt64
)
219 rep movsb ; Move it to final pos.
221 ; Create the Level 4 Page Map. (Maps 4GBs of 2MB pages)
222 ; First create a PML4 entry.
223 ; PML4 is stored at 0x0000000000002000, create the first entry there
224 ; A single PML4 entry can map 512GB with 2MB pages.
226 mov edi, 0x00002000 ; Create a PML4 entry for the first 4GB of RAM
232 mov edi, 0x00002800 ; Create a PML4 entry for higher half (starting at 0xFFFF800000000000)
233 mov eax, 0x00003007 ; The higher half is identity mapped to the lower half
238 ; Create the PDP entries.
239 ; The first PDP is stored at 0x0000000000003000, create the first entries there
240 ; A single PDP entry can map 1GB with 2MB pages
241 mov ecx, 64 ; number of PDPE's to make.. each PDPE maps 1GB of physical memory
243 mov eax, 0x00010007 ; location of first PD
250 add eax, 0x00001000 ; 4K later (512 records x 8 bytes)
255 ; Create the PD entries.
256 ; PD entries are stored starting at 0x0000000000010000 and ending at 0x000000000004FFFF (256 KiB)
257 ; This gives us room to map 64 GiB with 2 MiB pages
259 mov eax, 0x0000008F ; Bit 7 must be set to 1 as we have 2 MiB pages
261 pd_again: ; Create a 2 MiB page
270 jne pd_again
; Create 2048 2 MiB page maps.
273 ; call serial_send_32
279 ; call serial_send_32
281 ; Enable extended properties
283 or eax, 0x0000000B0 ; PGE (Bit 7), PAE (Bit 5), and PSE (Bit 4)
287 ; call serial_send_32
290 mov eax, 0x00002008 ; Write-thru (Bit 3)
294 ; call serial_send_32
296 ; Enable long mode and SYSCALL/SYSRET
297 mov ecx, 0xC0000080 ; EFER MSR number
299 or eax, 0x00000101 ; LME (Bit 8)
303 ; call serial_send_32
306 mov al, '1' ; About to make the jump into 64-bit mode
312 ; call serial_send_32
314 ; Enable paging to activate long mode
316 or eax, 0x80000000 ; PG (Bit 31)
319 jmp SYS64_CODE_SEL:start64
; Jump to 64-bit mode
321 ; 32-bit function to send a char our via serial
324 ; push eax ; Save EAX since the serial line status check clobbers AL
325 ; mov dx, 0x03FD ; Serial Line Status register
326 ;serial_send_wait_32:
328 ; bt ax, 5 ; Copy bit 5 (THR is empty) to the Carry Flag
329 ; jnc serial_send_wait_32 ; If the bit is not set then the queue isn't ready for another byte
330 ; pop eax ; Get the byte back from the stack
331 ; mov dx, 0x03F8 ; Serial data register
332 ; out dx, al ; Send the byte
341 ; =============================================================================
347 mov al, '4' ; Now in 64-bit mode
352 ; call serial_send_64
358 xor rax
, rax
; aka r0
359 xor rbx
, rbx
; aka r3
360 xor rcx
, rcx
; aka r1
361 xor rdx
, rdx
; aka r2
362 xor rsi
, rsi
; aka r6
363 xor rdi
, rdi
; aka r7
364 xor rbp
, rbp
; aka r5
365 mov rsp
, 0x8000 ; aka r4
375 mov ds, ax ; Clear the legacy segment registers
381 mov rax
, clearcs64
; Do a proper 64-bit jump. Should not be needed as the ...
382 jmp rax
; ... jmp SYS64_CODE_SEL:start64 would have sent us ...
383 nop ; .. out of compatibilty mode and into 64-bit mode
387 lgdt [GDTR64
] ; Reload the GDT
393 ; call serial_send_64
395 ; Patch Pure64 AP code ; The AP's will be told to start execution at 0x8000
396 mov edi, 0x00008030 ; We need to remove the BSP Jump call to get the AP's
397 mov eax, 0x90909090 ; to fall through to the AP Init code
400 ; Build the rest of the page tables (4GiB+)
401 mov rcx
, 0x0000000000000000
402 mov rax
, 0x000000010000008F
403 mov rdi
, 0x0000000000014000
406 add rax
, 0x0000000000200000
408 cmp rcx
, 30720 ; Another 60 GiB (We already mapped 4 GiB)
410 ; We have 64 GiB mapped now
412 ; Build a temporary IDT
413 xor rdi
, rdi
; create the 64-bit IDT (at linear address 0x0000000000000000)
416 make_exception_gates: ; make gates for exception handlers
417 mov rax
, exception_gate
418 push rax
; save the exception gate to the stack for later use
419 stosw ; store the low word (15..0) of the address
420 mov ax, SYS64_CODE_SEL
421 stosw ; store the segment selector
423 stosw ; store exception gate marker
424 pop rax
; get the exception gate back
426 stosw ; store the high word (31..16) of the address
428 stosd ; store the extra high dword (63..32) of the address.
432 jnz make_exception_gates
435 make_interrupt_gates: ; make gates for the other interrupts
436 mov rax
, interrupt_gate
437 push rax
; save the interrupt gate to the stack for later use
438 stosw ; store the low word (15..0) of the address
439 mov ax, SYS64_CODE_SEL
440 stosw ; store the segment selector
442 stosw ; store interrupt gate marker
443 pop rax
; get the interrupt gate back
445 stosw ; store the high word (31..16) of the address
447 stosd ; store the extra high dword (63..32) of the address.
451 jnz make_interrupt_gates
453 ; Set up the exception gates for all of the CPU exceptions
454 ; The following code will be seriously busted if the exception gates are moved above 16MB
455 mov word [0x00*16], exception_gate_00
456 mov word [0x01*16], exception_gate_01
457 mov word [0x02*16], exception_gate_02
458 mov word [0x03*16], exception_gate_03
459 mov word [0x04*16], exception_gate_04
460 mov word [0x05*16], exception_gate_05
461 mov word [0x06*16], exception_gate_06
462 mov word [0x07*16], exception_gate_07
463 mov word [0x08*16], exception_gate_08
464 mov word [0x09*16], exception_gate_09
465 mov word [0x0A*16], exception_gate_10
466 mov word [0x0B*16], exception_gate_11
467 mov word [0x0C*16], exception_gate_12
468 mov word [0x0D*16], exception_gate_13
469 mov word [0x0E*16], exception_gate_14
470 mov word [0x0F*16], exception_gate_15
471 mov word [0x10*16], exception_gate_16
472 mov word [0x11*16], exception_gate_17
473 mov word [0x12*16], exception_gate_18
474 mov word [0x13*16], exception_gate_19
476 mov rdi
, 0x21 ; Set up Keyboard IRQ handler
479 mov rdi
, 0x28 ; Set up RTC IRQ handler
482 mov rdi
, 0xF8 ; Set up Spurious handler
486 lidt [IDTR64
] ; load IDT register
492 ; call serial_send_64
494 ; Clear memory 0xf000 - 0xf7ff for the infomap (2048 bytes)
497 mov rdi
, 0x000000000000F000
504 call init_acpi
; Find and process the ACPI tables
506 call init_cpu
; Setup CPU
508 call init_ioapic
; Setup the IO-APIC(s), also activate interrupts
511 mov al, '6' ; CPU Init complete
514 ; call serial_send_64
516 ; Make sure exceptions are working.
523 call hdd_setup
; Gather Hard Drive information
526 mov al, '8' ; HDD Init complete
529 ; call serial_send_64
532 ; mov rbx, configname
535 ; je near noconfig ; If the config file was not found we just use the default settings.
537 mov byte [cfg_default
], al ; We have a config file
539 ; Read in the first cluster of init64.cfg
540 ; mov rdi, 0x0000000000100000
552 ; Reset the stack to the proper location (was set to 0x8000 previously)
553 mov rsi
, [os_LocalAPICAddress
] ; We would call os_smp_get_id here but the stack is not ...
554 add rsi
, 0x20 ; ... yet defined. It is safer to find the value directly.
555 lodsd ; Load a 32-bit value. We only want the high 8 bits
556 shr rax
, 24 ; Shift to the right and AL now holds the CPU's APIC ID
557 shl rax
, 10 ; shift left 10 bits for a 1024byte stack
558 add rax
, 0x0000000000050400 ; stacks decrement when you "push", start at 1024 bytes in
559 mov rsp
, rax
; Pure64 leaves 0x50000-0x9FFFF free so we use that
562 mov al, '6' ; SMP Init complete
567 ; call serial_send_64
569 ; Calculate amount of usable RAM from Memory Map
571 mov rsi
, 0x0000000000004000 ; E820 Map location
576 cmp eax, 0 ; Are we at the end?
578 cmp eax, 1 ; Usuable RAM
580 cmp eax, 3 ; ACPI Reclaimable
582 cmp eax, 6 ; BIOS Reclaimable
596 shr rcx
, 20 ; Value is in bytes so do a quick divide by 1048576 to get MiB's
597 add cx, 1 ; The BIOS will usually report actual memory minus 1
598 and cx, 0xFFFE ; Make sure it is an even number (in case we added 1 to an even number)
599 mov word [mem_amount
], cx
605 ; Convert CPU speed value to string
608 mov rdi
, speedtempstring
609 call os_int_to_string
611 ; Convert CPU amount value to string
613 mov ax, [cpu_activated
]
614 mov rdi
, cpu_amount_string
615 call os_int_to_string
617 ; Convert RAM amount value to string
620 mov rdi
, memtempstring
621 call os_int_to_string
626 mov rax
, [os_ACPITableAddress
]
634 mov ax, [cpu_activated
]
636 mov ax, [cpu_detected
]
646 mov al, [os_IOAPICCount
]
650 mov eax, [VBEModeInfoBlock.PhysBasePtr
]
652 mov ax, [VBEModeInfoBlock.XResolution
]
654 mov ax, [VBEModeInfoBlock.YResolution
]
658 mov rax
, [os_LocalAPICAddress
]
661 mov cl, [os_IOAPICCount
]
662 mov rsi
, os_IOAPICAddress
670 ; Initialization is now complete... write a message to the screen
674 ; Write an extra message if we are using the default config
675 cmp byte [cfg_default
], 1
680 mov rsi
, msg_noconfig
688 ; Print info on CPU, MEM, and HD
693 mov rsi
, speedtempstring
697 mov rsi
, cpu_amount_string
702 mov rsi
, memtempstring
709 mov rsi
, hdtempstring
714 ; =============================================================================
715 %ifdef PURE64_CHAIN_LOADING
716 mov rsi
, 0x8000+7168 ; Memory offset to end of pure64.sys
717 mov rdi
, 0x100000 ; Destination address at the 1MiB mark
718 mov rcx
, 0x1000 ; For up to 32KiB kernel (4096 x 8)
719 rep movsq
; Copy 8 bytes at a time
720 jmp fini
; Print starting message and jump to kernel
722 ; =============================================================================
724 ; Print a message that the kernel is being loaded
727 mov rsi
, msg_loadingkernel
730 ; Find the kernel file
742 ; Load 64-bit kernel from drive to 0x0000000000010000
743 mov rdi
, 0x0000000000100000
746 mov al, '.' ; Show loading progress
749 call readcluster
; store in memory
750 cmp ax, 0xFFFF ; Value for end of cluster chain.
751 jne readfile_getdata
; Are there more clusters? If so then read again.. if not fall through.
753 ; Print a message that the kernel has been loaded
757 fini: ; For chainloading
759 ; Print a message that the kernel is being started
762 mov rsi
, msg_startingkernel
766 ; mov al, ' ' ; Clear the debug messages
767 ; mov [0x000B809A], al
768 ; mov [0x000B809C], al
769 ; mov [0x000B809E], al
772 ; call serial_send_64
774 ; Clear all registers (skip the stack pointer)
775 xor rax
, rax
; aka r0
776 xor rbx
, rbx
; aka r3
777 xor rcx
, rcx
; aka r1
778 xor rdx
, rdx
; aka r2
779 xor rsi
, rsi
; aka r6
780 xor rdi
, rdi
; aka r7
781 xor rbp
, rbp
; aka r5
791 jmp 0x0000000000100000 ; Jump to the kernel
803 ; 64-bit function to send a char our via serial
806 ; push rax ; Save RAX since the serial line status check clobbers AL
807 ; mov dx, 0x03FD ; Serial Line Status register
808 ;serial_send_wait_64:
810 ; bt ax, 5 ; Copy bit 5 (THR is empty) to the Carry Flag
811 ; jnc serial_send_wait_64 ; If the bit is not set then the queue isn't ready for another byte
812 ; pop rax ; Get the byte back from the stack
813 ; mov dx, 0x03F8 ; Serial data register
814 ; out dx, al ; Send the byte
818 %include "init_cpu.asm"
819 %include "init_acpi.asm"
820 %include "init_ioapic.asm"
821 %include "init_hdd.asm"
822 %include "init_smp.asm"
823 %include "syscalls.asm"
824 %include "interrupt.asm"
827 %include "sysvar.asm"
829 ; Pad to an even KB file (6 KiB)
830 times
7168-($
-$$
) db 0x90
832 ; =============================================================================