diff --git a/docs/README.md b/docs/README.md index 492ad39..15413d8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -183,7 +183,7 @@ The Pure64 information table is located at `0x0000000000005000` and ends at `0x0 Memory AddressVariable SizeNameDescription 0x500064-bitACPIAddress of the ACPI tables 0x500832-bitCPU_BSP_IDAPIC ID of the BSP -0x501016-bitCPU_SPEEDSpeed of the CPUs in MegaHertz (MHz) +0x500C - 0x5011  For future use 0x501216-bitCPU_CORES_ACTIVEThe number of CPU cores that were activated in the system 0x501416-bitCPU_CORES_DETECTThe number of CPU cores that were detected in the system 0x50168-bitCPU_MEM_PHYSICALThe number of bits that are valid for a physical address @@ -200,6 +200,8 @@ The Pure64 information table is located at `0x0000000000005000` and ends at `0x0 0x504C16-bitHPET Counter MinumumMinimum Counter for the High Precision Event Timer 0x504E8-bitHPET CountersNumber of Counter in the High Precision Event Timer 0x504F  For future use +0x505064-bitT0RDTSC at Pure64 start +0x505864-bitT1RDTSC at Pure64 end 0x506064-bitLAPICLocal APIC address 0x5068 - 0x507F  For future use 0x508064-bitVIDEO_BASEBase memory for video (if graphics mode set) diff --git a/src/boot/bios-pxe.asm b/src/boot/bios-pxe.asm index f695018..184e5b9 100644 --- a/src/boot/bios-pxe.asm +++ b/src/boot/bios-pxe.asm @@ -121,7 +121,7 @@ check_A20: cmp ax, 0x004F ; Return value in AX should equal 0x004F if command supported and successful jne halt - cmp byte [VBEModeInfoBlock.BitsPerPixel], 24 ; Make sure this matches the number of bits for the mode! + cmp byte [VBEModeInfoBlock.BitsPerPixel], 32 ; Make sure this matches the number of bits for the mode! jne halt ; If set bit mode was unsuccessful then bail out or bx, 0x4000 ; Use linear/flat frame buffer model (set bit 14) mov ax, 0x4F02 ; SET SuperVGA VIDEO MODE - http://www.ctyme.com/intr/rb-0275.htm diff --git a/src/boot/uefi.asm b/src/boot/uefi.asm index 9588654..877eecf 100644 --- a/src/boot/uefi.asm +++ b/src/boot/uefi.asm @@ -139,7 +139,7 @@ EntryPoint: ; Find the address of the ACPI data from the UEFI configuration table mov rax, [EFI_SYSTEM_TABLE] mov rcx, [rax + EFI_SYSTEM_TABLE_NUMBEROFENTRIES] - shl rcx, 3 ; Quick multiply by 4 + shl rcx, 3 ; Quick multiply by 8 mov rsi, [CONFIG] nextentry: dec rcx @@ -367,6 +367,10 @@ get_memmap: ; Stop interrupts cli + rdtsc ; Read the timestamp counter into EDX:EAX + mov [0x5FFC], edx + mov [0x5FF8], eax + ; Copy Pure64 to the correct memory address mov rsi, PAYLOAD mov rdi, 0x8000 diff --git a/src/init/acpi.asm b/src/init/acpi.asm index 301957d..198b102 100644 --- a/src/init/acpi.asm +++ b/src/init/acpi.asm @@ -17,16 +17,32 @@ init_acpi: je foundACPIfromUEFI ; If so, jump - otherwise fall thru for BIOS ; Find the ACPI RSDP Structure on a BIOS system -; It's supposed to be somewhere in the first MiB of memory but some systems don't adhere to that - mov esi, 0x00000007 ; Start looking for the Root System Description Pointer Structure -searchingforACPI: - sub esi, 0x7 - lodsq ; Load a quad word from RSI and store in RAX, then increment RSI by 8 - cmp rax, rbx ; Verify the Signature +; The RSDP is potentially located in 2 places: +; 1) Within the first 1 KiB of the EBDA (Extended BIOS Data Area; a 2 byte address to the start of it is located at 0x40E) +; 2) In the BIOS ROM memory region from 0x000E0000 to 0x000FFFFF +; The signature always starts on a 16 byte boundary. +; If the system does not adhere to the standard then this will fail. + +; ; Check EBDA (first 1KB) +; mov rsi, [p_EBDA] +; mov ecx, 64 ; 0x400 / 16 = 64 iterations +;acpi_search_ebda: +; cmp qword [rsi], rbx ; Compare the Signature +; je foundACPI +; add esi, 16 +; dec ecx +; jnz acpi_search_ebda + + ; Check BIOS ROM area (0xE0000–0xFFFFF) + mov esi, 0xE0000 ; Start of BIOS ROM + mov ecx, 8192 ; 0x20000 / 16 = 8192 iterations +acpi_search_rom: + cmp qword [rsi], rbx ; Compare the Signature je foundACPI - cmp esi, 0xFFFFFFF8 ; Keep looking until we get here - ja noACPI ; ACPI tables couldn't be found, fail - jmp searchingforACPI + add esi, 16 + dec ecx + jnz acpi_search_rom + jmp noACPI ; ACPI tables couldn't be found, fail ; Find the ACPI RSDP Structure on a UEFI system foundACPIfromUEFI: @@ -41,7 +57,6 @@ foundACPI: ; Found a Pointer Structure, verify the checksum push rbx xor ebx, ebx mov ecx, 20 ; As per the spec only the first 20 bytes matter - sub esi, 8 ; Bytes 0 thru 19 must sum to zero nextchecksum: lodsb ; Get a byte add bl, al ; Add it to the running total @@ -51,11 +66,8 @@ nextchecksum: pop rbx pop rsi ; Restore the RSDP location cmp al, 0 ; Verify the checksum is zero - jne searchingforACPI ; Checksum didn't check out? Keep looking for a valid record - - lodsb ; Checksum - lodsd ; OEMID (First 4 bytes) - lodsw ; OEMID (Last 2 bytes) + jne noACPI ; Checksum invalid? Bail out + add esi, 15 ; Skip to the Revision byte lodsb ; Revision (0 is v1.0, 1 is v2.0, 2 is v3.0, etc) cmp al, 0 je foundACPIv1 ; If AL is 0 then the system is using ACPI v1.0 @@ -119,17 +131,13 @@ nextACPITable: pop rsi ; Pop an Entry address from the stack lodsd add ecx, 1 - mov ebx, 'APIC' ; Signature for the Multiple APIC Description Table - cmp eax, ebx + cmp eax, 'APIC' ; Signature for the Multiple APIC Description Table je foundAPICTable - mov ebx, 'HPET' ; Signature for the HPET Description Table - cmp eax, ebx + cmp eax, 'HPET' ; Signature for the HPET Description Table je foundHPETTable - mov ebx, 'MCFG' ; Signature for the PCIe Enhanced Configuration Mechanism - cmp eax, ebx + cmp eax, 'MCFG' ; Signature for the PCIe Enhanced Configuration Mechanism je foundMCFGTable - mov ebx, 'FACP' ; Signature for the Fixed ACPI Description Table - cmp eax, ebx + cmp eax, 'FACP' ; Signature for the Fixed ACPI Description Table je foundFADTTable jmp nextACPITable @@ -320,28 +328,38 @@ parseAPICTable_done: ; ----------------------------------------------------------------------------- ; High Precision Event Timer (HPET) +; +; ACPI Memory Layout +; 4 byte - Signature +; 4 byte - Length of HPET in bytes +; 1 byte - Revision +; 1 byte - Checksum +; 6 byte - OEMID +; 8 byte - OEM Table ID +; 4 byte - OEM Revision +; 4 byte - Creator ID +; 4 byte - Creator Revision +; 1 byte - Hardware Revision ID +; 1 byte - # of Comparators (5:0), COUNT_SIZE_CAP (6), Legacy IRQ (7) +; 2 byte - PCI Vendor ID +; 1 byte - Address Space ID +; 1 byte - Register bit width +; 1 byte - Register bit offset +; 1 byte - Reserved +; 8 byte - Base Address Value +; 1 byte - HPET Number +; 2 byte - Main Counter Minimum +; 1 byte - Page Protection +; ; http://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/software-developers-hpet-spec-1-0a.pdf parseHPETTable: - lodsd ; Length of HPET in bytes - lodsb ; Revision - lodsb ; Checksum - lodsd ; OEMID (First 4 bytes) - lodsw ; OEMID (Last 2 bytes) - lodsq ; OEM Table ID - lodsd ; OEM Revision - lodsd ; Creator ID - lodsd ; Creator Revision - - lodsb ; Hardware Revision ID - lodsb ; # of Comparators (5:0), COUNT_SIZE_CAP (6), Legacy IRQ (7) - lodsw ; PCI Vendor ID - lodsd ; Generic Address Structure - lodsq ; Base Address Value + ; At this point RSI points to the Length of HPET in bytes + mov ebx, [rsi] ; Should be 0x38 + mov rax, [rsi + 40] ; Base Address Value mov [p_HPET_Address], rax ; Save the Address of the HPET - lodsb ; HPET Number - lodsw ; Main Counter Minimum + mov ax, [rsi + 49] ; Main Counter Minimum mov [p_HPET_CounterMin], ax ; Save the Counter Minimum - lodsb ; Page Protection And OEM Attribute + add rsi, rbx ; Add the table length to RSI ret ; ----------------------------------------------------------------------------- diff --git a/src/init/serial.asm b/src/init/serial.asm index b784e2a..c4c674d 100644 --- a/src/init/serial.asm +++ b/src/init/serial.asm @@ -14,7 +14,7 @@ init_serial: ; Enable divisor register for setting baud rate mov dx, COM_PORT_LINE_CONTROL - mov dl, 0x80 ; DLB (7 set) + mov al, 0x80 ; DLB (7 set) out dx, al ; Send the divisor (baud rate will be 115200 / divisor) @@ -27,7 +27,7 @@ init_serial: ; Disable divisor register and set values mov dx, COM_PORT_LINE_CONTROL - mov al, 00000111b ; 8 data bits (0-1 set), one stop bit (2 set), no parity (3-5 clear), DLB (7 clear) + mov al, 00000011b ; 8 data bits (0-1 set), one stop bit (2 clear), no parity (3-5 clear), DLB (7 clear) out dx, al ; Disable modem control diff --git a/src/init/smp.asm b/src/init/smp.asm index 1903817..beb2873 100644 --- a/src/init/smp.asm +++ b/src/init/smp.asm @@ -9,13 +9,16 @@ init_smp: ; Check if we want the AP's to be enabled.. if not then skip to end cmp byte [cfg_smpinit], 1 ; Check if SMP should be enabled - jne noMP ; If not then skip SMP init + jne init_smp_done ; If not then skip SMP init + ; Check if multiple CPUs were detected via the ACPI tables + xor ecx, ecx + mov cx, [p_cpu_detected] + cmp cx, 1 + je init_smp_done ; Only 1 CPU detected, skip SMP init mov edx, [p_BSP] ; Get the BSP APIC ID mov esi, IM_DetectedCoreIDs ; List of 32-bit APIC IDs xor eax, eax - xor ecx, ecx - mov cx, [p_cpu_detected] smp_send_INIT: cmp cx, 0 je smp_send_INIT_done @@ -60,8 +63,8 @@ smp_send_INIT_skipcore: smp_send_INIT_done: - ; Wait 500 microseconds - mov eax, 500 + ; Wait + mov eax, 500 ; 500 microseconds (0.5ms) call timer_delay mov esi, IM_DetectedCoreIDs @@ -97,9 +100,9 @@ smp_send_SIPI_verify: smp_send_SIPI_x2APIC: ; Send 'Startup' IPI to destination using vector 0x08 to specify entry-point is at the memory-address 0x00008000 push rcx - mov ecx, APIC_ICR ; Interrupt Command Register (ICR); bits 63-0 + mov ecx, APIC_ICR ; Interrupt Command Register (ICR); bits 63-0 shl rax, 32 - mov ax, 0x4608 ; Vector 0x08 + mov ax, 0x4608 ; Vector 0x08 call apic_write pop rcx @@ -111,28 +114,11 @@ smp_send_SIPI_skipcore: smp_send_SIPI_done: - ; Wait 10000 microseconds for the AP's to finish - mov eax, 10000 - call timer_delay - -noMP: - - ; Calculate base speed of CPU - cpuid - xor edx, edx - xor eax, eax - rdtsc - push rax - mov rax, 1024 + ; Wait for the AP's to finish + mov eax, 10000 ; 10000 microseconds (10ms) call timer_delay - rdtsc - pop rdx - sub rax, rdx - xor edx, edx - mov rcx, 1024 - div rcx - mov [p_cpu_speed], ax +init_smp_done: ret diff --git a/src/init/timer.asm b/src/init/timer.asm index 0b87585..e925abd 100644 --- a/src/init/timer.asm +++ b/src/init/timer.asm @@ -65,10 +65,16 @@ init_timer_hpet: ; Calculate the HPET frequency mov rbx, rax ; Move Counter Clock Period to RBX xor rdx, rdx - mov rax, 1000000000000000 ; femotoseconds per second - div rbx ; RDX:RAX / RBX + mov rax, 1000000000000000 ; femtoseconds per second + div rbx ; RDX:RAX / RBX = frequency in Hz mov [p_HPET_Frequency], eax ; Save the HPET frequency + ; Precompute cycles per microsecond (freq_Hz / 1,000,000) + xor rdx, rdx + mov ecx, 1000000 + div rcx + mov [p_HPET_CyclesPerUs], rax + ; Disable interrupts on all timers xor ebx, ebx mov bl, [p_HPET_Timers] @@ -140,24 +146,18 @@ hpet_delay: push rax mov rbx, rax ; Save delay to RBX - xor edx, edx - xor ecx, ecx - call hpet_read ; Get HPET General Capabilities and ID Register - shr rax, 32 - mov rcx, rax ; RCX = RAX >> 32 (timer period in femtoseconds) - mov rax, 1000000000 - div rcx ; Divide 1000000000 (RDX:RAX) / RCX (converting from period in femtoseconds to frequency in MHz) - mul rbx ; RAX *= RBX, should get number of HPET cycles to wait, save result in RBX + mov rax, [p_HPET_CyclesPerUs] ; Use precomputed cycles per microsecond + mul rbx ; RAX = cycles to wait mov rbx, rax mov ecx, HPET_MAIN_COUNTER call hpet_read ; Get HPET counter in RAX - add rbx, rax ; RBX += RAX Until when to wait + add rbx, rax ; RBX = target counter value hpet_delay_loop: ; Stay in this loop until the HPET timer reaches the expected value + pause mov ecx, HPET_MAIN_COUNTER call hpet_read ; Get HPET counter in RAX - cmp rax, rbx ; If RAX >= RBX then jump to end, otherwise jump to loop - jae hpet_delay_end - jmp hpet_delay_loop + cmp rax, rbx + jb hpet_delay_loop hpet_delay_end: pop rax @@ -291,6 +291,7 @@ kvm_delay: call kvm_get_usec add rbx, rax ; Add elapsed time kvm_delay_wait: + pause call kvm_get_usec cmp rax, rbx jb kvm_delay_wait diff --git a/src/interrupt.asm b/src/interrupt.asm index 0897f80..dda8092 100644 --- a/src/interrupt.asm +++ b/src/interrupt.asm @@ -159,27 +159,5 @@ exception_gate_main_hang: ; ----------------------------------------------------------------------------- -; ----------------------------------------------------------------------------- -; create_gate -; rax = address of handler -; rdi = gate # to configure -create_gate: - push rdi - push rax - - shl rdi, 4 ; quickly multiply rdi by 16 - stosw ; store the low word (15:0) - shr rax, 16 - add rdi, 4 ; skip the gate marker - stosw ; store the high word (31:16) - shr rax, 16 - stosd ; store the high dword (63:32) - - pop rax - pop rdi - ret -; ----------------------------------------------------------------------------- - - ; ============================================================================= ; EOF diff --git a/src/pure64.asm b/src/pure64.asm index 655bf0c..1080b02 100644 --- a/src/pure64.asm +++ b/src/pure64.asm @@ -64,6 +64,10 @@ bootmode: %ifdef BIOS mov [p_BootDisk], bh ; Save disk from where system was booted from + rdtsc ; Read the timestamp counter into EDX:EAX + mov [0x5FFC], edx + mov [0x5FF8], eax + mov eax, 16 ; Set the correct segment registers mov ds, ax mov es, ax @@ -113,11 +117,6 @@ bootmode: stosw ; BitsPerPixel %endif - ; 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 @@ -145,8 +144,7 @@ pdpte_low_32: pop eax add eax, 0x00001000 ; 4KiB later (512 records x 8 bytes) dec ecx - cmp ecx, 0 - jne pdpte_low_32 + jnz pdpte_low_32 ; Create the temporary low Page-Directory Entries (PDE). ; A single PDE can map 2MiB of RAM @@ -200,17 +198,17 @@ 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 mov [p_BootDisk], bh mov ax, 0x03 ; Set flags for legacy ports (in case of no ACPI data) mov [p_IAPC_BOOT_ARCH], ax + ; Save EBDA segment from BDA (BIOS Data Area) + movzx esi, word [0x040E] ; EBDA segment + shl esi, 4 ; Convert to proper address + mov [p_EBDA], rsi + ; Mask all PIC interrupts mov al, 0xFF out 0x21, al @@ -291,17 +289,6 @@ boot_uefi: msg_boot_done: %endif -; 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 - mov edi, eax - rep stosd - -; Clear memory for the Page Descriptor Entries (0x10000 - 0x5FFFF) - mov edi, 0x00010000 - mov ecx, 81920 - rep stosd ; Write 320KiB - ; Copy the GDT to its final location in memory mov esi, gdt64 mov edi, 0x00001000 ; GDT address @@ -376,7 +363,7 @@ pml4_low_1GB: dec ecx jnz pml4_low_1GB - mov ecx, 8191 ; number of PDPE's to make.. each PDPE maps 1GiB of physical memory + mov ecx, 7 ; number of PDPE's to make.. each PDPE maps 1GiB of physical memory mov edi, 0x00010000 ; location of low PDPE mov eax, 0x00000083 ; Bits 0 (P), 1 (R/W), 7 (PS) pdpte_low_1GB: ; Create a 1GiB page @@ -435,41 +422,33 @@ clearcs64: ; Build the IDT xor edi, edi ; create the 64-bit IDT (at linear address 0x0000000000000000) + ; exception_gate and interrupt_gate are within the Pure64 binary (0x8000-0x97FF), + ; so bits 31:16 and 63:32 of their addresses are always 0. mov ecx, 32 make_exception_gates: ; make gates for exception handlers mov eax, exception_gate - push rax ; save the exception gate to the stack for later use stosw ; store the low word (15:0) of the address mov ax, SYS64_CODE_SEL stosw ; store the segment selector mov ax, 0x8E00 stosw ; store exception gate marker - pop rax ; get the exception gate back - shr rax, 16 - stosw ; store the high word (31:16) of the address - shr rax, 16 - stosd ; store the extra high dword (63:32) of the address. xor eax, eax - stosd ; reserved + stosw ; store the high word (31:16) of the address (always 0) + stosq ; store bits 63:32 of address + reserved (always 0) dec ecx jnz make_exception_gates mov ecx, 256-32 make_interrupt_gates: ; make gates for the other interrupts mov eax, interrupt_gate - push rax ; save the interrupt gate to the stack for later use stosw ; store the low word (15:0) of the address mov ax, SYS64_CODE_SEL stosw ; store the segment selector mov ax, 0x8F00 stosw ; store interrupt gate marker - pop rax ; get the interrupt gate back - shr rax, 16 - stosw ; store the high word (31:16) of the address - shr rax, 16 - stosd ; store the extra high dword (63:32) of the address. xor eax, eax - stosd ; reserved + stosw ; store the high word (31:16) of the address (always 0) + stosq ; store bits 63:32 of address + reserved (always 0) dec ecx jnz make_interrupt_gates @@ -684,8 +663,7 @@ create_pdpe_high: stosq add rax, 0x00001000 ; 4K later (512 records x 8 bytes) dec ecx - cmp ecx, 0 - jne create_pdpe_high + jnz create_pdpe_high ; Create the High Page-Directory Entries (PDE). ; A single PDE can map 2MiB of RAM @@ -705,15 +683,14 @@ pde_next_range: sub rcx, 2 ; Subtract 2 MiB from the length skipfirst4mb: shr ecx, 1 ; Quick divide by 2 for 2 MB pages + cmp ecx, 0 + je pde_next_range add rax, 0x00000083 ; Bits 0 (P), 1 (R/W), and 7 (PS) set pde_high: ; Create a 2MiB page stosq add rax, 0x00200000 ; Increment by 2MiB - cmp ecx, 0 - je pde_next_range dec ecx - cmp ecx, 0 - jne pde_high + jnz pde_high jmp pde_next_range pde_end: @@ -807,9 +784,7 @@ pde_end: mov eax, [p_BSP] stosd - mov di, 0x5010 - mov ax, [p_cpu_speed] - stosw + mov di, 0x5012 mov ax, [p_cpu_activated] stosw mov ax, [p_cpu_detected] @@ -850,6 +825,10 @@ no_address_size: mov al, [p_HPET_Timers] stosb + mov esi, 0x5FF8 ; Address of T0 + mov di, 0x5050 + movsq + mov di, 0x5060 mov rax, [p_LocalAPICAddress] stosq @@ -958,6 +937,12 @@ lfb_wc_end: call read_floppy ; Then load whole floppy at memory %endif + mov edi, 0x5058 + rdtsc ; Gather T1 to EDX:EAX + shl rdx, 32 ; Shift low bits to high + or rax, rdx + stosq + ; Clear all registers (skip the stack pointer) clear_regs: xor eax, eax ; These 32-bit calls also clear the upper bits of the 64-bit registers diff --git a/src/sysvar.asm b/src/sysvar.asm index af8779a..759aba3 100644 --- a/src/sysvar.asm +++ b/src/sysvar.asm @@ -38,9 +38,11 @@ VBEModeInfoBlock: equ 0x0000000000005F00 ; 256 bytes ; DQ - Starting at offset 0, increments by 0x8 p_ACPITableAddress: equ SystemVariables + 0x00 +p_HPET_CyclesPerUs: equ SystemVariables + 0x08 ; Precomputed HPET cycles per microsecond p_LocalAPICAddress: equ SystemVariables + 0x10 p_HPET_Address: equ SystemVariables + 0x28 sys_timer: equ SystemVariables + 0x30 +p_EBDA: equ SystemVariables + 0x38 ; DD - Starting at offset 0x80, increments by 4 p_BSP: equ SystemVariables + 0x80 @@ -48,7 +50,6 @@ p_mem_amount: equ SystemVariables + 0x84 ; in MiB p_HPET_Frequency: equ SystemVariables + 0x88 ; DW - Starting at offset 0x100, increments by 2 -p_cpu_speed: equ SystemVariables + 0x100 p_cpu_activated: equ SystemVariables + 0x102 p_cpu_detected: equ SystemVariables + 0x104 p_PCIECount: equ SystemVariables + 0x106