diff --git a/build.sh b/build.sh
index 8606352..5f643b9 100755
--- a/build.sh
+++ b/build.sh
@@ -8,10 +8,7 @@ nasm pure64.asm -o ../bin/pure64.sys -l ../bin/pure64-debug.txt
cd boot
-nasm mbr.asm -o ../../bin/mbr.sys
-nasm pxestart.asm -o ../../bin/pxestart.sys
-nasm multiboot.asm -o ../../bin/multiboot.sys
-nasm multiboot2.asm -o ../../bin/multiboot2.sys
-nasm uefi.asm -o ../../bin/uefi.sys
+nasm bios.asm -o ../../bin/bios.sys -l ../../bin/bios-debug.txt
+nasm uefi.asm -o ../../bin/uefi.sys -l ../../bin/uefi-debug.txt
cd ../..
diff --git a/docs/README.md b/docs/README.md
index 7c9d716..7e7a2ac 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -73,19 +73,19 @@ OUTPUT_ARCH("i386:x86-64")
SECTIONS
{
- . = 0x100000;
- .text : {
- *(.text)
- }
- .data : {
- *(.data)
- }
- .rodata : {
- *(.rodata)
- }
- .bss : {
- *(.bss)
- }
+ . = 0x100000;
+ .text : {
+ *(.text)
+ }
+ .data : {
+ *(.data)
+ }
+ .rodata : {
+ *(.rodata)
+ }
+ .bss : {
+ *(.bss)
+ }
}
```
@@ -108,7 +108,7 @@ extern int main(void);
void _start(void)
{
- main();
+ main();
}
```
This file would **always** have to be linked in front of everything else. For the above example that would mean the linker command above would have to become:
@@ -231,13 +231,14 @@ MPS INTI flags:
| Trigger Mode | 2 | 2 | 01 Edge-triggered, 11 Level-triggered |
-A copy of the E820 System Memory Map is stored at memory address `0x0000000000006000`. Each E820 record is 32 bytes in length and the memory map is terminated by a blank record.
+A copy of the UEFI System Memory Map is stored at memory address `0x0000000000006000`. Each UEFI record is 48 bytes in length and the memory map is terminated by a blank record.
| Variable | Variable Size | Description |
-| Starting Address | 64-bit | The starting address for this record |
-| Length | 64-bit | The length of memory for this record |
-| Memory Type | 32-bit | Type 1 is usable memory, Type 2 is not usable |
-| Extended Attributes | 32-bit | ACPI 3.0 Extended Attributes bitfield |
-| Padding | 64-bit | Padding for 32-byte alignment |
+| Type | 64-bit | The type of the memory region |
+| Physical Start | 64-bit | Physical Address - 4K aligned |
+| Virtual Start | 64-bit | Virtual Address - 4K aligned |
+| NumberOfPages | 64-bit | The number of 4K pages in this section |
+| Attribute | 64-bit | See document linked below |
+| Padding | 64-bit | Padding |
-For more information on the E820 Memory Map: OSDev wiki on E820
+For more information on the UEFI Memory Map: UEFI Specs
diff --git a/src/boot/pxestart.asm b/src/boot/bios-pxe.asm
similarity index 96%
rename from src/boot/pxestart.asm
rename to src/boot/bios-pxe.asm
index 67667ae..e655a63 100644
--- a/src/boot/pxestart.asm
+++ b/src/boot/bios-pxe.asm
@@ -17,6 +17,9 @@
; kernel64.sys 16384 bytes (or so)
; =============================================================================
+; Set the desired screen resolution values below
+Horizontal_Resolution equ 800
+Vertical_Resolution equ 600
BITS 16
org 0x7C00
@@ -31,10 +34,10 @@ start:
mov sp, 0x7C00
sti ; Enable interrupts
- mov ah, 0
- mov al, 11100011b ; 9600bps, no parity, 1 stop bit, 8 data bits
- mov dx, 0 ; Serial port 0
- int 0x14 ; Configure serial port
+; mov ah, 0
+; mov al, 11100011b ; 9600bps, no parity, 1 stop bit, 8 data bits
+; mov dx, 0 ; Serial port 0
+; int 0x14 ; Configure serial port
; Get the BIOS E820 Memory Map
; use the INT 0x15, eax= 0xE820 BIOS function to get a memory map
@@ -133,6 +136,8 @@ check_A20:
mov si, msg_OK
call print_string_16
+ mov bl, 'B' ; 'B' as we booted via BIOS
+
; At this point we are done with real mode and BIOS interrupts. Jump to 32-bit mode.
cli ; No more interrupts
lgdt [cs:GDTR32] ; Load GDT register
@@ -230,4 +235,4 @@ VBEModeInfoBlock.PhysBasePtr equ VBEModeInfoBlock + 40 ; DD - physical address
VBEModeInfoBlock.Reserved1 equ VBEModeInfoBlock + 44 ; DD - Reserved - always set to 0
VBEModeInfoBlock.Reserved2 equ VBEModeInfoBlock + 48 ; DD - Reserved - always set to 0
-; EOF
+; EOF
\ No newline at end of file
diff --git a/src/boot/mbr.asm b/src/boot/bios.asm
similarity index 79%
rename from src/boot/mbr.asm
rename to src/boot/bios.asm
index 03e7ba1..f358437 100644
--- a/src/boot/mbr.asm
+++ b/src/boot/bios.asm
@@ -13,15 +13,53 @@
; Default location of the second stage boot loader. This loads
; 32 KiB from sector 16 into memory at 0x8000
%define DAP_SECTORS 64
-%define DAP_STARTSECTOR 16
+%define DAP_STARTSECTOR 262160
%define DAP_ADDRESS 0x8000
%define DAP_SEGMENT 0x0000
+; Set the desired screen resolution values below
+Horizontal_Resolution equ 800
+Vertical_Resolution equ 600
BITS 16
org 0x7C00
entry:
+ jmp bootcode
+ nop
+
+; BPB (BIOS Parameter Block)
+dq 0 ; OEM identifier
+dw 0 ; Bytes per sector
+db 0 ; Sectors per cluster
+dw 0 ; Reserved sectors
+db 0 ; Number of FATs
+dw 0 ; Number of root directory entries
+dw 0 ; The total sectors in the logical volume
+db 0 ; Media descriptor type
+dw 0 ; Number of sectors per FAT
+dw 0 ; Number of sectors per track
+dw 0 ; Number of heads or sides on the storage media
+dd 0 ; Number of hidden sectors
+dd 0 ; Large sector count
+
+; EBPB (Extended Boot Record)
+dd 0 ; Sectors per FAT
+dw 0 ; Flags
+dw 0 ; FAT version number
+dd 0 ; The cluster number of the root directory
+dw 0 ; The sector number of the FSInfo structure
+dw 0 ; The sector number of the backup boot sector
+dq 0 ; Reserved
+dd 0 ; Reserved
+db 0 ; Drive number
+db 0 ; Flags in Windows NT
+db 0 ; Signature
+dd 0 ; Volume ID 'Serial' number
+times 11 db 0 ; Volume label string
+dq 0 ; System identifier string. Always "FAT32 "
+
+bootcode:
cli ; Disable interrupts
cld ; Clear direction flag
xor eax, eax
@@ -33,11 +71,6 @@ entry:
mov [DriveNumber], dl ; BIOS passes drive number in DL
- mov ah, 0
- mov al, 11100011b ; 9600bps, no parity, 1 stop bit, 8 data bits
- mov dx, 0 ; Serial port 0
- int 0x14 ; Configure serial port
-
; Get the BIOS E820 Memory Map
; use the INT 0x15, eax= 0xE820 BIOS function to get a memory map
; inputs: es:di -> destination buffer for 24 byte entries
@@ -104,8 +137,8 @@ check_A20:
mov al, 0xDF
out 0x60, al
- mov si, msg_Load
- call print_string_16
+; mov si, msg_Load
+; call print_string_16
mov cx, 0x4000 - 1 ; Start looking from here
VBESearch:
@@ -121,9 +154,9 @@ VBESearch:
jne VBESearch ; Try next mode
cmp byte [VBEModeInfoBlock.BitsPerPixel], 32 ; Desired bit depth
jne VBESearch ; If not equal, try next mode
- cmp word [VBEModeInfoBlock.XResolution], 800 ; Desired XRes here
+ cmp word [VBEModeInfoBlock.XResolution], Horizontal_Resolution ; Desired XRes here
jne VBESearch
- cmp word [VBEModeInfoBlock.YResolution], 600 ; Desired YRes here
+ cmp word [VBEModeInfoBlock.YResolution], Vertical_Resolution ; Desired YRes here
jne VBESearch
or bx, 0x4000 ; Use linear/flat frame buffer model (set bit 14)
@@ -140,12 +173,15 @@ VBESearch:
jc read_fail
; Verify that the 2nd stage boot loader was read.
- mov ax, [0x8006]
- cmp ax, 0x3436 ; Match against the Pure64 binary
- jne sig_fail
+; mov ax, [0x8006]
+; cmp ax, 0x3436 ; Match against the Pure64 binary
+; jne sig_fail
+
+ mov bl, 'B' ; 'B' as we booted via BIOS
+; mov [0x5FFF], al ; Store the boot marker
- mov si, msg_OK
- call print_string_16
+; mov si, msg_OK
+; call print_string_16
; At this point we are done with real mode and BIOS interrupts. Jump to 32-bit mode.
cli ; No more interrupts
@@ -156,12 +192,12 @@ VBESearch:
jmp 8:0x8000 ; Jump to 32-bit protected mode
read_fail:
- mov si, msg_ReadFail
- call print_string_16
- jmp halt
+; mov si, msg_ReadFail
+; call print_string_16
+; jmp halt
sig_fail:
- mov si, msg_SigFail
- call print_string_16
+; mov si, msg_SigFail
+; call print_string_16
halt:
hlt
jmp halt
@@ -171,19 +207,19 @@ halt:
;------------------------------------------------------------------------------
; 16-bit function to output a string to the serial port
; IN: SI - Address of start of string
-print_string_16: ; Output string in SI to screen
- pusha
- mov dx, 0 ; Port 0
-.repeat:
- mov ah, 0x01 ; Serial - Write character to port
- lodsb ; Get char from string
- cmp al, 0
- je .done ; If char is zero, end of string
- int 0x14 ; Output the character
- jmp short .repeat
-.done:
- popa
- ret
+;print_string_16: ; Output string in SI to screen
+; pusha
+; mov dx, 0 ; Port 0
+;.repeat:
+; mov ah, 0x01 ; Serial - Write character to port
+; lodsb ; Get char from string
+; cmp al, 0
+; je .done ; If char is zero, end of string
+; int 0x14 ; Output the character
+; jmp short .repeat
+;.done:
+; popa
+; ret
;------------------------------------------------------------------------------
align 16
@@ -198,31 +234,30 @@ dw 0xFFFF, 0x0000, 0x9A00, 0x00CF ; 32-bit code descriptor
dw 0xFFFF, 0x0000, 0x9200, 0x00CF ; 32-bit data descriptor
gdt32_end:
-msg_Load db 10, "MBR ", 0
-msg_OK db "OK", 0
-msg_SigFail db "- Bad Sig!", 0
-msg_ReadFail db "Failed to read drive!", 0
+align 4
-times 446-$+$$ db 0
+DAP:
+db 0x10
+db 0x00
+dw DAP_SECTORS
+dw DAP_ADDRESS
+dw DAP_SEGMENT
+dq DAP_STARTSECTOR
-; False partition table entry required by some BIOS vendors.
-db 0x80, 0x00, 0x01, 0x00, 0xEB, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF
DriveNumber db 0x00
-times 476-$+$$ db 0
+;msg_Load db 10, "MBR ", 0
+;msg_OK db "OK", 0
+;msg_SigFail db "- Bad Sig!", 0
+;msg_ReadFail db "Failed to read!", 0
-align 4
+times 446-$+$$ db 0
-DAP:
- db 0x10
- db 0x00
- dw DAP_SECTORS
- dw DAP_ADDRESS
- dw DAP_SEGMENT
- dq DAP_STARTSECTOR
+; Partition entries (4x 16-bytes)
times 510-$+$$ db 0
+; Boot signature
sign dw 0xAA55
VBEModeInfoBlock: equ 0x5F00
diff --git a/src/boot/multiboot.asm b/src/boot/multiboot.asm
deleted file mode 100644
index 33e7199..0000000
--- a/src/boot/multiboot.asm
+++ /dev/null
@@ -1,110 +0,0 @@
-; =============================================================================
-; Pure64 Multiboot -- a 64-bit OS/software loader written in Assembly for x86-64 systems
-; Copyright (C) 2008-2024 Return Infinity -- see LICENSE.TXT
-;
-; http://stackoverflow.com/questions/33488194/creating-a-simple-multiboot-kernel-loaded-with-grub2
-; https://www.gnu.org/software/grub/manual/multiboot/multiboot.html#OS-image-format
-; =============================================================================
-
-
-[BITS 32]
-[global _start]
-[ORG 0x100000] ;If using '-f bin' we need to specify the
- ;origin point for our code with ORG directive
- ;multiboot loaders load us at physical
- ;address 0x100000
-
-FLAG_ALIGN equ 1<<0 ; align loaded modules on page boundaries
-FLAG_MEMINFO equ 1<<1 ; provide memory map
-FLAG_VIDEO equ 1<<2 ; set video mode
-FLAG_AOUT_KLUDGE equ 1<<16
- ;FLAGS[16] indicates to GRUB we are not
- ;an ELF executable and the fields
- ;header address, load address, load end address;
- ;bss end address and entry address will be available
- ;in Multiboot header
-
-MAGIC equ 0x1BADB002
- ;magic number GRUB searches for in the first 8k
- ;of the kernel file GRUB is told to load
-
-FLAGS equ FLAG_ALIGN | FLAG_MEMINFO | FLAG_VIDEO | FLAG_AOUT_KLUDGE
-CHECKSUM equ -(MAGIC + FLAGS)
-
-mode_type equ 0 ; Linear
-width equ 1024
-height equ 768
-depth equ 24
-
-_start: ; We need some code before the multiboot header
- xor eax, eax ; Clear eax and ebx in the event
- xor ebx, ebx ; we are not loaded by GRUB.
- jmp multiboot_entry ; Jump over the multiboot header
- align 4 ; Multiboot header must be 32-bit aligned
-
-multiboot_header:
- dd MAGIC ; magic
- dd FLAGS ; flags
- dd CHECKSUM ; checksum
- dd multiboot_header ; header address
- dd _start ; load address of code entry point
- dd 0x00 ; load end address : not necessary
- dd 0x00 ; bss end address : not necessary
- dd multiboot_entry ; entry address GRUB will start at
- dd mode_type
- dd width
- dd height
- dd depth
-
-align 16
-
-multiboot_entry:
- push 0
- popf
- cld ; Clear direction flag
-
-; Copy memory map
- mov esi, ebx ; GRUB stores the Multiboot info table at the address in EBX
- mov edi, 0x6000 ; We want the memory map stored here
- add esi, 44 ; Memory map address at this offset in the Mutliboot table
- lodsd ; Grab the memory map size in bytes
- mov ecx, eax
- lodsd ; Grab the memory map address
- mov esi, eax
-
-memmap_entry:
- lodsd ; Size of entry
- cmp eax, 0
- je memmap_end
- movsd ; base_addr_low
- movsd ; base_addr_high
- movsd ; length_low
- movsd ; length_high
- movsd ; type
- xor eax, eax
- stosd ; padding
- stosd
- stosd
- jmp memmap_entry
-
-memmap_end:
- xor eax, eax
- mov ecx, 8
- rep stosd
-
-; Copy loader and kernel to expected location
- mov esi, multiboot_end
- mov edi, 0x00008000
- mov ecx, 8192 ; Copy 32K
- rep movsd ; Copy loader to expected address
-
- cli
-
- jmp 0x00008000
-
-times 512-$+$$ db 0 ; Padding
-
-multiboot_end:
-
-; =============================================================================
-; EOF
diff --git a/src/boot/multiboot2.asm b/src/boot/multiboot2.asm
deleted file mode 100644
index bee725b..0000000
--- a/src/boot/multiboot2.asm
+++ /dev/null
@@ -1,55 +0,0 @@
-; =============================================================================
-; Pure64 Multiboot 2 -- a 64-bit OS/software loader written in Assembly for x86-64 systems
-; Copyright (C) 2008-2024 Return Infinity -- see LICENSE.TXT
-;
-; http://nongnu.askapache.com/grub/phcoder/multiboot.pdf
-; =============================================================================
-
-
-[BITS 32]
-[global _start]
-[ORG 0x100000] ;If using '-f bin' we need to specify the
- ;origin point for our code with ORG directive
- ;multiboot loaders load us at physical
- ;address 0x100000
-
-MAGIC equ 0xE85250D6
-ARCHITECHTURE equ 0
-HEADER_LENGTH equ multiboot_header_end - multiboot_header_start
-CHECKSUM equ 0x100000000 - (MAGIC + ARCHITECHTURE + HEADER_LENGTH)
-
-_start: ; We need some code before the multiboot header
- xor eax, eax ; Clear eax and ebx in the event
- xor ebx, ebx ; we are not loaded by GRUB.
- jmp multiboot_entry ; Jump over the multiboot header
- align 8 ; Multiboot 2 header must be 64-bit aligned
-
-multiboot_header_start:
- dd MAGIC
- dd ARCHITECHTURE
- dd HEADER_LENGTH
- dd CHECKSUM
-entry_address_tag_start:
- dw 3
- dw 0
- dd entry_address_tag_end - entry_address_tag_start
- dq multiboot_entry
-entry_address_tag_end:
-framebuffer_tag_start:
- dw 5
- dw 0
- dd framebuffer_tag_end - framebuffer_tag_start
- dd 800
- dd 600
- dd 32
-framebuffer_tag_end:
- dw 0 ; End type
- dw 0
- dd 8
-multiboot_header_end:
-
-multiboot_entry:
- cmp eax, 0x36D76289 ; Magic value
- jne error
-error:
- jmp $
diff --git a/src/boot/uefi.asm b/src/boot/uefi.asm
index 9804d61..2d1a9b8 100644
--- a/src/boot/uefi.asm
+++ b/src/boot/uefi.asm
@@ -12,8 +12,8 @@
; =============================================================================
; Set the desired screen resolution values below
-Horizontal_Resolution equ 640
-Vertical_Resolution equ 480
+Horizontal_Resolution equ 800
+Vertical_Resolution equ 600
BITS 64
ORG 0x00400000
@@ -126,13 +126,18 @@ EntryPoint:
; Set screen colour attributes
mov rcx, [OUTPUT] ; IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
- mov rdx, 0x7F ; IN UINTN Attribute Light grey background, white foreground
+ mov rdx, 0x07 ; IN UINTN Attribute - Black background, grey foreground
call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_SET_ATTRIBUTE]
- ; Clear screen
+ ; Clear screen (This also sets the cursor position to 0,0)
mov rcx, [OUTPUT] ; IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_CLEAR_SCREEN]
+ ; Output 'UEFI '
+ mov rcx, [OUTPUT] ; IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
+ lea rdx, [msg_uefi] ; IN CHAR16 *String
+ call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUTSTRING]
+
; Find the address of the ACPI data from the UEFI configuration table
mov rax, [EFI_SYSTEM_TABLE]
mov rcx, [rax + EFI_SYSTEM_TABLE_NUMBEROFENTRIES]
@@ -164,7 +169,7 @@ nextentry:
jne error
; Parse the graphics information
- ; Mode Structure
+ ; EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE Structure
; 0 UINT32 - MaxMode
; 4 UINT32 - Mode
; 8 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION - *Info;
@@ -175,7 +180,9 @@ nextentry:
add rax, EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE
mov rax, [rax] ; RAX holds the address of the Mode structure
mov eax, [rax] ; RAX holds UINT32 MaxMode
- mov [vid_max], rax
+ mov ebx, [rax+4] ; RBX holds UINT32 Mode (The current mode)
+ mov [vid_max], rax ; The maximum video modes we can check
+ mov [vid_orig], rbx ; The mode currently used by UEFI
jmp vid_query
next_video_mode:
@@ -185,7 +192,7 @@ next_video_mode:
mov rdx, [vid_max]
cmp rax, rdx
je skip_set_video ; If we have reached the max then bail out
-
+
vid_query:
; Query a video mode
mov rcx, [VIDEO] ; IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This
@@ -211,6 +218,8 @@ vid_query:
mov rcx, [VIDEO] ; IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This
mov rdx, [vid_current] ; IN UINT32 ModeNumber
call [rcx + EFI_GRAPHICS_OUTPUT_PROTOCOL_SET_MODE]
+ cmp rax, EFI_SUCCESS
+ jne next_video_mode
skip_set_video:
; Gather video mode details
@@ -243,126 +252,106 @@ skip_set_video:
cmp ax, 0x3436 ; Match against the Pure64 binary
jne sig_fail
- ; Signal to Pure64 that it was booted via UEFI
- mov al, 'U'
- mov [0x8005], al
-
- ; Save video values to the area of memory where Pure64 expects them
- mov rdi, 0x00005C00 + 40 ; VBEModeInfoBlock.PhysBasePtr
- mov rax, [FB]
- stosd
- mov rdi, 0x00005C00 + 18 ; VBEModeInfoBlock.XResolution & .YResolution
- mov rax, [HR]
- stosw
- mov rax, [VR]
- stosw
- mov rdi, 0x00005C00 + 25 ; VBEModeInfoBlock.BitsPerPixel
- mov rax, 32
- stosb
+; Debug
+; mov rbx, [FB] ; Display the framebuffer address
+; call printhex
+get_memmap:
+ ; Output 'OK' as we are about to leave UEFI
mov rcx, [OUTPUT] ; IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
lea rdx, [msg_OK] ; IN CHAR16 *String
call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUTSTRING]
- ; Get Memory Map
-get_memmap:
+ ; Get Memory Map from UEFI and save it [memmap]
lea rcx, [memmapsize] ; IN OUT UINTN *MemoryMapSize
- lea rdx, 0x6000 ; OUT EFI_MEMORY_DESCRIPTOR *MemoryMap
+ mov rdx, [memmap] ; OUT EFI_MEMORY_DESCRIPTOR *MemoryMap
lea r8, [memmapkey] ; OUT UINTN *MapKey
lea r9, [memmapdescsize] ; OUT UINTN *DescriptorSize
lea r10, [memmapdescver] ; OUT UINT32 *DescriptorVersion
- sub rsp, 32 ; Shadow space
- push r10
+ mov [rsp+32], r10
mov rax, [BS]
call [rax + EFI_BOOT_SERVICES_GETMEMORYMAP]
- pop r10
- add rsp, 32
- cmp al, 5 ; EFI_BUFFER_TOO_SMALL
+ cmp al, EFI_BUFFER_TOO_SMALL
je get_memmap ; Attempt again as the memmapsize was updated by EFI
cmp rax, EFI_SUCCESS
- jne error
+ jne exitfailure
; Output at 0x6000 is as follows:
; 0 UINT32 - Type
; 8 EFI_PHYSICAL_ADDRESS - PhysicalStart
; 16 EFI_VIRTUAL_ADDRESS - VirtualStart
- ; 24 UINT64 - NumberOfPages
+ ; 24 UINT64 - NumberOfPages - This is a number of 4K pages (must be a non-zero value)
; 32 UINT64 - Attribute
; 40 UINT64 - Blank
- ; Exit Boot services as EFI is no longer needed
+ ; Exit Boot services as UEFI is no longer needed
mov rcx, [EFI_IMAGE_HANDLE] ; IN EFI_HANDLE ImageHandle
mov rdx, [memmapkey] ; IN UINTN MapKey
mov rax, [BS]
call [rax + EFI_BOOT_SERVICES_EXITBOOTSERVICES]
cmp rax, EFI_SUCCESS
- jne exitfailure
+ jne get_memmap ; If it failed, get the memory map and try to exit again
; Stop interrupts
cli
- ; Build a 32-bit memory table for 4GiB of identity mapped memory
- mov rdi, 0x200000
- mov rax, 0x00000083
- mov rcx, 1024
-nextpage:
- stosd
- add rax, 0x400000
- dec rcx
- cmp rcx, 0
- jne nextpage
-
- ; Load the custom GDT
- lgdt [gdtr]
+ ; Save UEFI values to the area of memory where Pure64 expects them
+ mov rdi, 0x00005F00
+ mov rax, [FB]
+ stosq ; 64-bit Frame Buffer Base
+ mov rax, [FBS]
+ stosq ; 64-bit Frame Buffer Size in bytes
+ mov rax, [HR]
+ stosw ; 16-bit Screen X
+ mov rax, [VR]
+ stosw ; 16-bit Screen Y
+ xor eax, eax
+ stosd ; Padding
+ mov rax, [memmap]
+ stosq ; Memory Map Base
+ mov rax, [memmapsize]
+ stosq ; Size of Memory Map in bytes
+ mov rax, [memmapkey]
+ stosq ; The key used to exit Boot Services
+ mov rax, [memmapdescsize]
+ stosq ; EFI_MEMORY_DESCRIPTOR size in bytes
+ mov rax, [memmapdescver]
+ stosq ; EFI_MEMORY_DESCRIPTOR version
+
+ ; Set screen to green before jumping to Pure64
+ mov rdi, [FB]
+ mov eax, 0x0000FF00 ; 0x00RRGGBB
+ mov rcx, [FBS]
+ shr rcx, 2 ; Quick divide by 4 (32-bit colour)
+ rep stosd
- ; Switch to compatibility mode
- mov rax, SYS32_CODE_SEL ; Compatibility mode
- push rax
- lea rax, [compatmode]
- push rax
- retfq
-
-BITS 32
-compatmode:
- ; Set the segment registers
- mov eax, SYS32_DATA_SEL
- mov ds, ax
- mov es, ax
- mov fs, ax
- mov gs, ax
- mov ss, ax
-
- ; Deactivate IA-32e mode by clearing CR0.PG
- mov eax, cr0
- btc eax, 31 ; Clear PG (Bit 31)
- mov cr0, eax
-
- ; Load CR3
- mov eax, 0x00200000 ; Address of memory map
- mov cr3, eax
-
- ; Disable IA-32e mode by setting IA32_EFER.LME = 0
- mov ecx, 0xC0000080 ; EFER MSR number
- rdmsr ; Read EFER
- and eax, 0xFFFFFEFF ; Clear LME (Bit 8)
- wrmsr ; Write EFER
-
- mov eax, 0x00000010 ; Set PSE (Bit 4)
- mov cr4, eax
-
- ; Enable legacy paged-protected mode by setting CR0.PG
- mov eax, 0x00000001 ; Set PM (Bit 0)
- mov cr0, eax
-
- jmp SYS32_CODE_SEL:0x8000 ; 32-bit jump to set CS
+ ; Clear registers
+ xor eax, eax
+ xor ecx, ecx
+ xor edx, edx
+ xor ebx, ebx
+ mov rsp, 0x8000
+ xor ebp, ebp
+ xor esi, esi
+ xor edi, edi
+ xor r8, r8
+ xor r9, r9
+ xor r10, r10
+ xor r11, r11
+ xor r12, r12
+ xor r13, r13
+ xor r14, r14
+ xor r15, r15
+
+ mov bl, 'U'
+ jmp 0x8000
-BITS 64
exitfailure:
+ ; Set screen to red on exit failure
mov rdi, [FB]
- mov eax, 0x00FF0000 ; Red
+ mov eax, 0x00FF0000 ; 0x00RRGGBB
mov rcx, [FBS]
shr rcx, 2 ; Quick divide by 4 (32-bit colour)
rep stosd
- jmp halt
error:
mov rcx, [OUTPUT] ; IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
lea rdx, [msg_error] ; IN CHAR16 *String
@@ -376,29 +365,64 @@ halt:
hlt
jmp halt
+; -----------------------------------------------------------------------------
+; printhex - Display a 64-bit value in hex
+; IN: RBX = Value
+printhex:
+ mov rbp, 16 ; Counter
+ push rax
+ push rcx
+ push rdx ; 3 pushes also align stack on 16 byte boundary
+ ; (8+3*8)=32, 32 evenly divisible by 16
+ sub rsp, 32 ; Allocate 32 bytes of shadow space
+printhex_loop:
+ rol rbx, 4
+ mov rax, rbx
+ and rax, 0Fh
+ lea rcx, [Hex]
+ mov rax, [rax + rcx]
+ mov byte [Num], al
+ lea rdx, [Num]
+ mov rcx, [OUTPUT] ; IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
+ call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUTSTRING]
+ dec rbp
+ jnz printhex_loop
+ lea rdx, [newline]
+ mov rcx, [OUTPUT] ; IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This
+ call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUTSTRING]
+
+ add rsp, 32
+ pop rdx
+ pop rcx
+ pop rax
+ ret
+; -----------------------------------------------------------------------------
+
align 2048
CODE_END:
; Data begins here
DATA:
-EFI_IMAGE_HANDLE: dq 0 ; EFI gives this in RCX
-EFI_SYSTEM_TABLE: dq 0 ; And this in RDX
-EFI_RETURN: dq 0 ; And this in RSP
-BS: dq 0 ; Boot services
-RTS: dq 0 ; Runtime services
-CONFIG: dq 0 ; Config Table address
-ACPI: dq 0 ; ACPI table address
-OUTPUT: dq 0 ; Output services
-VIDEO: dq 0 ; Video services
-FB: dq 0 ; Frame buffer base address
-FBS: dq 0 ; Frame buffer size
-HR: dq 0 ; Horizontal Resolution
-VR: dq 0 ; Vertical Resolution
-memmapsize: dq 8192
+EFI_IMAGE_HANDLE: dq 0 ; EFI gives this in RCX
+EFI_SYSTEM_TABLE: dq 0 ; And this in RDX
+EFI_RETURN: dq 0 ; And this in RSP
+BS: dq 0 ; Boot services
+RTS: dq 0 ; Runtime services
+CONFIG: dq 0 ; Config Table address
+ACPI: dq 0 ; ACPI table address
+OUTPUT: dq 0 ; Output services
+VIDEO: dq 0 ; Video services
+FB: dq 0 ; Frame buffer base address
+FBS: dq 0 ; Frame buffer size
+HR: dq 0 ; Horizontal Resolution
+VR: dq 0 ; Vertical Resolution
+memmap: dq 0x200000 ; Store the Memory Map from UEFI here
+memmapsize: dq 32768 ; Max size we are expecting in bytes
memmapkey: dq 0
memmapdescsize: dq 0
memmapdescver: dq 0
+vid_orig: dq 0
vid_current: dq 0
vid_max: dq 0
vid_size: dq 0
@@ -414,34 +438,19 @@ dd 0x9042a9de
dw 0x23dc, 0x4a38
db 0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a
-hextable: db '0123456789ABCDEF'
+msg_uefi: dw u('UEFI '), 0
+msg_OK: dw u('OK '), 0
msg_error: dw u('Error'), 0
msg_SigFail: dw u('Bad Sig!'), 0
-msg_OK: dw u('OK'), 0
-
-align 16
-gdtr: ; Global Descriptors Table Register
-dw gdt_end - gdt - 1 ; limit of GDT (size minus one)
-dq gdt ; linear address of GDT
-
-align 16
-gdt:
-SYS64_NULL_SEL equ $-gdt ; Null Segment
-dq 0x0000000000000000
-SYS32_CODE_SEL equ $-gdt ; 32-bit code descriptor
-dq 0x00CF9A000000FFFF ; 55 Granularity 4KiB, 54 Size 32bit, 47 Present, 44 Code/Data, 43 Executable, 41 Readable
-SYS32_DATA_SEL equ $-gdt ; 32-bit data descriptor
-dq 0x00CF92000000FFFF ; 55 Granularity 4KiB, 54 Size 32bit, 47 Present, 44 Code/Data, 41 Writeable
-SYS64_CODE_SEL equ $-gdt ; 64-bit code segment, read/execute, nonconforming
-dq 0x00209A0000000000 ; 53 Long mode code, 47 Present, 44 Code/Data, 43 Executable, 41 Readable
-SYS64_DATA_SEL equ $-gdt ; 64-bit data segment, read/write, expand down
-dq 0x0000920000000000 ; 47 Present, 44 Code/Data, 41 Writable
-gdt_end:
+Hex: db '0123456789ABCDEF'
+Num: dw 0, 0
+newline: dw 13, 10, 0
+
align 4096
PAYLOAD:
-align 65536 ; Pad out to 64K
+align 65536 ; Pad out to 64K
DATA_END:
END:
@@ -462,7 +471,7 @@ EFI_NO_MEDIA equ 12
EFI_MEDIA_CHANGED equ 13
EFI_NOT_FOUND equ 14
-EFI_SYSTEM_TABLE_CONOUT equ 64
+EFI_SYSTEM_TABLE_CONOUT equ 64
EFI_SYSTEM_TABLE_RUNTIMESERVICES equ 88
EFI_SYSTEM_TABLE_BOOTSERVICES equ 96
EFI_SYSTEM_TABLE_NUMBEROFENTRIES equ 104
diff --git a/src/init/acpi.asm b/src/init/acpi.asm
index 4a3403f..eaaa276 100644
--- a/src/init/acpi.asm
+++ b/src/init/acpi.asm
@@ -136,6 +136,10 @@ init_smp_acpi_done:
noACPI:
novalidacpi:
+ mov rdi, [0x00005F00 + 40]
+ mov eax, 0x00FF0000 ; 0x00RRGGBB
+ mov ecx, 800 * 600
+ rep stosd
jmp $
diff --git a/src/init/smp_ap.asm b/src/init/smp_ap.asm
index 0e8f60a..b204b04 100644
--- a/src/init/smp_ap.asm
+++ b/src/init/smp_ap.asm
@@ -110,18 +110,13 @@ startap64:
xor r14, r14
xor r15, r15
+ mov ax, 0x10 ; TODO Is this needed?
mov ds, ax ; Clear the legacy segment registers
mov es, ax
mov ss, ax
mov fs, ax
mov gs, ax
- mov rax, clearcs64_ap
- jmp rax
- nop
-clearcs64_ap:
- xor eax, eax
-
; Reset the stack. Each CPU gets a 1024-byte unique stack location
mov rsi, [p_LocalAPICAddress] ; We would call p_smp_get_id here but the stack is not ...
add rsi, 0x20 ; ... yet defined. It is safer to find the value directly.
diff --git a/src/interrupt.asm b/src/interrupt.asm
index 6ad9de4..9b50c8e 100644
--- a/src/interrupt.asm
+++ b/src/interrupt.asm
@@ -174,8 +174,17 @@ exception_gate_19:
jmp exception_gate_main
exception_gate_main:
+ ; Output message via serial port
+ mov rsi, message_error ; Location of message
+ call debug_msg
+ ; Set screen to Red
+ mov rdi, [0x00005F00] ; Frame buffer base
+ mov rcx, [0x00005F08] ; Frame buffer size
+ shr rcx, 2 ; Quick divide by 4
+ mov eax, 0x00FF0000 ; 0x00RRGGBB
+ rep stosd
exception_gate_main_hang:
- nop
+ hlt
jmp exception_gate_main_hang ; Hang. User must reset machine at this point
; -----------------------------------------------------------------------------
diff --git a/src/pure64.asm b/src/pure64.asm
index 2b2492c..dc6db6b 100644
--- a/src/pure64.asm
+++ b/src/pure64.asm
@@ -17,12 +17,12 @@
; =============================================================================
-BITS 32
+BITS 64
ORG 0x00008000
PURE64SIZE equ 4096 ; Pad Pure64 to this length
start:
- jmp start32 ; This command will be overwritten with 'NOP's before the AP's are started
+ jmp bootmode ; This command will be overwritten with 'NOP's before the AP's are started
nop
db 0x36, 0x34 ; '64' marker
@@ -47,9 +47,14 @@ BITS 16
%include "init/smp_ap.asm" ; AP's will start execution at 0x8000 and fall through to this code
; =============================================================================
-; 32-bit mode
+; This is 32-bit code so it's important that the encoding of the first few instructions also
+; work in 64-bit mode. If a 'U' is stored at 0x5FFF then we know it was a UEFI boot and can
+; immediately proceed to start64. Otherwise we need to set up a minimal 64-bit environment.
BITS 32
-start32:
+bootmode:
+ cmp bl, 'U' ; If it is 'U' then we booted via UEFI and are already in 64-bit mode for the BSP
+ je start64 ; Jump to the 64-bit code, otherwise fall through to 32-bit init
+
mov eax, 16 ; Set the correct segment registers
mov ds, ax
mov es, ax
@@ -57,11 +62,6 @@ start32:
mov fs, ax
mov gs, ax
- mov edi, 0x5000 ; Clear the info map and system variable memory
- xor eax, eax
- mov ecx, 960 ; 3840 bytes (Range is 0x5000 - 0x5EFF)
- rep stosd ; Don't overwrite the VBE data at 0x5F00
-
xor eax, eax ; Clear all registers
xor ebx, ebx
xor ecx, ecx
@@ -71,6 +71,121 @@ start32:
xor ebp, ebp
mov esp, 0x8000 ; Set a known free location for the stack
+ ; Save the frame buffer address, size (after its calculated), and the screen x,y
+ mov bx, [0x5F00 + 20]
+ push ebx
+ mov ax, [0x5F00 + 18]
+ push eax
+ mul ebx
+ mov ecx, eax
+ shl ecx, 2 ; Quick multiply by 4
+ mov edi, 0x5F00
+ mov eax, [0x5F00 + 40]
+ stosd ; 64-bit Frame Buffer Base (low)
+ xor eax, eax
+ stosd ; 64-bit Frame Buffer Base (high)
+ mov eax, ecx
+ stosd ; 64-bit Frame Buffer Size in bytes (low)
+ xor eax, eax
+ stosd ; 64-bit Frame Buffer Size in bytes (high)
+ pop eax
+ stosw ; 16-bit Screen X
+ pop eax
+ stosw ; 16-bit Screen y
+
+ ; Clear memory for the Page Descriptor Entries (0x10000 - 0x5FFFF)
+ mov edi, 0x00210000
+ mov ecx, 81920
+ rep stosd ; Write 320KiB
+
+; Create the temporary Page Map Level 4 Entries (PML4E)
+; PML4 is stored at 0x0000000000202000, create the first entry there
+; A single PML4 entry can map 512GiB with 2MiB pages
+; A single PML4 entry is 8 bytes in length
+ cld
+ mov edi, 0x00202000 ; Create a PML4 entry for the first 4GiB of RAM
+ mov eax, 0x00203007 ; Bits 0 (P), 1 (R/W), 2 (U/S), location of low PDP (4KiB aligned)
+ stosd
+ xor eax, eax
+ stosd
+
+; Create the temporary Page-Directory-Pointer-Table Entries (PDPTE)
+; PDPTE is stored at 0x0000000000203000, create the first entry there
+; A single PDPTE can map 1GiB with 2MiB pages
+; A single PDPTE is 8 bytes in length
+; 4 entries are created to map the first 4GiB of RAM
+ mov ecx, 4 ; number of PDPE's to make.. each PDPE maps 1GiB of physical memory
+ mov edi, 0x00203000 ; location of low PDPE
+ mov eax, 0x00210007 ; Bits 0 (P), 1 (R/W), 2 (U/S), location of first low PD (4KiB aligned)
+pdpte_low_32:
+ stosd
+ push eax
+ xor eax, eax
+ stosd
+ pop eax
+ add eax, 0x00001000 ; 4KiB later (512 records x 8 bytes)
+ dec ecx
+ cmp ecx, 0
+ jne pdpte_low_32
+
+; Create the temporary low Page-Directory Entries (PDE).
+; A single PDE can map 2MiB of RAM
+; A single PDE is 8 bytes in length
+ mov edi, 0x00210000 ; Location of first PDE
+ mov eax, 0x0000008F ; Bits 0 (P), 1 (R/W), 2 (U/S), 3 (PWT), and 7 (PS) set
+ xor ecx, ecx
+pde_low_32: ; Create a 2 MiB page
+ stosd
+ push eax
+ xor eax, eax
+ stosd
+ pop eax
+ add eax, 0x00200000 ; Increment by 2MiB
+ inc ecx
+ cmp ecx, 2048
+ jne pde_low_32 ; Create 2048 2 MiB page maps.
+
+; Load the GDT
+ lgdt [tGDTR64]
+
+; Enable extended properties
+ mov eax, cr4
+ or eax, 0x0000000B0 ; PGE (Bit 7), PAE (Bit 5), and PSE (Bit 4)
+ mov cr4, eax
+
+; Point cr3 at PML4
+ mov eax, 0x00202008 ; Write-thru enabled (Bit 3)
+ mov cr3, eax
+
+; Enable long mode and SYSCALL/SYSRET
+ mov ecx, 0xC0000080 ; EFER MSR number
+ rdmsr ; Read EFER
+ or eax, 0x00000101 ; LME (Bit 8)
+ wrmsr ; Write EFER
+
+ mov bl, 'B'
+
+; Enable paging to activate long mode
+ mov eax, cr0
+ or eax, 0x80000000 ; PG (Bit 31)
+ mov cr0, eax
+
+ jmp SYS64_CODE_SEL:start64 ; Jump to 64-bit mode
+
+
+; =============================================================================
+; 64-bit mode
+BITS 64
+start64:
+ mov esp, 0x8000 ; Set a known free location for the stack
+
+ mov edi, 0x5000 ; Clear the info map and system variable memory
+ xor eax, eax
+ mov ecx, 960 ; 3840 bytes (Range is 0x5000 - 0x5EFF)
+ rep stosd ; Don't overwrite the UEFI/BIOS data at 0x5F00
+
+ mov [p_BootMode], bl
+
; Set up RTC
; Port 0x70 is RTC Address, and 0x71 is RTC Data
; http://www.nondot.org/sabre/os/files/MiscHW/RealtimeClockFAQ.txt
@@ -114,26 +229,35 @@ rtc_poll:
out 0x21, al
out 0xA1, al
-; Configure serial port @ 0x03F8
+; Configure serial port @ 0x03F8 as 115200 8N1
mov dx, 0x03F8 + 1 ; Interrupt Enable
mov al, 0x00 ; Disable all interrupts
out dx, al
mov dx, 0x03F8 + 3 ; Line Control
- mov al, 80
+ mov al, 80 ; Enable DLAB
out dx, al
- mov dx, 0x03F8 + 0 ; Divisor Latch
- mov ax, 1 ; 1 = 115200 baud
- out dx, ax
- mov dx, 0x03F8 + 3 ; Line Control
- mov al, 3 ; 8 bits, no parity, one stop bit
+ mov dx, 0x03F8 + 0 ; Divisor Latch Low
+ mov al, 1 ; 1 = 115200 baud
out dx, al
- mov dx, 0x03F8 + 4 ; Modem Control
- mov al, 3
+ mov dx, 0x03F8 + 1 ; Divisor Latch High
+ mov al, 0
+ out dx, al
+ mov dx, 0x03F8 + 3 ; Line Control
+ mov al, 3 ; 8 data bits (0-1 set), one stop bit (2 set), no parity (3-5 clear), DLB (7 clear)
out dx, al
+ mov dx, 0x03F8 + 2 ; Interrupt Identification and FIFO Control
mov al, 0xC7 ; Enable FIFO, clear them, with 14-byte threshold
- mov dx, 0x03F8 + 2
+ out dx, al
+ mov dx, 0x03F8 + 4 ; Modem Control
+ mov al, 0 ; No flow control, no interrupts
out dx, al
+ mov rsi, message_pure64 ; Location of message
+ call debug_msg
+
+; mov al, 'a' ; Newline
+; call debug_msg_char
+
; Clear out the first 20KiB of memory. This will store the 64-bit IDT, GDT, PML4, PDP Low, and PDP High
mov ecx, 5120
xor eax, eax
@@ -149,94 +273,88 @@ rtc_poll:
mov esi, gdt64
mov edi, 0x00001000 ; GDT address
mov ecx, (gdt64_end - gdt64)
- rep movsb ; Move it to final pos.
+ rep movsb ; Copy it to final location
; Create the Page Map Level 4 Entries (PML4E)
; PML4 is stored at 0x0000000000002000, create the first entry there
-; A single PML4 entry can map 512GiB with 2MiB pages
+; A single PML4 entry can map 512GiB
; A single PML4 entry is 8 bytes in length
- cld
- mov edi, 0x00002000 ; Create a PML4 entry for the first 4GiB of RAM
+ mov edi, 0x00002000 ; Create a PML4 entry for physical memory
mov eax, 0x00003007 ; Bits 0 (P), 1 (R/W), 2 (U/S), location of low PDP (4KiB aligned)
- stosd
- xor eax, eax
- stosd
-
+ stosq
mov edi, 0x00002800 ; Create a PML4 entry for higher half (starting at 0xFFFF800000000000)
mov eax, 0x00004007 ; Bits 0 (P), 1 (R/W), 2 (U/S), location of high PDP (4KiB aligned)
- stosd
- xor eax, eax
- stosd
+ stosq
-; Create the Page-Directory-Pointer-Table Entries (PDPTE)
+; Check to see if the system supports 1 GiB pages
+; If it does we will use that for identity mapping the lower memory
+ mov eax, 0x80000001
+ cpuid
+ bt edx, 26 ; Page1GB
+ jc pdpte_1GB
+
+; Create the Low Page-Directory-Pointer-Table Entries (PDPTE)
; PDPTE is stored at 0x0000000000003000, create the first entry there
-; A single PDPTE can map 1GiB with 2MiB pages
+; A single PDPTE can map 1GiB
; A single PDPTE is 8 bytes in length
; 4 entries are created to map the first 4GiB of RAM
mov ecx, 4 ; number of PDPE's to make.. each PDPE maps 1GiB of physical memory
mov edi, 0x00003000 ; location of low PDPE
mov eax, 0x00010007 ; Bits 0 (P), 1 (R/W), 2 (U/S), location of first low PD (4KiB aligned)
-create_pdpte_low:
- stosd
- push eax
- xor eax, eax
- stosd
- pop eax
- add eax, 0x00001000 ; 4KiB later (512 records x 8 bytes)
+pdpte_low:
+ stosq
+ add rax, 0x00001000 ; 4KiB later (512 records x 8 bytes)
dec ecx
- cmp ecx, 0
- jne create_pdpte_low
+ jnz pdpte_low
-; Create the low Page-Directory Entries (PDE).
+; Create the Low Page-Directory Entries (PDE)
; A single PDE can map 2MiB of RAM
; A single PDE is 8 bytes in length
mov edi, 0x00010000 ; Location of first PDE
- mov eax, 0x0000008F ; Bits 0 (P), 1 (R/W), 2 (U/S), 3 (PWT), and 7 (PS) set
- xor ecx, ecx
-pde_low: ; Create a 2 MiB page
- stosd
- push eax
- xor eax, eax
- stosd
- pop eax
- add eax, 0x00200000 ; Increment by 2MiB
- inc ecx
- cmp ecx, 2048
- jne pde_low ; Create 2048 2 MiB page maps.
+ mov eax, 0x00000087 ; Bits 0 (P), 1 (R/W), 2 (U/S), and 7 (PS) set
+ mov ecx, 2048 ; Create 2048 2MiB page maps
+pde_low: ; Create a 2MiB page
+ stosq
+ add rax, 0x00200000 ; Increment by 2MiB
+ dec ecx
+ jnz pde_low
+ jmp skip1GB
+
+; Create the Low Page-Directory-Pointer Table Entries (PDPTE)
+; PDPTE is stored at 0x0000000000003000, create the first entry there
+; A single PDPTE can map 1GiB
+; A single PDPTE is 8 bytes in length
+; 512 entries are created to map the first 512GiB of RAM
+pdpte_1GB:
+ mov ecx, 512 ; number of PDPE's to make.. each PDPE maps 1GiB of physical memory
+ mov edi, 0x00003000 ; location of low PDPE
+ mov eax, 0x00000087 ; Bits 0 (P), 1 (R/W), 2 (U/S), 7 (PS)
+pdpte_low_1GB: ; Create a 1GiB page
+ stosq
+ add rax, 0x40000000 ; Increment by 1GiB
+ dec ecx
+ jnz pdpte_low_1GB
+
+skip1GB:
+ ; Debug - Set screen to blue prior to loading GDT and PML4
+ mov rdi, [0x00005F00] ; Frame buffer base
+ mov rcx, [0x00005F08] ; Frame buffer size
+ shr rcx, 2 ; Quick divide by 4
+ mov eax, 0x002020F0 ; 0x00RRGGBB
+ rep stosd
; Load the GDT
lgdt [GDTR64]
; Enable extended properties
- mov eax, cr4
- or eax, 0x0000000B0 ; PGE (Bit 7), PAE (Bit 5), and PSE (Bit 4)
- mov cr4, eax
+; mov eax, cr4
+; or eax, 0x0000000B0 ; PGE (Bit 7), PAE (Bit 5), and PSE (Bit 4)
+; mov cr4, eax
; Point cr3 at PML4
- mov eax, 0x00002008 ; Write-thru enabled (Bit 3)
- mov cr3, eax
-
-; Enable long mode and SYSCALL/SYSRET
- mov ecx, 0xC0000080 ; EFER MSR number
- rdmsr ; Read EFER
- or eax, 0x00000101 ; LME (Bit 8)
- wrmsr ; Write EFER
-
-; Enable paging to activate long mode
- mov eax, cr0
- or eax, 0x80000000 ; PG (Bit 31)
- mov cr0, eax
-
- jmp SYS64_CODE_SEL:start64 ; Jump to 64-bit mode
-
-
-align 16
-
-; =============================================================================
-; 64-bit mode
-BITS 64
+ mov rax, 0x00002008 ; Write-thru enabled (Bit 3)
+ mov cr3, rax
-start64:
xor eax, eax ; aka r0
xor ebx, ebx ; aka r3
xor ecx, ecx ; aka r1
@@ -254,23 +372,27 @@ start64:
xor r14, r14
xor r15, r15
- mov ds, ax ; Clear the legacy segment registers
+ mov ax, 0x10 ; TODO Is this needed?
+ mov ds, ax
mov es, ax
mov ss, ax
mov fs, ax
mov gs, ax
- mov rax, clearcs64 ; Do a proper 64-bit jump. Should not be needed as the ...
- jmp rax ; jmp SYS64_CODE_SEL:start64 would have sent us ...
- nop ; out of compatibility mode and into 64-bit mode
+ ; Set CS with a far return
+ push SYS64_CODE_SEL
+ push clearcs64
+ retfq
clearcs64:
- xor eax, eax
lgdt [GDTR64] ; Reload the GDT
-; Save the Boot Mode (it will be 'U' if started via UEFI)
- mov al, [0x8005]
- mov [p_BootMode], al ; Save the byte as a Boot Mode flag
+ ; Debug - Set screen to purple after loading GDT and PML4
+ mov rdi, [0x00005F00] ; Frame buffer base
+ mov rcx, [0x00005F08] ; Frame buffer size
+ shr rcx, 2 ; Quick divide by 4
+ mov eax, 0x00F020F0 ; 0x00RRGGBB
+ rep stosd
; Patch Pure64 AP code ; The AP's will be told to start execution at 0x8000
mov edi, start ; We need to remove the BSP Jump call to get the AP's
@@ -278,64 +400,37 @@ clearcs64:
stosd
stosd ; Write 8 bytes in total to overwrite the 'far jump' and marker
- mov al, [p_BootMode]
- cmp al, 'U'
- je uefi_memmap
-; Process the E820 memory map to find all possible 2MiB pages that are free to use
-; Build a map at 0x400000
- xor ecx, ecx
- xor ebx, ebx ; Counter for pages found
- mov esi, 0x00006000 ; E820 Map location
-nextentry:
- add esi, 16 ; Skip ESI to type marker
- mov eax, [esi] ; Load the 32-bit type marker
- cmp eax, 0 ; End of the list?
- je end820
- cmp eax, 1 ; Is it marked as free?
- je processfree
- add esi, 16 ; Skip ESI to start of next entry
- jmp nextentry
-
-processfree:
- sub esi, 16
- mov rax, [rsi] ; Physical start address
- add esi, 8
- mov rcx, [rsi] ; Physical length
- add esi, 24
- shr rcx, 21 ; Convert bytes to # of 2 MiB pages
- cmp rcx, 0 ; Do we have at least 1 page?
- je nextentry
- shl rax, 1
- mov edx, 0x1FFFFF
- not rdx ; Clear bits 20 - 0
- and rax, rdx
- ; At this point RAX points to the start and RCX has the # of pages
- shr rax, 21 ; page # to start on
- mov rdi, 0x400000 ; 4 MiB into physical memory
- add rdi, rax
- mov al, 1
- add ebx, ecx
- rep stosb
- jmp nextentry
-
-end820:
- shl ebx, 1
+; Parse the Memory Map at 0x200000
+uefi_memmap:
+ xor ebx, ebx ; Running counter of 4K pages
+ mov esi, 0x200000
+uefi_memmap_next:
+ mov rax, [rsi]
+ cmp rax, 7
+ je uefi_memmap_conventional
+ cmp rax, 0
+ je uefi_memmap_end
+uefi_memmap_skip:
+ add esi, 48
+ jmp uefi_memmap_next
+uefi_memmap_conventional:
+ mov rax, [rsi + 24]
+ add rbx, rax
+ jmp uefi_memmap_skip
+uefi_memmap_end:
+ shr rbx, 8
mov dword [p_mem_amount], ebx
- shr ebx, 1
- jmp memmap_end
-uefi_memmap: ; TODO fix this as it is a terrible hack
- mov rdi, 0x400000
- mov al, 1
- mov rcx, 32
- rep stosb
- mov ebx, 64
- mov dword [p_mem_amount], ebx
-memmap_end:
+; FIXME - Don't hardcode the RAM to 64MiB
+ mov eax, 64
+ mov dword [p_mem_amount], eax
-; Create the high memory map
- mov rcx, rbx
- shr rcx, 9 ; TODO - This isn't the exact math but good enough
+; Create the High Page-Directory-Pointer-Table Entries (PDPTE)
+; High PDPTE is stored at 0x0000000000004000, create the first entry there
+; A single PDPTE can map 1GiB with 2MiB pages
+; A single PDPTE is 8 bytes in length
+; 1 entry is created to map the first 1GiB of physical RAM to 0xFFFF800000000000
+; FIXME - Create more than just one PDPE depending on the amount of RAM in the system
add rcx, 1 ; number of PDPE's to make.. each PDPE maps 1GB of physical memory
mov edi, 0x00004000 ; location of high PDPE
mov eax, 0x00020007 ; location of first high PD. Bits (0) P, 1 (R/W), and 2 (U/S) set
@@ -346,36 +441,22 @@ create_pdpe_high:
cmp ecx, 0
jne create_pdpe_high
-; Create the high PD entries
-; EBX contains the number of pages that should exist in the map, once they are all found bail out
- xor ecx, ecx
- xor eax, eax
- xor edx, edx
- mov edi, 0x00020000 ; Location of high PD entries
- mov esi, 0x00400000 ; Location of free pages map
-
-pd_high:
- cmp rdx, rbx ; Compare mapped pages to max pages
- je pd_high_done
- lodsb
- cmp al, 1
- je pd_high_entry
- add rcx, 1
- jmp pd_high
-
-pd_high_entry:
+; Create the High Page-Directory Entries (PDE).
+; A single PDE can map 2MiB of RAM
+; A single PDE is 8 bytes in length
+; FIXME - Map more than 64MiB depending on the amount of RAM in the system
+ mov edi, 0x00020000 ; Location of first PDE
mov eax, 0x0000008F ; Bits 0 (P), 1 (R/W), 2 (U/S), 3 (PWT), and 7 (PS) set
- shl rcx, 21
- add rax, rcx
- shr rcx, 21
+ add rax, 0x00400000 ; Start at 4MiB in
+ mov ecx, 32 ; Create 32 2MiB page maps
+pde_high: ; Create a 2MiB page
stosq
- add rcx, 1
- add rdx, 1 ; We have mapped a valid page
- jmp pd_high
-
-pd_high_done:
+ add rax, 0x00200000 ; Increment by 2MiB
+ dec ecx
+ cmp ecx, 0
+ jne pde_high
-; Build a temporary IDT
+; Build the IDT
xor edi, edi ; create the 64-bit IDT (at linear address 0x0000000000000000)
mov rcx, 32
@@ -476,6 +557,12 @@ clearmapnext:
and cl, 1
mov byte [p_x2APIC], cl
+ mov rdi, [0x00005F00] ; Frame buffer base
+ mov rcx, [0x00005F08] ; Frame buffer size
+ shr rcx, 2 ; Quick divide by 4
+ mov eax, 0x00206020 ; 0x00RRGGBB
+ rep stosd
+
call init_acpi ; Find and process the ACPI tables
call init_cpu ; Configure the BSP CPU
@@ -527,12 +614,17 @@ clearmapnext:
mov rax, [p_LocalAPICAddress]
stosq
+ ; TODO - Copy the data we received from GOP
+ ; FB
+ ; FBS
+ ; X
+ ; Y
mov di, 0x5080
- mov eax, [VBEModeInfoBlock.PhysBasePtr] ; Base address of video memory (if graphics mode is set)
+ mov eax, [0x00005F00] ; Base address of video memory
stosd
- mov eax, [VBEModeInfoBlock.XResolution] ; X and Y resolution (16-bits each)
+ mov eax, [0x00005F00 + 0x10] ; X and Y resolution (16-bits each)
stosd
- mov al, [VBEModeInfoBlock.BitsPerPixel] ; Color depth
+ mov al, 32 ; Color depth
stosb
mov di, 0x5090
@@ -546,23 +638,8 @@ clearmapnext:
rep movsq ; Copy 8 bytes at a time
; Output message via serial port
- cld ; Clear the direction flag.. we want to increment through the string
- mov dx, 0x03F8 ; Address of first serial port
- mov rsi, message ; Location of message
- mov cx, 11 ; Length of message
-serial_nextchar:
- jrcxz serial_done ; If RCX is 0 then the function is complete
- add dx, 5 ; Offset to Line Status Register
- in al, dx
- sub dx, 5 ; Back to to base
- and al, 0x20
- cmp al, 0
- je serial_nextchar
- dec cx
- lodsb ; Get char from string and store in AL
- out dx, al ; Send the char to the serial port
- jmp serial_nextchar
-serial_done:
+ mov rsi, message_ok ; Location of message
+ call debug_msg
; Clear all registers (skip the stack pointer)
xor eax, eax ; These 32-bit calls also clear the upper bits of the 64-bit registers
@@ -580,7 +657,7 @@ serial_done:
xor r13, r13
xor r14, r14
xor r15, r15
- jmp 0x00100000
+ jmp 0x00100000 ; Done with Pure64, jump to the kernel
%include "init/acpi.asm"
@@ -590,6 +667,60 @@ serial_done:
%include "interrupt.asm"
%include "sysvar.asm"
+
+; -----------------------------------------------------------------------------
+; debug_msg_char - Send a single char via the serial port
+; IN: AL = Byte to send
+debug_msg_char:
+ pushf
+ push rdx
+ push rax ; Save the byte
+ mov dx, 0x03F8 ; Address of first serial port
+debug_msg_char_wait:
+ add dx, 5 ; Offset to Line Status Register
+ in al, dx
+ sub dx, 5 ; Back to to base
+ and al, 0x20
+ cmp al, 0
+ je debug_msg_char_wait
+ pop rax ; Restore the byte
+ out dx, al ; Send the char to the serial port
+debug_msg_char_done:
+ pop rdx
+ popf
+ ret
+; -----------------------------------------------------------------------------
+
+
+; -----------------------------------------------------------------------------
+; debug_msg_char - Send a message via the serial port
+; IN: RSI = Location of message
+debug_msg:
+ pushf
+ push rdx
+ push rax
+ cld ; Clear the direction flag.. we want to increment through the string
+ mov dx, 0x03F8 ; Address of first serial port
+debug_msg_next:
+ add dx, 5 ; Offset to Line Status Register
+ in al, dx
+ sub dx, 5 ; Back to to base
+ and al, 0x20
+ cmp al, 0
+ je debug_msg_next
+ lodsb ; Get char from string and store in AL
+ cmp al, 0
+ je debug_msg_done
+ out dx, al ; Send the char to the serial port
+ jmp debug_msg_next
+debug_msg_done:
+ pop rax
+ pop rdx
+ popf
+ ret
+; -----------------------------------------------------------------------------
+
+
EOF:
db 0xDE, 0xAD, 0xC0, 0xDE
diff --git a/src/sysvar.asm b/src/sysvar.asm
index 4c5f380..287291a 100644
--- a/src/sysvar.asm
+++ b/src/sysvar.asm
@@ -6,13 +6,14 @@
; =============================================================================
-message: db 10, 'Pure64 OK', 10
+message_pure64: db 10, 'Pure64 ', 0
+message_ok: db 'OK', 10, 0
+message_error: db 'Error', 10, 0
;CONFIG
cfg_smpinit: db 1 ; By default SMP is enabled. Set to 0 to disable.
; Memory locations
-E820Map: equ 0x0000000000004000
InfoMap: equ 0x0000000000005000
IM_PCIE: equ 0x0000000000005400 ; 16 bytes per entry
IM_IOAPICAddress: equ 0x0000000000005600 ; 16 bytes per entry
@@ -57,7 +58,11 @@ dq 0x00CF92000000FFFF ; 32-bit data descriptor
; 55 Granularity 4KiB, 54 Size 32bit, 47 Present, 44 Code/Data, 41 Writeable
gdt32_end:
-; -----------------------------------------------------------------------------
+align 16
+tGDTR64: ; Global Descriptors Table Register
+dw gdt64_end - gdt64 - 1 ; limit of GDT (size minus one)
+dq gdt64 ; linear address of GDT
+
align 16
GDTR64: ; Global Descriptors Table Register
dw gdt64_end - gdt64 - 1 ; limit of GDT (size minus one)
@@ -75,45 +80,7 @@ gdt64_end:
IDTR64: ; Interrupt Descriptor Table Register
dw 256*16-1 ; limit of IDT (size minus one) (4096 bytes - 1)
dq 0x0000000000000000 ; linear address of IDT
-; -----------------------------------------------------------------------------
-
-; VESA
-; Mandatory information for all VBE revisions
-VBEModeInfoBlock.ModeAttributes equ VBEModeInfoBlock + 0 ; DW - mode attributes
-VBEModeInfoBlock.WinAAttributes equ VBEModeInfoBlock + 2 ; DB - window A attributes
-VBEModeInfoBlock.WinBAttributes equ VBEModeInfoBlock + 3 ; DB - window B attributes
-VBEModeInfoBlock.WinGranularity equ VBEModeInfoBlock + 4 ; DW - window granularity in KB
-VBEModeInfoBlock.WinSize equ VBEModeInfoBlock + 6 ; DW - window size in KB
-VBEModeInfoBlock.WinASegment equ VBEModeInfoBlock + 8 ; DW - window A start segment
-VBEModeInfoBlock.WinBSegment equ VBEModeInfoBlock + 10 ; DW - window B start segment
-VBEModeInfoBlock.WinFuncPtr equ VBEModeInfoBlock + 12 ; DD - real mode pointer to window function
-VBEModeInfoBlock.BytesPerScanLine equ VBEModeInfoBlock + 16 ; DW - bytes per scan line
-; Mandatory information for VBE 1.2 and above
-VBEModeInfoBlock.XResolution equ VBEModeInfoBlock + 18 ; DW - horizontal resolution in pixels or characters
-VBEModeInfoBlock.YResolution equ VBEModeInfoBlock + 20 ; DW - vertical resolution in pixels or characters
-VBEModeInfoBlock.XCharSize equ VBEModeInfoBlock + 22 ; DB - character cell width in pixels
-VBEModeInfoBlock.YCharSize equ VBEModeInfoBlock + 23 ; DB - character cell height in pixels
-VBEModeInfoBlock.NumberOfPlanes equ VBEModeInfoBlock + 24 ; DB - number of memory planes
-VBEModeInfoBlock.BitsPerPixel equ VBEModeInfoBlock + 25 ; DB - bits per pixel
-VBEModeInfoBlock.NumberOfBanks equ VBEModeInfoBlock + 26 ; DB - number of banks
-VBEModeInfoBlock.MemoryModel equ VBEModeInfoBlock + 27 ; DB - memory model type
-VBEModeInfoBlock.BankSize equ VBEModeInfoBlock + 28 ; DB - bank size in KB
-VBEModeInfoBlock.NumberOfImagePages equ VBEModeInfoBlock + 29 ; DB - number of image pages
-VBEModeInfoBlock.Reserved equ VBEModeInfoBlock + 30 ; DB - reserved (0x00 for VBE 1.0-2.0, 0x01 for VBE 3.0)
-; Direct Color fields (required for direct/6 and YUV/7 memory models)
-VBEModeInfoBlock.RedMaskSize equ VBEModeInfoBlock + 31 ; DB - size of direct color red mask in bits
-VBEModeInfoBlock.RedFieldPosition equ VBEModeInfoBlock + 32 ; DB - bit position of lsb of red mask
-VBEModeInfoBlock.GreenMaskSize equ VBEModeInfoBlock + 33 ; DB - size of direct color green mask in bits
-VBEModeInfoBlock.GreenFieldPosition equ VBEModeInfoBlock + 34 ; DB - bit position of lsb of green mask
-VBEModeInfoBlock.BlueMaskSize equ VBEModeInfoBlock + 35 ; DB - size of direct color blue mask in bits
-VBEModeInfoBlock.BlueFieldPosition equ VBEModeInfoBlock + 36 ; DB - bit position of lsb of blue mask
-VBEModeInfoBlock.RsvdMaskSize equ VBEModeInfoBlock + 37 ; DB - size of direct color reserved mask in bits
-VBEModeInfoBlock.RsvdFieldPosition equ VBEModeInfoBlock + 38 ; DB - bit position of lsb of reserved mask
-VBEModeInfoBlock.DirectColorModeInfo equ VBEModeInfoBlock + 39 ; DB - direct color mode attributes
-; Mandatory information for VBE 2.0 and above
-VBEModeInfoBlock.PhysBasePtr equ VBEModeInfoBlock + 40 ; DD - physical address for flat memory frame buffer
-VBEModeInfoBlock.Reserved1 equ VBEModeInfoBlock + 44 ; DD - Reserved - always set to 0
-VBEModeInfoBlock.Reserved2 equ VBEModeInfoBlock + 48 ; DD - Reserved - always set to 0
+
; =============================================================================
; EOF