* better
[mascara-docs.git] / amd64 / bareMetalOS-0.5.2 / pure64.boot0 / src / pure64.asm
blobea06e1ff26bdd65f875a0145ba67f9f71e25f6e6
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
8 ; jump to it!
9 ; =============================================================================
12 USE16
13 ORG 0x00008000
14 start:
15 cli ; Disable all interrupts
16 xor eax, eax
17 xor ebx, ebx
18 xor ecx, ecx
19 xor edx, edx
20 xor esi, esi
21 xor edi, edi
22 xor ebp, ebp
23 mov ds, ax
24 mov es, ax
25 mov ss, ax
26 mov fs, ax
27 mov gs, ax
28 mov esp, 0x8000 ; Set a known free location for the stack
30 align 16
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
35 align 16
36 db '16'
37 align 16
39 USE16
40 start16:
41 jmp 0x0000:clearcs
43 clearcs:
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)
46 jne no_mbr
47 mov byte [cfg_mbr], 1 ; Set for booting from a disk with a MBR
48 no_mbr:
50 ; Make sure the screen is set to 80x25 color text mode
51 mov ax, 0x0003 ; Set to normal (80x25 text) video mode
52 int 0x10
54 ; Hide the cursor
55 ; mov ax, 0x0100
56 ; mov cx, 0x200F
57 ; int 0x10
59 ; Print message
60 mov si, initStartupMsg
61 call print_string_16
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
78 mov eax, cr0
79 or al, 0x01 ; Set protected mode bit
80 mov cr0, eax
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
86 pusha
87 mov ah, 0x0E ; int 0x10 teletype function
88 .repeat:
89 lodsb ; Get char from string
90 cmp al, 0
91 je .done ; If char is zero, end of string
92 int 0x10 ; Otherwise, print it
93 jmp short .repeat
94 .done:
95 popa
96 ret
98 ; Display an error message that the CPU does not support 64-bit mode
99 no_long_mode:
100 mov si, no64msg
101 call print_string_16
102 jmp $
104 %include "init_isa.asm"
106 align 16
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
111 align 16
112 gdt32:
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
116 gdt32_end:
118 align 16
119 db '32'
120 align 16
123 ; =============================================================================
124 ; 32-bit mode
125 USE32
127 start32:
128 mov eax, 16 ; load 4 GB data descriptor
129 mov ds, ax ; to all data segment registers
130 mov es, ax
131 mov fs, ax
132 mov gs, ax
133 mov ss, ax
134 xor eax, eax
135 xor ebx, ebx
136 xor ecx, ecx
137 xor edx, edx
138 xor esi, esi
139 xor edi, edi
140 xor ebp, ebp
141 mov esp, 0x8000 ; Set a known free location for the stack
143 ; Debug
144 mov al, '1' ; Now in 32-bit protected mode
145 mov [0x000B809C], al
146 mov al, '0'
147 mov [0x000B809E], al
149 ; Clear out the first 4096 bytes of memory. This will store the 64-bit IDT, GDT, PML4, and PDP
150 mov ecx, 1024
151 xor eax, eax
152 mov edi, eax
153 rep stosd
155 ; Clear memory for the Page Descriptor Entries (0x10000 - 0x4FFFF)
156 mov edi, 0x00010000
157 mov ecx, 65536
158 rep stosd
160 ; Copy the GDT to its final location in memory
161 mov esi, gdt64
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
172 mov eax, 0x00003007
173 stosd
174 xor eax, eax
175 stosd
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
179 stosd
180 xor eax, eax
181 stosd
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
187 mov edi, 0x00003000
188 mov eax, 0x00010007 ; location of first PD
189 create_pdpe:
190 stosd
191 push eax
192 xor eax, eax
193 stosd
194 pop eax
195 add eax, 0x00001000 ; 4K later (512 records x 8 bytes)
196 dec ecx
197 cmp ecx, 0
198 jne create_pdpe
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
203 mov edi, 0x00010000
204 mov eax, 0x0000008F ; Bit 7 must be set to 1 as we have 2 MiB pages
205 xor ecx, ecx
206 pd_again: ; Create a 2 MiB page
207 stosd
208 push eax
209 xor eax, eax
210 stosd
211 pop eax
212 add eax, 0x00200000
213 inc ecx
214 cmp ecx, 2048
215 jne pd_again ; Create 2048 2 MiB page maps.
217 ; Load the GDT
218 lgdt [GDTR64]
220 ; Enable physical-address extensions (set CR4.PAE=1)
221 mov eax, cr4
222 or eax, 0x000000020 ; PAE (Bit 5)
223 mov cr4, eax
225 ; Point cr3 at PML4
226 mov eax, 0x00002008 ; Write-thru (Bit 3)
227 mov cr3, eax
229 ; Enable long mode (set EFER.LME=1)
230 mov ecx, 0xC0000080 ; EFER MSR number
231 rdmsr ; Read EFER
232 or eax, 0x00000100 ; LME (Bit 8)
233 wrmsr ; Write EFER
235 ; Debug
236 mov al, '1' ; About to make the jump into 64-bit mode
237 mov [0x000B809C], al
238 mov al, 'E'
239 mov [0x000B809E], al
241 ; Enable paging to activate long mode (set CR0.PG=1)
242 mov eax, cr0
243 or eax, 0x80000000 ; PG (Bit 31)
244 mov cr0, eax
246 jmp SYS64_CODE_SEL:start64 ; Jump to 64-bit mode
248 align 16
249 db '64'
250 align 16
253 ; =============================================================================
254 ; 64-bit mode
255 USE64
257 start64:
258 ; Debug
259 mov al, '2' ; Now in 64-bit mode
260 mov [0x000B809C], al
261 mov al, '0'
262 mov [0x000B809E], al
264 mov al, 2
265 mov ah, 22
266 call os_move_cursor
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
276 xor r8, r8
277 xor r9, r9
278 xor r10, r10
279 xor r11, r11
280 xor r12, r12
281 xor r13, r13
282 xor r14, r14
283 xor r15, r15
285 mov ds, ax ; Clear the legacy segment registers
286 mov es, ax
287 mov ss, ax
288 mov fs, ax
289 mov gs, ax
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
294 clearcs64:
295 xor rax, rax
297 lgdt [GDTR64] ; Reload the GDT
299 ; Debug
300 mov al, '2'
301 mov [0x000B809C], al
302 mov al, '2'
303 mov [0x000B809E], al
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
308 stosd
310 ; Build the rest of the page tables (4GiB+)
311 mov rcx, 0x0000000000000000
312 mov rax, 0x000000010000008F
313 mov rdi, 0x0000000000014000
314 buildem:
315 stosq
316 add rax, 0x0000000000200000
317 add rcx, 1
318 cmp rcx, 30720 ; Another 60 GiB (We already mapped 4 GiB)
319 jne buildem
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)
325 mov rcx, 32
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
332 mov ax, 0x8E00
333 stosw ; store exception gate marker
334 pop rax ; get the exception gate back
335 shr rax, 16
336 stosw ; store the high word (31..16) of the address
337 shr rax, 16
338 stosd ; store the extra high dword (63..32) of the address.
339 xor rax, rax
340 stosd ; reserved
341 dec rcx
342 jnz make_exception_gates
344 mov rcx, 256-32
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
351 mov ax, 0x8F00
352 stosw ; store interrupt gate marker
353 pop rax ; get the interrupt gate back
354 shr rax, 16
355 stosw ; store the high word (31..16) of the address
356 shr rax, 16
357 stosd ; store the extra high dword (63..32) of the address.
358 xor rax, rax
359 stosd ; reserved
360 dec rcx
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
387 mov rax, timer
388 call create_gate
390 lidt [IDTR64] ; load IDT register
392 ; Debug
393 mov al, '2'
394 mov [0x000B809C], al
395 mov al, '4'
396 mov [0x000B809E], al
398 ; Clear memory 0xf000 - 0xf7ff for the infomap (2048 bytes)
399 xor rax, rax
400 mov rcx, 256
401 mov rdi, 0x000000000000F000
402 clearmapnext:
403 stosq
404 dec rcx
405 cmp rcx, 0
406 jne clearmapnext
408 call init_cpu ; Setup CPU
410 ; Debug
411 mov al, '2' ; CPU Init complete
412 mov [0x000B809C], al
413 mov al, '6'
414 mov [0x000B809E], al
416 ; Make sure exceptions are working.
417 ; xor rax, rax
418 ; xor rbx, rbx
419 ; xor rcx, rcx
420 ; xor rdx, rdx
421 ; div rax
423 call hdd_setup ; Gather Hard Drive information
425 ; Debug
426 mov al, '2' ; HDD Init complete
427 mov [0x000B809C], al
428 mov al, '8'
429 mov [0x000B809E], al
431 ; Find init64.cfg
432 ; mov rbx, configname
433 ; call findfile
434 ; cmp rbx, 0
435 ; je near noconfig ; If the config file was not found we just use the default settings.
436 mov al, 1
437 mov byte [cfg_default], al ; We have a config file
439 ; Read in the first cluster of init64.cfg
440 ; mov rdi, 0x0000000000100000
441 ; call readcluster
443 ; Parse init64.cfg
444 ; Get Kernel name
445 ; get SMP setting
447 ; noconfig:
449 ; Init of SMP
450 call smp_setup
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
461 ; Debug
462 mov al, '3' ; SMP Init complete
463 mov [0x000B809C], al
464 mov al, 'E'
465 mov [0x000B809E], al
467 ; Calculate amount of usable RAM from Memory Map
468 xor rcx, rcx
469 mov rsi, 0x0000000000004000 ; E820 Map location
470 readnextrecord:
471 lodsq
472 lodsq
473 lodsd
474 cmp eax, 0 ; Are we at the end?
475 je endmemcalc
476 cmp eax, 1 ; Usuable RAM
477 je goodmem
478 cmp eax, 3 ; ACPI Reclaimable
479 je goodmem
480 cmp eax, 6 ; BIOS Reclaimable
481 je goodmem
482 lodsd
483 lodsq
484 jmp readnextrecord
485 goodmem:
486 sub rsi, 12
487 lodsq
488 add rcx, rax
489 lodsq
490 lodsq
491 jmp readnextrecord
493 endmemcalc:
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
500 xor rax, rax
501 mov ax, [cpu_speed]
502 mov rdi, speedtempstring
503 call os_int_to_string
505 ; Convert CPU amount value to string
506 xor rax, rax
507 mov ax, [cpu_activated]
508 mov rdi, cpu_amount_string
509 call os_int_to_string
511 ; Convert RAM amount value to string
512 xor rax, rax
513 mov ax, [mem_amount]
514 mov rdi, memtempstring
515 call os_int_to_string
517 ; Build the infomap
518 mov rdi, 0x0000000000005000
519 mov rax, [os_LocalAPICAddress]
520 stosq
521 mov rax, [os_IOAPICAddress]
522 stosq
524 mov rdi, 0x0000000000005010
525 mov ax, [cpu_speed]
526 stosw
527 mov ax, [cpu_activated]
528 stosw
529 mov ax, [cpu_detected]
530 stosw
532 mov rdi, 0x0000000000005020
533 mov ax, [mem_amount]
534 stosw
536 mov rdi, 0x0000000000005030
537 mov al, [cfg_mbr]
538 stosb
540 mov rdi, 0x0000000000005040
541 mov rax, [os_ACPITableAddress]
542 stosq
544 mov rdi, 0x0000000000005050
545 mov eax, [VBEModeInfoBlock.PhysBasePtr]
546 stosd
547 mov ax, [VBEModeInfoBlock.XResolution]
548 stosw
549 mov ax, [VBEModeInfoBlock.YResolution]
550 stosw
552 ; Initialization is now complete... write a message to the screen
553 mov rsi, msg_done
554 call os_print_string
556 ; Write an extra message if we are using the default config
557 cmp byte [cfg_default], 1
558 je nodefaultconfig
559 mov al, 2
560 mov ah, 28
561 call os_move_cursor
562 mov rsi, msg_noconfig
563 call os_print_string
564 nodefaultconfig:
566 ; Print info on CPU, MEM, and HD
567 mov ax, 0x0004
568 call os_move_cursor
569 mov rsi, msg_CPU
570 call os_print_string
571 mov rsi, speedtempstring
572 call os_print_string
573 mov rsi, msg_mhz
574 call os_print_string
575 mov rsi, cpu_amount_string
576 call os_print_string
578 mov rsi, msg_MEM
579 call os_print_string
580 mov rsi, memtempstring
581 call os_print_string
582 mov rsi, msg_mb
583 call os_print_string
585 mov rsi, msg_HDD
586 call os_print_string
587 mov rsi, hdtempstring
588 call os_print_string
589 mov rsi, msg_mb
590 call os_print_string
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
606 mov ax, 0x0006
607 call os_move_cursor
608 mov rsi, msg_loadingkernel
609 call os_print_string
611 ; Find the kernel file
612 mov rsi, kernelname
613 call findfile
614 cmp ax, 0x0000
615 je near nokernel
617 ; Load 64-bit kernel from drive to 0x0000000000010000
618 mov rdi, 0x0000000000100000
619 readfile_getdata:
620 ; push rax
621 ; mov al, '.' ; Show loading progress
622 ; call os_print_char
623 ; pop rax
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
629 mov rsi, msg_done
630 call os_print_string
632 fini: ; For chainloading
634 ; Print a message that the kernel is being started
635 mov ax, 0x0008
636 call os_move_cursor
637 mov rsi, msg_startingkernel
638 call os_print_string
640 ; Debug
641 mov al, ' ' ; Clear the debug messages
642 mov [0x000B809A], al
643 mov [0x000B809C], al
644 mov [0x000B809E], al
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
654 xor r8, r8
655 xor r9, r9
656 xor r10, r10
657 xor r11, r11
658 xor r12, r12
659 xor r13, r13
660 xor r14, r14
661 xor r15, r15
663 jmp 0x0000000000100000 ; Jump to the kernel
665 nokernel:
666 mov al, 6
667 mov ah, 0
668 call os_move_cursor
669 mov rsi, kernelerror
670 call os_print_string
671 jmp $
673 %include "init_cpu.asm"
674 %include "init_hdd.asm"
675 %include "init_smp.asm"
676 %include "syscalls.asm"
677 %include "interrupt.asm"
678 %include "fat16.asm"
679 %include "sysvar.asm"
681 ; Pad to an even KB file (6 KiB)
682 times 6144-($-$$) db 0x90
684 ; =============================================================================
685 ; EOF