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 Mode2201 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. - - - - - + + + + + +
VariableVariable SizeDescription
Starting Address64-bitThe starting address for this record
Length64-bitThe length of memory for this record
Memory Type32-bitType 1 is usable memory, Type 2 is not usable
Extended Attributes32-bitACPI 3.0 Extended Attributes bitfield
Padding64-bitPadding for 32-byte alignment
Type64-bitThe type of the memory region
Physical Start64-bitPhysical Address - 4K aligned
Virtual Start64-bitVirtual Address - 4K aligned
NumberOfPages64-bitThe number of 4K pages in this section
Attribute64-bitSee document linked below
Padding64-bitPadding
-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