├── .editorconfig ├── .github └── workflows │ └── main.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.sh ├── clean.sh ├── docs ├── Boot Process.md ├── CREDITS.TXT └── README.md └── src ├── boot ├── bios-floppy.asm ├── bios-pxe.asm ├── bios.asm └── uefi.asm ├── fdc ├── dma.asm └── fdc_64.asm ├── init ├── acpi.asm ├── cpu.asm ├── hpet.asm ├── smp.asm └── smp_ap.asm ├── interrupt.asm ├── pure64.asm └── sysvar.asm /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = false 10 | 11 | # Set tab indentation 12 | [*.asm] 13 | charset = utf-8 14 | indent_style = tab 15 | indent_size = 8 -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ '*' ] 6 | pull_request: 7 | branches: [ '*' ] 8 | jobs: 9 | build-macos: 10 | runs-on: macos-latest 11 | strategy: 12 | fail-fast: false 13 | 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - name: nasm 18 | run: brew install nasm 19 | 20 | - name: Build 21 | run: ./build.sh 22 | 23 | build-ubuntu: 24 | runs-on: ubuntu-latest 25 | strategy: 26 | fail-fast: false 27 | 28 | steps: 29 | - uses: actions/checkout@v4 30 | 31 | - name: install nasm 32 | run: sudo apt install nasm 33 | 34 | - name: Build img 35 | run: ./build.sh 36 | 37 | build-windows: 38 | runs-on: windows-latest 39 | strategy: 40 | fail-fast: false 41 | 42 | steps: 43 | - uses: actions/checkout@v4 44 | 45 | - name: install nasm 46 | run: choco install nasm 47 | 48 | - name: Build img 49 | run: ./build.sh 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *DS_Store 2 | bin/* 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Return Infinity 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pure64 - a software loader for x86-64 systems 2 | 3 | Pure64 is a software loader that was initially created for BareMetal OS. 4 | The loader sets the computer into a full 64-bit state with no legacy compatibility layers and also enables all available CPU Cores in the computer. 5 | Pure64 keeps an information table in memory that stores important details about the computer (Amount of RAM and memory layout, number of CPU cores and their APIC IDs, etc). 6 | The Pure64 loader has been released separately so others can use it in their own software projects. 7 | 8 | 9 | ## Prerequisites 10 | 11 | The scripts in this repo depend on a Debian-based Linux system like [Ubuntu](https://www.ubuntu.com/download/desktop) or [Elementary](https://elementary.io). macOS is also supported if you are using [Homebrew](https://brew.sh). 12 | 13 | - [NASM](https://nasm.us) - Assembly compiler to build the loader and boot sectors. 14 | 15 | In Linux this can be completed with the following command: 16 | 17 | sudo apt install nasm 18 | 19 | 20 | ## Building the source code 21 | 22 | ./build.sh 23 | 24 | 25 | // EOF 26 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mkdir -p bin 4 | 5 | cd src 6 | 7 | nasm -DBIOS=1 pure64.asm -o ../bin/pure64-bios.sys -l ../bin/pure64-bios-debug.txt 8 | nasm -DUEFI=1 pure64.asm -o ../bin/pure64-uefi.sys -l ../bin/pure64-uefi-debug.txt 9 | 10 | cd boot 11 | 12 | nasm bios.asm -o ../../bin/bios.sys -l ../../bin/bios-debug.txt 13 | nasm uefi.asm -o ../../bin/uefi.sys -l ../../bin/uefi-debug.txt 14 | nasm bios-floppy.asm -o ../../bin/bios-floppy.sys -l ../../bin/bios-floppy-debug.txt 15 | nasm bios-pxe.asm -o ../../bin/bios-pxe.sys -l ../../bin/bios-pxe-debug.txt 16 | 17 | cd ../.. 18 | -------------------------------------------------------------------------------- /clean.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | rm -f bin/*.sys 4 | -------------------------------------------------------------------------------- /docs/Boot Process.md: -------------------------------------------------------------------------------- 1 | # Pure64 boot process 2 | Pure64 uses two different first stage loaders depending on the firmware of the system. Older systems will be using BIOS while newer systems will be using UEFI. 3 | 4 | ## BIOS boot (`bios.asm`): 5 | * BIOS loads the first 512-byte sector from the boot disk to `0x7C00` 6 | * The boot code pulls some data about the system (memory map), sets the video mode, and loads the second stage loader to `0x8000`. 7 | * The boot code transitions from 16-bit real mode to 32-bit protected mode and then jumps to the second stage. 8 | * The second stage immediately transitions from 32-bit protected mode to a minimal 64-bit long mode. It then follows unified boot path. 9 | 10 | ## UEFI boot (`uefi.asm`): 11 | * UEFI loads `\EFI\BOOT\BOOTX64.EFI` from the FAT32 partition of the boot disk. 12 | * The boot code sets the video mode, copies the second stage loader to `0x8000`, gets the system memory map, and then jumps to the second stage loader. 13 | * The second stage then follows the unified boot process as the system is already in 64-bit mode. 14 | 15 | ## Unified boot with the second stage (`pure64.asm`): 16 | * Once it 64-bit mode it configures the PIC, PIT, serial port, sets up the proper 64-bit environment. 17 | * Patches the second stage loader so that the AP's can start at `0x8000` as well. 18 | * The software processes the system memory map and creates a list of memory that is usable. 19 | * All available memory above 4MiB is mapped to `0xFFFF800000000000` 20 | * Exception gates are built at `0x0` 21 | * ACPI tables are parsed 22 | * BSP and HPET are configured 23 | * PIC is activated 24 | * SMP is activated 25 | * An information table is built at `0x5000` 26 | * The (up to) 26 KiB of binary data directly after Pure64 is copied to `0x100000` 27 | * Jumps to `0x100000` to start the kernel 28 | -------------------------------------------------------------------------------- /docs/CREDITS.TXT: -------------------------------------------------------------------------------- 1 | =============================================================================== 2 | Pure64 -- a 64-bit loader written in Assembly for x86-64 systems 3 | Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | =============================================================================== 5 | 6 | 7 | PROJECT ADMIN (MAIN CODE AND DOCUMENTATION) 8 | 9 | * Ian Seyler -- ian.seyler@returninfinity.com 10 | 11 | 12 | DEVELOPMENT AND TESTING 13 | 14 | * Members of OSDev.org 15 | 16 | 17 | =============================================================================== 18 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Pure64 Manual 2 | 3 | This manual has the purpose of helping software developers understand how they should be using Pure64 to load their kernel. 4 | 5 | 6 | ## Prerequisites 7 | 8 | This manual assumes you have some understanding of a typical boot process. 9 | 10 | You'll have to have NASM installed to build the software. 11 | 12 | 13 | ## Building 14 | 15 | In the top directory of Pure64, just run the following: 16 | 17 | ``` 18 | ./build.sh 19 | ``` 20 | 21 | 22 | ## System Requirements 23 | 24 | A computer with at least one 64-bit Intel or AMD CPU (or anything else that uses the x86-64 architecture) 25 | 26 | At least 2 MiB of RAM 27 | 28 | The ability to boot via a hard drive, USB stick, or the network 29 | 30 | 31 | ## Writing the Kernel with NASM 32 | 33 | Here's a minimal kernel, written for NASM, that you could use with Pure64. 34 | Once it's loaded, it enters an infinite loop. 35 | The file can be called `kernel.asm`. 36 | 37 | ``` 38 | BITS 64 39 | ORG 0x100000 40 | 41 | start: 42 | jmp start 43 | ``` 44 | 45 | The `ORG` statement tells NASM that the code should be made to run at the address, `0x100000`. 46 | 47 | Assemble it like this: 48 | 49 | ``` 50 | nasm kernel.asm -o kernel.bin 51 | ``` 52 | 53 | ## Writing a Kernel with GCC 54 | 55 | Here's a similar example written in C with GCC. 56 | The file can be called `kernel.c`. 57 | 58 | ``` 59 | void _start(void) { 60 | 61 | for (;;) { 62 | 63 | } 64 | } 65 | ``` 66 | 67 | The kernel needs a linker script to be loaded to 1 MiB, replacing NASMs `ORG` instruction. 68 | The file can be called `kernel.ld`. 69 | 70 | ``` 71 | OUTPUT_FORMAT("binary") 72 | OUTPUT_ARCH("i386:x86-64") 73 | 74 | SECTIONS 75 | { 76 | . = 0x100000; 77 | .text : { 78 | *(.text) 79 | } 80 | .data : { 81 | *(.data) 82 | } 83 | .rodata : { 84 | *(.rodata) 85 | } 86 | .bss : { 87 | *(.bss) 88 | } 89 | } 90 | 91 | ``` 92 | 93 | Compile is like this: 94 | 95 | ``` 96 | gcc -c kernel.c -o kernel.o -mno-red-zone -fno-stack-protector -fomit-frame-pointer 97 | ld -T kernel.ld -o kernel.bin kernel.o 98 | ``` 99 | 100 | The flags added to the first command are there to help GCC produce could that will run in kernel space. 101 | The second command simply takes kernel.o and orders it as the linker script tells it to. 102 | 103 | ## Contributors note 104 | 105 | The `_start` symbol must always appear first within flat binaries as Pure64 will call the start of the file so it must contain executable code. Function definitions (such as inline ones) in header files could interfere with the placement of the `_start` symbol. The best solution is to put the entry point in a separate file that calls the main function. Such a file could be called `start.c`. 106 | ``` 107 | extern int main(void); 108 | 109 | void _start(void) 110 | { 111 | main(); 112 | } 113 | ``` 114 | 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: 115 | ``` 116 | ld -T kernel.ld -o kernel.bin start.o kernel.o 117 | ``` 118 | 119 | ## Creating a bootable image 120 | 121 | After creating a kernel this is a possible routine to create a bootable image. 122 | The commands require Pure64 to be build and `pure64.sys` and `mbr.sys` to be in the same directory 123 | as your kernel with the name `kernel.bin` 124 | 125 | ``` 126 | dd if=/dev/zero of=disk.img count=128 bs=1048576 127 | cat pure64.sys kernel.bin > software.sys 128 | 129 | dd if=mbr.sys of=disk.img conv=notrunc 130 | dd if=software.sys of=disk.img bs=512 seek=16 conv=notrunc 131 | ``` 132 | 133 | After creating a bootable image it can be tested using qemu: 134 | `qemu-system-x86_64 -drive format=raw,file=disk.img` 135 | 136 | ## Memory Map 137 | 138 | This memory map shows how physical memory looks after Pure64 is finished. 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 |
Start AddressEnd AddressSizeDescription
0x00000000000000000x0000000000000FFF4 KiBIDT - 256 descriptors (each descriptor is 16 bytes)
0x00000000000010000x0000000000001FFF4 KiBGDT - 256 descriptors (each descriptor is 16 bytes)
0x00000000000020000x0000000000002FFF4 KiBPML4 - 512 entries, first entry points to PDP at 0x3000
0x00000000000030000x0000000000003FFF4 KiBPDP Low - 512 entries
0x00000000000040000x0000000000004FFF4 KiBPDP High - 512 entries
0x00000000000050000x0000000000007FFF12 KiBPure64 Data
0x00000000000080000x000000000000FFFF32 KiBPure64 - After the OS is loaded and running this memory is free again
0x00000000000100000x000000000001FFFF64 KiBPD Low - Entries are 8 bytes per 2MiB page
0x00000000000200000x000000000005FFFF256 KiBPD High - Entries are 8 bytes per 2MiB page
0x00000000000600000x000000000009FFFF256 KiBFree
0x00000000000A00000x00000000000FFFFF384 KiBBIOS ROM Area
   VGA mem at 0xA0000 (128 KiB) Color text starts at 0xB8000
   Video BIOS at 0xC0000 (64 KiB)
   Motherboard BIOS at F0000 (64 KiB)
0x00000000001000000xFFFFFFFFFFFFFFFF1+ MiBThe software payload is loaded here
158 | 159 | When creating your Operating System or Demo you can use the sections marked free, however it is the safest to use memory above 1 MiB. 160 | 161 | 162 | ## Information Table 163 | 164 | Pure64 stores an information table in memory that contains various pieces of data about the computer before it passes control over to the software you want it to load. 165 | 166 | The Pure64 information table is located at `0x0000000000005000` and ends at `0x00000000000057FF` (2048 bytes). 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 |
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)
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
0x50178-bitCPU_MEM_VIRTUALThe number of bits that are valid for a virtual address
0x501832-bitCPU_ID_MAX_STANDARDThe maximum CPUID standard leaf
0x501C32-bitCPU_ID_MAX_EXTENDEDThe maximum CPUID extended leaf
0x502032-bitRAMAMOUNTAmount of system RAM in Mebibytes (MiB)
0x5022 - 0x502F  For future use
0x50308-bitIOAPIC_COUNTNumber of I/O APICs in the system
0x50318-bitIOAPIC_INTSOURCE_COUNTNumber of I/O APIC Interrupt Source Override
0x5032 - 0x503F  For future use
0x504064-bitHPET AddressBase memory address for the High Precision Event Timer
0x504832-bitHPET FrequencyFrequency for the High Precision Event Timer
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
0x506064-bitLAPICLocal APIC address
0x5068 - 0x507F  For future use
0x508064-bitVIDEO_BASEBase memory for video (if graphics mode set)
0x508816-bitVIDEO_XX resolution
0x508A16-bitVIDEO_YY resolution
0x508C16-bitVIDEO_PPSL# of pixels per scan line
0x508E16-bitVIDEO_BPP# of bits per pixel
0x509016-bitPCIE_COUNTNumber of PCIe buses
0x509216-bitIAPC_BOOT_ARCHIA-PC Boot Architecture Flags
0x5094 - 0x50DF  For future use
0x50E08-bitFLAG_1GBPAGE1 if 1GB Pages are supported
0x50E18-bitFLAG_X2APIC1 if X2APIC is supported
0x50E2 - 0x50FF  For future use
0x5100 - 0x51FF8-bitAPIC_IDAPIC ID's for valid CPU cores (based on CORES_ACTIVE)
0x5200 - 0x53FF  For future use
0x5400 - 0x55FF16 byte entriesPCIEPCIe bus data
0x5600 - 0x56FF16 byte entriesIOAPICI/O APIC addresses (based on IOAPIC_COUNT)
0x5700 - 0x57FF8 byte entriesIOAPIC_INTSOURCEI/O APIC Interrupt Source Override Entries (based on IOAPIC_INTSOURCE_COUNT)
208 | 209 | PCIE list format: 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 |
OffsetVariable SizeNameDescription
0x0064-bitBaseThe base address of enhanced configuration mechanism
0x0816-bitGroupThe PCI segment group number
0x0A8-bitStart BusStart PCI bus number decoded by this host bridge
0x0B8-bitEnd BusEnd PCI bus number decoded by this host bridge
0x0C32-bitReservedThis value should be 0
218 | 219 | IOAPIC list format: 220 | 221 | 222 | 223 | 224 | 225 | 226 |
OffsetVariable SizeNameDescription
0x0032-bitI/O APIC IDThe ID of an I/O APIC
0x0032-bitI/O APIC AddressThe 32-bit physical address to access this I/O APIC
0x0032-bitGlobal System Interrupt BaseThe global system interrupt number where this I/O APIC’s interrupt inputs start
0x0032-bitReservedThis value should be 0
227 | 228 | IOAPIC_INTSOURCE list format: 229 | 230 | 231 | 232 | 233 | 234 | 235 |
OffsetVariable SizeNameDescription
0x008-bitBus0
0x008-bitSourceBus-relative interrupt source
0x0032-bitGlobal System InterruptThe Global System Interrupt that this bus-relative interrupt source will signal
0x0016-bitFlagsMPS INTI flags
236 | 237 | MPS INTI flags: 238 | 239 | 240 | 241 | 242 |
FlagsBit LengthBit OffsetDescription
Polarity2001 Active high, 11 Active low
Trigger Mode2201 Edge-triggered, 11 Level-triggered
243 | 244 | ## System Memory Map 245 | 246 | ### BIOS 247 | 248 | A copy of the BIOS System Memory Map is stored at memory address `0x0000000000006000`. Each BIOS record is 32 bytes in length and the memory map is terminated by a blank record. 249 | 250 | 251 | 252 | 253 | 254 | 255 |
VariableVariable SizeDescription
Base64-bitBase Address
Length64-bitLength in bytes
Type32-bitType of memory (1 is usable)
ACPI32-bitSee document linked below
256 | For more information on the BIOS Memory Map: BIOS Specs 257 | 258 | ### UEFI 259 | 260 | A copy of the UEFI System Memory Map is stored at memory address `0x0000000000220000`. Each UEFI record is 48 bytes in length and the memory map is terminated by a blank record. 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 |
VariableVariable SizeDescription
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
270 | For more information on the UEFI Memory Map: UEFI Specs 271 | 272 | 273 | ## Debugging 274 | 275 | Pure64 initializes the serial port `COM1` at 115200bps, 8 data bytes, 1 stop bit, no parity, and no flow control. It will display messages on boot-up. 276 | 277 | 278 | // EOF 279 | -------------------------------------------------------------------------------- /src/boot/bios-floppy.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; Pure64 MBR -- a 64-bit OS/software loader written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; This Master Boot Record will load Pure64 from a pre-defined location on the 6 | ; floppy drive without making use of the file system. 7 | ; 8 | ; In this code we are not expecting any formatted drive. A small 9 | ; check is made to make sure Pure64 was loaded by comparing a signature. 10 | ; ============================================================================= 11 | 12 | ; Default location of the second stage boot loader. This loads 13 | ; 32 KiB from sector 2 into memory at 0x8000 14 | ; Extended read int 0x13 AH=0x42 not possible for floppy devices. 15 | ; http://www.ctyme.com/intr/rb-0607.htm 16 | 17 | ; Set the desired screen resolution values below 18 | Horizontal_Resolution equ 1024 19 | Vertical_Resolution equ 768 20 | 21 | BITS 16 22 | org 0x7C00 23 | 24 | entry: 25 | jmp bootcode ; Jump past the BPB data 26 | nop 27 | 28 | ; BPB (BIOS Parameter Block) 29 | dq 0 ; OEM identifier 30 | dw 0 ; Bytes per sector 31 | db 0 ; Sectors per cluster 32 | dw 0 ; Reserved sectors 33 | db 0 ; Number of FATs 34 | dw 0 ; Number of root directory entries 35 | dw 0 ; The total sectors in the logical volume 36 | db 0 ; Media descriptor type 37 | dw 0 ; Number of sectors per FAT 38 | dw 0 ; Number of sectors per track 39 | dw 0 ; Number of heads or sides on the storage media 40 | dd 0 ; Number of hidden sectors 41 | dd 0 ; Large sector count 42 | 43 | bootcode: 44 | cli ; Disable interrupts 45 | cld ; Clear direction flag 46 | xor eax, eax 47 | mov ss, ax 48 | mov es, ax 49 | mov ds, ax 50 | mov sp, 0x7C00 51 | sti ; Enable interrupts 52 | 53 | mov [DriveNumber], dl ; BIOS passes drive number in DL 54 | 55 | ; Get the BIOS E820 Memory Map 56 | ; https://wiki.osdev.org/Detecting_Memory_(x86)#BIOS_Function:_INT_0x15,_EAX_=_0xE820 57 | ; The code below is from https://wiki.osdev.org/Detecting_Memory_(x86)#Getting_an_E820_Memory_Map 58 | ; inputs: es:di -> destination buffer for 24 byte entries 59 | ; outputs: bp = entry count, trashes all registers except esi 60 | ; The function below creates a memory map at address 0x6000 and the records are: 61 | ; 64-bit Base 62 | ; 64-bit Length 63 | ; 32-bit Type (1 = normal, 2 reserved, ACPI reclaimable) 64 | ; 32-bit ACPI 65 | ; 64-bit Padding 66 | do_e820: 67 | mov edi, 0x00006000 ; location that memory map will be stored to 68 | xor ebx, ebx ; ebx must be 0 to start 69 | xor bp, bp ; keep an entry count in bp 70 | mov edx, 0x0534D4150 ; Place "SMAP" into edx 71 | mov eax, 0xe820 72 | mov [es:di + 20], dword 1 ; force a valid ACPI 3.X entry 73 | mov ecx, 24 ; ask for 24 bytes 74 | int 0x15 75 | jc nomemmap ; carry set on first call means "unsupported function" 76 | mov edx, 0x0534D4150 ; Some BIOSes apparently trash this register? 77 | cmp eax, edx ; on success, eax must have been reset to "SMAP" 78 | jne nomemmap 79 | test ebx, ebx ; ebx = 0 implies list is only 1 entry long (worthless) 80 | je nomemmap 81 | jmp jmpin 82 | e820lp: 83 | mov eax, 0xe820 ; eax, ecx get trashed on every int 0x15 call 84 | mov [es:di + 20], dword 1 ; force a valid ACPI 3.X entry 85 | mov ecx, 24 ; ask for 24 bytes again 86 | int 0x15 87 | jc memmapend ; carry set means "end of list already reached" 88 | mov edx, 0x0534D4150 ; repair potentially trashed register 89 | jmpin: 90 | jcxz skipent ; skip any 0 length entries 91 | cmp cl, 20 ; got a 24 byte ACPI 3.X response? 92 | jbe notext 93 | test byte [es:di + 20], 1 ; if so: is the "ignore this data" bit clear? 94 | je skipent 95 | notext: 96 | mov ecx, [es:di + 8] ; get lower dword of memory region length 97 | test ecx, ecx ; is the qword == 0? 98 | jne goodent 99 | mov ecx, [es:di + 12] ; get upper dword of memory region length 100 | jecxz skipent ; if length qword is 0, skip entry 101 | goodent: 102 | inc bp ; got a good entry: ++count, move to next storage spot 103 | add di, 32 ; Pad to 32 bytes for each record 104 | skipent: 105 | test ebx, ebx ; if ebx resets to 0, list is complete 106 | jne e820lp 107 | nomemmap: 108 | memmapend: 109 | xor eax, eax ; Create a blank record for termination (32 bytes) 110 | mov ecx, 8 111 | rep stosd 112 | 113 | ; Enable the A20 gate 114 | set_A20: 115 | in al, 0x64 116 | test al, 0x02 117 | jnz set_A20 118 | mov al, 0xD1 119 | out 0x64, al 120 | check_A20: 121 | in al, 0x64 122 | test al, 0x02 123 | jnz check_A20 124 | mov al, 0xDF 125 | out 0x60, al 126 | 127 | mov cx, 0x4000 - 1 ; Start looking from here 128 | VBESearch: 129 | inc cx 130 | mov bx, cx ; Mode is saved to BX for the set command later 131 | cmp cx, 0x5000 132 | je halt 133 | mov edi, VBEModeInfoBlock ; VBE data will be stored at this address 134 | mov ax, 0x4F01 ; VESA SuperVGA BIOS - GET SuperVGA MODE INFORMATION - http://www.ctyme.com/intr/rb-0274.htm 135 | int 0x10 136 | cmp ax, 0x004F ; Return value in AX should equal 0x004F if command supported and successful 137 | jne VBESearch ; Try next mode 138 | cmp byte [VBEModeInfoBlock.BitsPerPixel], 32 ; Desired bit depth 139 | jne VBESearch ; If not equal, try next mode 140 | cmp word [VBEModeInfoBlock.XResolution], Horizontal_Resolution ; Desired XRes here 141 | jne VBESearch 142 | cmp word [VBEModeInfoBlock.YResolution], Vertical_Resolution ; Desired YRes here 143 | jne VBESearch 144 | or bx, 0x4000 ; Use linear/flat frame buffer model (set bit 14) 145 | mov ax, 0x4F02 ; VESA SuperVGA BIOS - SET SuperVGA VIDEO MODE - http://www.ctyme.com/intr/rb-0275.htm 146 | int 0x10 147 | cmp ax, 0x004F ; Return value in AX should equal 0x004F if supported and successful 148 | jne halt 149 | 150 | ; Read the 2nd stage boot loader into memory. 151 | ; Load 4 cylinders - 7 sectors 152 | mov byte [cylinder], 0 153 | mov word [sec_buff], 0x7E00 ; For simplicity load whole cylinder but jump to 0x8000 154 | call load_sector 155 | mov byte [head], 1 156 | mov word [sec_buff], 0xA200 157 | call load_sector 158 | mov byte [cylinder], 1 159 | mov byte [head], 0 160 | mov word [sec_buff], 0xC600 161 | call load_sector 162 | mov byte [head], 1 163 | mov word [sec_buff], 0xEA00 164 | mov byte [nr_sect], 11 ; Remains just 11 for 16-bit memory limit 165 | call load_sector 166 | 167 | ; Stop floppy drive motor 168 | push dx 169 | mov dx, 0x3F2 ; Floppy DIGITAL_OUTPUT_REGISTER 170 | mov al, 0 171 | out dx, al 172 | pop dx 173 | 174 | mov bl, 'B' ; 'B' as we booted via BIOS 175 | mov bh, 'F' ; 'F' as we booted via floppy drive 176 | 177 | ; At this point we are done with real mode and BIOS interrupts. Jump to 32-bit mode. 178 | cli ; No more interrupts 179 | lgdt [cs:GDTR32] ; Load GDT register 180 | mov eax, cr0 181 | or al, 0x01 ; Set protected mode bit 182 | mov cr0, eax 183 | jmp 8:0x8000 ; Jump to 32-bit protected mode 184 | 185 | halt: 186 | hlt 187 | jmp halt 188 | ;------------------------------------------------------------------------------ 189 | 190 | 191 | ;------------------------------------------------------------------------------ 192 | load_sector: 193 | xor ah, ah ; Reset drive function 194 | int 0x13 195 | 196 | mov bx, [sec_buff] 197 | mov dl, [DriveNumber] 198 | mov dh, [head] 199 | mov al, [nr_sect] 200 | mov ch, [cylinder] 201 | mov cl, [sector] 202 | mov ah, 0x2 203 | int 0x13 204 | jnc exit ; If the carry flag is clear, it worked 205 | loop load_sector ; Read attempts 206 | exit: 207 | ret 208 | ;------------------------------------------------------------------------------ 209 | 210 | 211 | align 16 212 | 213 | GDTR32: ; Global Descriptors Table Register 214 | dw gdt32_end - gdt32 - 1 ; limit of GDT (size minus one) 215 | dq gdt32 ; linear address of GDT 216 | 217 | align 16 218 | gdt32: 219 | dw 0x0000, 0x0000, 0x0000, 0x0000 ; Null descriptor 220 | dw 0xFFFF, 0x0000, 0x9A00, 0x00CF ; 32-bit code descriptor 221 | dw 0xFFFF, 0x0000, 0x9200, 0x00CF ; 32-bit data descriptor 222 | gdt32_end: 223 | 224 | align 4 225 | 226 | ; CHS 227 | sector db 1 228 | cylinder db 0 229 | head db 0 230 | nr_sect db 18 231 | sec_buff dw 0 232 | 233 | DriveNumber db 0x00 234 | 235 | times 510-$+$$ db 0 236 | 237 | dw 0xAA55 ; Boot signature 238 | 239 | VBEModeInfoBlock: equ 0x5F00 240 | ; VESA 241 | ; Mandatory information for all VBE revisions 242 | VBEModeInfoBlock.ModeAttributes equ VBEModeInfoBlock + 0 ; DW - mode attributes 243 | VBEModeInfoBlock.WinAAttributes equ VBEModeInfoBlock + 2 ; DB - window A attributes 244 | VBEModeInfoBlock.WinBAttributes equ VBEModeInfoBlock + 3 ; DB - window B attributes 245 | VBEModeInfoBlock.WinGranularity equ VBEModeInfoBlock + 4 ; DW - window granularity in KB 246 | VBEModeInfoBlock.WinSize equ VBEModeInfoBlock + 6 ; DW - window size in KB 247 | VBEModeInfoBlock.WinASegment equ VBEModeInfoBlock + 8 ; DW - window A start segment 248 | VBEModeInfoBlock.WinBSegment equ VBEModeInfoBlock + 10 ; DW - window B start segment 249 | VBEModeInfoBlock.WinFuncPtr equ VBEModeInfoBlock + 12 ; DD - real mode pointer to window function 250 | VBEModeInfoBlock.BytesPerScanLine equ VBEModeInfoBlock + 16 ; DW - bytes per scan line 251 | ; Mandatory information for VBE 1.2 and above 252 | VBEModeInfoBlock.XResolution equ VBEModeInfoBlock + 18 ; DW - horizontal resolution in pixels or characters 253 | VBEModeInfoBlock.YResolution equ VBEModeInfoBlock + 20 ; DW - vertical resolution in pixels or characters 254 | VBEModeInfoBlock.XCharSize equ VBEModeInfoBlock + 22 ; DB - character cell width in pixels 255 | VBEModeInfoBlock.YCharSize equ VBEModeInfoBlock + 23 ; DB - character cell height in pixels 256 | VBEModeInfoBlock.NumberOfPlanes equ VBEModeInfoBlock + 24 ; DB - number of memory planes 257 | VBEModeInfoBlock.BitsPerPixel equ VBEModeInfoBlock + 25 ; DB - bits per pixel 258 | VBEModeInfoBlock.NumberOfBanks equ VBEModeInfoBlock + 26 ; DB - number of banks 259 | VBEModeInfoBlock.MemoryModel equ VBEModeInfoBlock + 27 ; DB - memory model type 260 | VBEModeInfoBlock.BankSize equ VBEModeInfoBlock + 28 ; DB - bank size in KB 261 | VBEModeInfoBlock.NumberOfImagePages equ VBEModeInfoBlock + 29 ; DB - number of image pages 262 | VBEModeInfoBlock.Reserved equ VBEModeInfoBlock + 30 ; DB - reserved (0x00 for VBE 1.0-2.0, 0x01 for VBE 3.0) 263 | ; Direct Color fields (required for direct/6 and YUV/7 memory models) 264 | VBEModeInfoBlock.RedMaskSize equ VBEModeInfoBlock + 31 ; DB - size of direct color red mask in bits 265 | VBEModeInfoBlock.RedFieldPosition equ VBEModeInfoBlock + 32 ; DB - bit position of lsb of red mask 266 | VBEModeInfoBlock.GreenMaskSize equ VBEModeInfoBlock + 33 ; DB - size of direct color green mask in bits 267 | VBEModeInfoBlock.GreenFieldPosition equ VBEModeInfoBlock + 34 ; DB - bit position of lsb of green mask 268 | VBEModeInfoBlock.BlueMaskSize equ VBEModeInfoBlock + 35 ; DB - size of direct color blue mask in bits 269 | VBEModeInfoBlock.BlueFieldPosition equ VBEModeInfoBlock + 36 ; DB - bit position of lsb of blue mask 270 | VBEModeInfoBlock.RsvdMaskSize equ VBEModeInfoBlock + 37 ; DB - size of direct color reserved mask in bits 271 | VBEModeInfoBlock.RsvdFieldPosition equ VBEModeInfoBlock + 38 ; DB - bit position of lsb of reserved mask 272 | VBEModeInfoBlock.DirectColorModeInfo equ VBEModeInfoBlock + 39 ; DB - direct color mode attributes 273 | ; Mandatory information for VBE 2.0 and above 274 | VBEModeInfoBlock.PhysBasePtr equ VBEModeInfoBlock + 40 ; DD - physical address for flat memory frame buffer 275 | VBEModeInfoBlock.Reserved1 equ VBEModeInfoBlock + 44 ; DD - Reserved - always set to 0 276 | VBEModeInfoBlock.Reserved2 equ VBEModeInfoBlock + 48 ; DD - Reserved - always set to 0 277 | 278 | ; EOF 279 | -------------------------------------------------------------------------------- /src/boot/bios-pxe.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; Pure64 PXE Start -- a 64-bit OS/software loader written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; This is a stub file for loading Pure64 and a kernel/software package via PXE. 6 | ; 7 | ; Windows - copy /b pxestart.bin + pure64.sys + kernel64.sys pxeboot.bin 8 | ; Unix - cat pxestart.bin pure64.sys kernel64.sys > pxeboot.bin 9 | ; 10 | ; Max size of the resulting pxeboot.bin is 33792 bytes. 1K for the PXE loader 11 | ; stub and up to 32KiB for the code/data. PXE loads the file to address 12 | ; 0x00007C00 (Just like a boot sector). 13 | ; 14 | ; File Sizes 15 | ; pxestart.bin 1024 bytes 16 | ; pure64.sys 4096 bytes 17 | ; kernel64.sys 16384 bytes (or so) 18 | ; ============================================================================= 19 | 20 | ; Set the desired screen resolution values below 21 | Horizontal_Resolution equ 1024 22 | Vertical_Resolution equ 768 23 | 24 | BITS 16 25 | org 0x7C00 26 | 27 | start: 28 | cli ; Disable interrupts 29 | cld ; Clear direction flag 30 | xor eax, eax 31 | mov ss, ax 32 | mov es, ax 33 | mov ds, ax 34 | mov sp, 0x7C00 35 | sti ; Enable interrupts 36 | 37 | ; mov ah, 0 38 | ; mov al, 11100011b ; 9600bps, no parity, 1 stop bit, 8 data bits 39 | ; mov dx, 0 ; Serial port 0 40 | ; int 0x14 ; Configure serial port 41 | 42 | ; Get the BIOS E820 Memory Map 43 | ; use the INT 0x15, eax= 0xE820 BIOS function to get a memory map 44 | ; inputs: es:di -> destination buffer for 24 byte entries 45 | ; outputs: bp = entry count, trashes all registers except esi 46 | do_e820: 47 | mov edi, 0x00006000 ; location that memory map will be stored to 48 | xor ebx, ebx ; ebx must be 0 to start 49 | xor bp, bp ; keep an entry count in bp 50 | mov edx, 0x0534D4150 ; Place "SMAP" into edx 51 | mov eax, 0xe820 52 | mov [es:di + 20], dword 1 ; force a valid ACPI 3.X entry 53 | mov ecx, 24 ; ask for 24 bytes 54 | int 0x15 55 | jc nomemmap ; carry set on first call means "unsupported function" 56 | mov edx, 0x0534D4150 ; Some BIOSes apparently trash this register? 57 | cmp eax, edx ; on success, eax must have been reset to "SMAP" 58 | jne nomemmap 59 | test ebx, ebx ; ebx = 0 implies list is only 1 entry long (worthless) 60 | je nomemmap 61 | jmp jmpin 62 | e820lp: 63 | mov eax, 0xe820 ; eax, ecx get trashed on every int 0x15 call 64 | mov [es:di + 20], dword 1 ; force a valid ACPI 3.X entry 65 | mov ecx, 24 ; ask for 24 bytes again 66 | int 0x15 67 | jc memmapend ; carry set means "end of list already reached" 68 | mov edx, 0x0534D4150 ; repair potentially trashed register 69 | jmpin: 70 | jcxz skipent ; skip any 0 length entries 71 | cmp cl, 20 ; got a 24 byte ACPI 3.X response? 72 | jbe notext 73 | test byte [es:di + 20], 1 ; if so: is the "ignore this data" bit clear? 74 | je skipent 75 | notext: 76 | mov ecx, [es:di + 8] ; get lower dword of memory region length 77 | test ecx, ecx ; is the qword == 0? 78 | jne goodent 79 | mov ecx, [es:di + 12] ; get upper dword of memory region length 80 | jecxz skipent ; if length qword is 0, skip entry 81 | goodent: 82 | inc bp ; got a good entry: ++count, move to next storage spot 83 | add di, 32 84 | skipent: 85 | test ebx, ebx ; if ebx resets to 0, list is complete 86 | jne e820lp 87 | nomemmap: 88 | ; mov byte [cfg_e820], 0 ; No memory map function 89 | memmapend: 90 | xor eax, eax ; Create a blank record for termination (32 bytes) 91 | mov ecx, 8 92 | rep stosd 93 | 94 | ; Enable the A20 gate 95 | set_A20: 96 | in al, 0x64 97 | test al, 0x02 98 | jnz set_A20 99 | mov al, 0xD1 100 | out 0x64, al 101 | check_A20: 102 | in al, 0x64 103 | test al, 0x02 104 | jnz check_A20 105 | mov al, 0xDF 106 | out 0x60, al 107 | 108 | mov si, msg_Load ; Print message 109 | call print_string_16 110 | 111 | mov edi, VBEModeInfoBlock ; VBE data will be stored at this address 112 | mov ax, 0x4F01 ; GET SuperVGA MODE INFORMATION - http://www.ctyme.com/intr/rb-0274.htm 113 | ; CX queries the mode, it should be in the form 0x41XX as bit 14 is set for LFB and bit 8 is set for VESA mode 114 | ; 0x4112 is 640x480x24bit, 0x4129 should be 32bit 115 | ; 0x4115 is 800x600x24bit, 0x412E should be 32bit 116 | ; 0x4118 is 1024x768x24bit, 0x4138 should be 32bit 117 | ; 0x411B is 1280x1024x24bit, 0x413D should be 32bit 118 | mov cx, 0x4118 ; Put your desired mode here 119 | mov bx, cx ; Mode is saved to BX for the set command later 120 | int 0x10 121 | 122 | cmp ax, 0x004F ; Return value in AX should equal 0x004F if command supported and successful 123 | jne halt 124 | cmp byte [VBEModeInfoBlock.BitsPerPixel], 24 ; Make sure this matches the number of bits for the mode! 125 | jne halt ; If set bit mode was unsuccessful then bail out 126 | or bx, 0x4000 ; Use linear/flat frame buffer model (set bit 14) 127 | mov ax, 0x4F02 ; SET SuperVGA VIDEO MODE - http://www.ctyme.com/intr/rb-0275.htm 128 | int 0x10 129 | cmp ax, 0x004F ; Return value in AX should equal 0x004F if supported and successful 130 | jne halt 131 | 132 | mov ax, [0x8006] 133 | cmp ax, 0x3436 ; Match against the Pure64 binary 134 | jne sig_fail 135 | 136 | mov si, msg_OK 137 | call print_string_16 138 | 139 | mov bl, 'B' ; 'B' as we booted via BIOS 140 | mov bh, 'P' ; 'P' as we booted via PXE 141 | 142 | ; At this point we are done with real mode and BIOS interrupts. Jump to 32-bit mode. 143 | cli ; No more interrupts 144 | lgdt [cs:GDTR32] ; Load GDT register 145 | mov eax, cr0 146 | or al, 0x01 ; Set protected mode bit 147 | mov cr0, eax 148 | jmp 8:0x8000 ; Jump to 32-bit protected mode 149 | 150 | sig_fail: 151 | mov si, msg_SigFail 152 | call print_string_16 153 | halt: 154 | hlt 155 | jmp halt 156 | ;------------------------------------------------------------------------------ 157 | 158 | 159 | ;------------------------------------------------------------------------------ 160 | ; 16-bit function to output a string to the serial port 161 | ; IN: SI - Address of start of string 162 | print_string_16: ; Output string in SI to screen 163 | pusha 164 | mov dx, 0 ; Port 0 165 | .repeat: 166 | mov ah, 0x01 ; Serial - Write character to port 167 | lodsb ; Get char from string 168 | cmp al, 0 169 | je .done ; If char is zero, end of string 170 | int 0x14 ; Output the character 171 | jmp short .repeat 172 | .done: 173 | popa 174 | ret 175 | ;------------------------------------------------------------------------------ 176 | 177 | 178 | align 16 179 | GDTR32: ; Global Descriptors Table Register 180 | dw gdt32_end - gdt32 - 1 ; limit of GDT (size minus one) 181 | dq gdt32 ; linear address of GDT 182 | 183 | align 16 184 | gdt32: 185 | dw 0x0000, 0x0000, 0x0000, 0x0000 ; Null desciptor 186 | dw 0xFFFF, 0x0000, 0x9A00, 0x00CF ; 32-bit code descriptor 187 | dw 0xFFFF, 0x0000, 0x9200, 0x00CF ; 32-bit data descriptor 188 | gdt32_end: 189 | 190 | msg_Load db "PXE ", 0 191 | msg_OK db "OK", 0 192 | msg_SigFail db "- Bad Sig!", 0 193 | 194 | times 510-$+$$ db 0 ; Pad out for a normal boot sector 195 | 196 | sign dw 0xAA55 ; BIOS boot sector signature 197 | 198 | times 1024-$+$$ db 0 ; Padding so that Pure64 will be aligned at 0x8000 199 | 200 | VBEModeInfoBlock: equ 0x5F00 201 | ; VESA 202 | ; Mandatory information for all VBE revisions 203 | VBEModeInfoBlock.ModeAttributes equ VBEModeInfoBlock + 0 ; DW - mode attributes 204 | VBEModeInfoBlock.WinAAttributes equ VBEModeInfoBlock + 2 ; DB - window A attributes 205 | VBEModeInfoBlock.WinBAttributes equ VBEModeInfoBlock + 3 ; DB - window B attributes 206 | VBEModeInfoBlock.WinGranularity equ VBEModeInfoBlock + 4 ; DW - window granularity in KB 207 | VBEModeInfoBlock.WinSize equ VBEModeInfoBlock + 6 ; DW - window size in KB 208 | VBEModeInfoBlock.WinASegment equ VBEModeInfoBlock + 8 ; DW - window A start segment 209 | VBEModeInfoBlock.WinBSegment equ VBEModeInfoBlock + 10 ; DW - window B start segment 210 | VBEModeInfoBlock.WinFuncPtr equ VBEModeInfoBlock + 12 ; DD - real mode pointer to window function 211 | VBEModeInfoBlock.BytesPerScanLine equ VBEModeInfoBlock + 16 ; DW - bytes per scan line 212 | ; Mandatory information for VBE 1.2 and above 213 | VBEModeInfoBlock.XResolution equ VBEModeInfoBlock + 18 ; DW - horizontal resolution in pixels or characters 214 | VBEModeInfoBlock.YResolution equ VBEModeInfoBlock + 20 ; DW - vertical resolution in pixels or characters 215 | VBEModeInfoBlock.XCharSize equ VBEModeInfoBlock + 22 ; DB - character cell width in pixels 216 | VBEModeInfoBlock.YCharSize equ VBEModeInfoBlock + 23 ; DB - character cell height in pixels 217 | VBEModeInfoBlock.NumberOfPlanes equ VBEModeInfoBlock + 24 ; DB - number of memory planes 218 | VBEModeInfoBlock.BitsPerPixel equ VBEModeInfoBlock + 25 ; DB - bits per pixel 219 | VBEModeInfoBlock.NumberOfBanks equ VBEModeInfoBlock + 26 ; DB - number of banks 220 | VBEModeInfoBlock.MemoryModel equ VBEModeInfoBlock + 27 ; DB - memory model type 221 | VBEModeInfoBlock.BankSize equ VBEModeInfoBlock + 28 ; DB - bank size in KB 222 | VBEModeInfoBlock.NumberOfImagePages equ VBEModeInfoBlock + 29 ; DB - number of image pages 223 | VBEModeInfoBlock.Reserved equ VBEModeInfoBlock + 30 ; DB - reserved (0x00 for VBE 1.0-2.0, 0x01 for VBE 3.0) 224 | ; Direct Color fields (required for direct/6 and YUV/7 memory models) 225 | VBEModeInfoBlock.RedMaskSize equ VBEModeInfoBlock + 31 ; DB - size of direct color red mask in bits 226 | VBEModeInfoBlock.RedFieldPosition equ VBEModeInfoBlock + 32 ; DB - bit position of lsb of red mask 227 | VBEModeInfoBlock.GreenMaskSize equ VBEModeInfoBlock + 33 ; DB - size of direct color green mask in bits 228 | VBEModeInfoBlock.GreenFieldPosition equ VBEModeInfoBlock + 34 ; DB - bit position of lsb of green mask 229 | VBEModeInfoBlock.BlueMaskSize equ VBEModeInfoBlock + 35 ; DB - size of direct color blue mask in bits 230 | VBEModeInfoBlock.BlueFieldPosition equ VBEModeInfoBlock + 36 ; DB - bit position of lsb of blue mask 231 | VBEModeInfoBlock.RsvdMaskSize equ VBEModeInfoBlock + 37 ; DB - size of direct color reserved mask in bits 232 | VBEModeInfoBlock.RsvdFieldPosition equ VBEModeInfoBlock + 38 ; DB - bit position of lsb of reserved mask 233 | VBEModeInfoBlock.DirectColorModeInfo equ VBEModeInfoBlock + 39 ; DB - direct color mode attributes 234 | ; Mandatory information for VBE 2.0 and above 235 | VBEModeInfoBlock.PhysBasePtr equ VBEModeInfoBlock + 40 ; DD - physical address for flat memory frame buffer 236 | VBEModeInfoBlock.Reserved1 equ VBEModeInfoBlock + 44 ; DD - Reserved - always set to 0 237 | VBEModeInfoBlock.Reserved2 equ VBEModeInfoBlock + 48 ; DD - Reserved - always set to 0 238 | 239 | ; EOF 240 | -------------------------------------------------------------------------------- /src/boot/bios.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; Pure64 MBR -- a 64-bit OS/software loader written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; This Master Boot Record will load Pure64 from a pre-defined location on the 6 | ; hard drive without making use of the file system. 7 | ; 8 | ; In this code we are expecting a BMFS-formatted drive. With BMFS the Pure64 9 | ; binary is required to start at sector 16 (8192 bytes from the start). A small 10 | ; check is made to make sure Pure64 was loaded by comparing a signature. 11 | ; ============================================================================= 12 | 13 | ; Default location of the second stage boot loader. This loads 14 | ; 32 KiB from sector 16 into memory at 0x8000 15 | %define DAP_SECTORS 64 16 | %define DAP_STARTSECTOR 262160 17 | %define DAP_ADDRESS 0x8000 18 | %define DAP_SEGMENT 0x0000 19 | 20 | ; Set the desired screen resolution values below 21 | Horizontal_Resolution equ 1024 22 | Vertical_Resolution equ 768 23 | 24 | BITS 16 25 | org 0x7C00 26 | 27 | entry: 28 | jmp bootcode ; Jump past the BPB data 29 | nop 30 | 31 | ; BPB (BIOS Parameter Block) 32 | dq 0 ; OEM identifier 33 | dw 0 ; Bytes per sector 34 | db 0 ; Sectors per cluster 35 | dw 0 ; Reserved sectors 36 | db 0 ; Number of FATs 37 | dw 0 ; Number of root directory entries 38 | dw 0 ; The total sectors in the logical volume 39 | db 0 ; Media descriptor type 40 | dw 0 ; Number of sectors per FAT 41 | dw 0 ; Number of sectors per track 42 | dw 0 ; Number of heads or sides on the storage media 43 | dd 0 ; Number of hidden sectors 44 | dd 0 ; Large sector count 45 | 46 | ; EBPB (Extended Boot Record) 47 | dd 0 ; Sectors per FAT 48 | dw 0 ; Flags 49 | dw 0 ; FAT version number 50 | dd 0 ; The cluster number of the root directory 51 | dw 0 ; The sector number of the FSInfo structure 52 | dw 0 ; The sector number of the backup boot sector 53 | dq 0 ; Reserved 54 | dd 0 ; Reserved 55 | db 0 ; Drive number 56 | db 0 ; Flags in Windows NT 57 | db 0 ; Signature 58 | dd 0 ; Volume ID 'Serial' number 59 | times 11 db 0 ; Volume label string 60 | dq 0 ; System identifier string. Always "FAT32 " 61 | 62 | bootcode: 63 | cli ; Disable interrupts 64 | cld ; Clear direction flag 65 | xor eax, eax 66 | mov ss, ax 67 | mov es, ax 68 | mov ds, ax 69 | mov sp, 0x7C00 70 | sti ; Enable interrupts 71 | 72 | mov [DriveNumber], dl ; BIOS passes drive number in DL 73 | 74 | ; Get the BIOS E820 Memory Map 75 | ; https://wiki.osdev.org/Detecting_Memory_(x86)#BIOS_Function:_INT_0x15,_EAX_=_0xE820 76 | ; The code below is from https://wiki.osdev.org/Detecting_Memory_(x86)#Getting_an_E820_Memory_Map 77 | ; inputs: es:di -> destination buffer for 24 byte entries 78 | ; outputs: bp = entry count, trashes all registers except esi 79 | ; The function below creates a memory map at address 0x6000 and the records are: 80 | ; 64-bit Base 81 | ; 64-bit Length 82 | ; 32-bit Type (1 = normal, 2 reserved, ACPI reclaimable) 83 | ; 32-bit ACPI 84 | ; 64-bit Padding 85 | do_e820: 86 | mov edi, 0x00006000 ; location that memory map will be stored to 87 | xor ebx, ebx ; ebx must be 0 to start 88 | xor bp, bp ; keep an entry count in bp 89 | mov edx, 0x0534D4150 ; Place "SMAP" into edx 90 | mov eax, 0xe820 91 | mov [es:di + 20], dword 1 ; force a valid ACPI 3.X entry 92 | mov ecx, 24 ; ask for 24 bytes 93 | int 0x15 94 | jc nomemmap ; carry set on first call means "unsupported function" 95 | mov edx, 0x0534D4150 ; Some BIOSes apparently trash this register? 96 | cmp eax, edx ; on success, eax must have been reset to "SMAP" 97 | jne nomemmap 98 | test ebx, ebx ; ebx = 0 implies list is only 1 entry long (worthless) 99 | je nomemmap 100 | jmp jmpin 101 | e820lp: 102 | mov eax, 0xe820 ; eax, ecx get trashed on every int 0x15 call 103 | mov [es:di + 20], dword 1 ; force a valid ACPI 3.X entry 104 | mov ecx, 24 ; ask for 24 bytes again 105 | int 0x15 106 | jc memmapend ; carry set means "end of list already reached" 107 | mov edx, 0x0534D4150 ; repair potentially trashed register 108 | jmpin: 109 | jcxz skipent ; skip any 0 length entries 110 | cmp cl, 20 ; got a 24 byte ACPI 3.X response? 111 | jbe notext 112 | test byte [es:di + 20], 1 ; if so: is the "ignore this data" bit clear? 113 | je skipent 114 | notext: 115 | mov ecx, [es:di + 8] ; get lower dword of memory region length 116 | test ecx, ecx ; is the qword == 0? 117 | jne goodent 118 | mov ecx, [es:di + 12] ; get upper dword of memory region length 119 | jecxz skipent ; if length qword is 0, skip entry 120 | goodent: 121 | inc bp ; got a good entry: ++count, move to next storage spot 122 | add di, 32 ; Pad to 32 bytes for each record 123 | skipent: 124 | test ebx, ebx ; if ebx resets to 0, list is complete 125 | jne e820lp 126 | nomemmap: 127 | memmapend: 128 | xor eax, eax ; Create a blank record for termination (32 bytes) 129 | mov ecx, 8 130 | rep stosd 131 | 132 | ; Enable the A20 gate 133 | set_A20: 134 | in al, 0x64 135 | test al, 0x02 136 | jnz set_A20 137 | mov al, 0xD1 138 | out 0x64, al 139 | check_A20: 140 | in al, 0x64 141 | test al, 0x02 142 | jnz check_A20 143 | mov al, 0xDF 144 | out 0x60, al 145 | 146 | mov cx, 0x4000 - 1 ; Start looking from here 147 | VBESearch: 148 | inc cx 149 | mov bx, cx ; Mode is saved to BX for the set command later 150 | cmp cx, 0x5000 151 | je halt 152 | mov edi, VBEModeInfoBlock ; VBE data will be stored at this address 153 | mov ax, 0x4F01 ; VESA SuperVGA BIOS - GET SuperVGA MODE INFORMATION - http://www.ctyme.com/intr/rb-0274.htm 154 | int 0x10 155 | cmp ax, 0x004F ; Return value in AX should equal 0x004F if command supported and successful 156 | jne VBESearch ; Try next mode 157 | cmp byte [VBEModeInfoBlock.BitsPerPixel], 32 ; Desired bit depth 158 | jne VBESearch ; If not equal, try next mode 159 | cmp word [VBEModeInfoBlock.XResolution], Horizontal_Resolution ; Desired XRes here 160 | jne VBESearch 161 | cmp word [VBEModeInfoBlock.YResolution], Vertical_Resolution ; Desired YRes here 162 | jne VBESearch 163 | or bx, 0x4000 ; Use linear/flat frame buffer model (set bit 14) 164 | mov ax, 0x4F02 ; VESA SuperVGA BIOS - SET SuperVGA VIDEO MODE - http://www.ctyme.com/intr/rb-0275.htm 165 | int 0x10 166 | cmp ax, 0x004F ; Return value in AX should equal 0x004F if supported and successful 167 | jne halt 168 | 169 | ; Read the 2nd stage boot loader into memory. 170 | mov ah, 0x42 ; Extended Read 171 | mov dl, [DriveNumber] ; http://www.ctyme.com/intr/rb-0708.htm 172 | mov si, DAP 173 | int 0x13 174 | jc halt 175 | 176 | mov bl, 'B' ; 'B' as we booted via BIOS 177 | 178 | ; At this point we are done with real mode and BIOS interrupts. Jump to 32-bit mode. 179 | cli ; No more interrupts 180 | lgdt [cs:GDTR32] ; Load GDT register 181 | mov eax, cr0 182 | or al, 0x01 ; Set protected mode bit 183 | mov cr0, eax 184 | jmp 8:0x8000 ; Jump to 32-bit protected mode 185 | 186 | halt: 187 | hlt 188 | jmp halt 189 | ;------------------------------------------------------------------------------ 190 | 191 | 192 | align 16 193 | GDTR32: ; Global Descriptors Table Register 194 | dw gdt32_end - gdt32 - 1 ; limit of GDT (size minus one) 195 | dq gdt32 ; linear address of GDT 196 | 197 | align 16 198 | gdt32: 199 | dw 0x0000, 0x0000, 0x0000, 0x0000 ; Null descriptor 200 | dw 0xFFFF, 0x0000, 0x9A00, 0x00CF ; 32-bit code descriptor 201 | dw 0xFFFF, 0x0000, 0x9200, 0x00CF ; 32-bit data descriptor 202 | gdt32_end: 203 | 204 | align 4 205 | 206 | DAP: 207 | db 0x10 208 | db 0x00 209 | dw DAP_SECTORS 210 | dw DAP_ADDRESS 211 | dw DAP_SEGMENT 212 | dq DAP_STARTSECTOR 213 | 214 | DriveNumber db 0x00 215 | 216 | times 446-$+$$ db 0 217 | 218 | ; Partition entries (4x 16-bytes) 219 | 220 | times 510-$+$$ db 0 221 | 222 | dw 0xAA55 ; Boot signature 223 | 224 | 225 | VBEModeInfoBlock: equ 0x5F00 226 | ; VESA 227 | ; Mandatory information for all VBE revisions 228 | VBEModeInfoBlock.ModeAttributes equ VBEModeInfoBlock + 0 ; DW - mode attributes 229 | VBEModeInfoBlock.WinAAttributes equ VBEModeInfoBlock + 2 ; DB - window A attributes 230 | VBEModeInfoBlock.WinBAttributes equ VBEModeInfoBlock + 3 ; DB - window B attributes 231 | VBEModeInfoBlock.WinGranularity equ VBEModeInfoBlock + 4 ; DW - window granularity in KB 232 | VBEModeInfoBlock.WinSize equ VBEModeInfoBlock + 6 ; DW - window size in KB 233 | VBEModeInfoBlock.WinASegment equ VBEModeInfoBlock + 8 ; DW - window A start segment 234 | VBEModeInfoBlock.WinBSegment equ VBEModeInfoBlock + 10 ; DW - window B start segment 235 | VBEModeInfoBlock.WinFuncPtr equ VBEModeInfoBlock + 12 ; DD - real mode pointer to window function 236 | VBEModeInfoBlock.BytesPerScanLine equ VBEModeInfoBlock + 16 ; DW - bytes per scan line 237 | ; Mandatory information for VBE 1.2 and above 238 | VBEModeInfoBlock.XResolution equ VBEModeInfoBlock + 18 ; DW - horizontal resolution in pixels or characters 239 | VBEModeInfoBlock.YResolution equ VBEModeInfoBlock + 20 ; DW - vertical resolution in pixels or characters 240 | VBEModeInfoBlock.XCharSize equ VBEModeInfoBlock + 22 ; DB - character cell width in pixels 241 | VBEModeInfoBlock.YCharSize equ VBEModeInfoBlock + 23 ; DB - character cell height in pixels 242 | VBEModeInfoBlock.NumberOfPlanes equ VBEModeInfoBlock + 24 ; DB - number of memory planes 243 | VBEModeInfoBlock.BitsPerPixel equ VBEModeInfoBlock + 25 ; DB - bits per pixel 244 | VBEModeInfoBlock.NumberOfBanks equ VBEModeInfoBlock + 26 ; DB - number of banks 245 | VBEModeInfoBlock.MemoryModel equ VBEModeInfoBlock + 27 ; DB - memory model type 246 | VBEModeInfoBlock.BankSize equ VBEModeInfoBlock + 28 ; DB - bank size in KB 247 | VBEModeInfoBlock.NumberOfImagePages equ VBEModeInfoBlock + 29 ; DB - number of image pages 248 | VBEModeInfoBlock.Reserved equ VBEModeInfoBlock + 30 ; DB - reserved (0x00 for VBE 1.0-2.0, 0x01 for VBE 3.0) 249 | ; Direct Color fields (required for direct/6 and YUV/7 memory models) 250 | VBEModeInfoBlock.RedMaskSize equ VBEModeInfoBlock + 31 ; DB - size of direct color red mask in bits 251 | VBEModeInfoBlock.RedFieldPosition equ VBEModeInfoBlock + 32 ; DB - bit position of lsb of red mask 252 | VBEModeInfoBlock.GreenMaskSize equ VBEModeInfoBlock + 33 ; DB - size of direct color green mask in bits 253 | VBEModeInfoBlock.GreenFieldPosition equ VBEModeInfoBlock + 34 ; DB - bit position of lsb of green mask 254 | VBEModeInfoBlock.BlueMaskSize equ VBEModeInfoBlock + 35 ; DB - size of direct color blue mask in bits 255 | VBEModeInfoBlock.BlueFieldPosition equ VBEModeInfoBlock + 36 ; DB - bit position of lsb of blue mask 256 | VBEModeInfoBlock.RsvdMaskSize equ VBEModeInfoBlock + 37 ; DB - size of direct color reserved mask in bits 257 | VBEModeInfoBlock.RsvdFieldPosition equ VBEModeInfoBlock + 38 ; DB - bit position of lsb of reserved mask 258 | VBEModeInfoBlock.DirectColorModeInfo equ VBEModeInfoBlock + 39 ; DB - direct color mode attributes 259 | ; Mandatory information for VBE 2.0 and above 260 | VBEModeInfoBlock.PhysBasePtr equ VBEModeInfoBlock + 40 ; DD - physical address for flat memory frame buffer 261 | VBEModeInfoBlock.Reserved1 equ VBEModeInfoBlock + 44 ; DD - Reserved - always set to 0 262 | VBEModeInfoBlock.Reserved2 equ VBEModeInfoBlock + 48 ; DD - Reserved - always set to 0 263 | 264 | ; EOF 265 | -------------------------------------------------------------------------------- /src/boot/uefi.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; UEFI loader for Pure64 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; Adapted from https://stackoverflow.com/questions/72947069/how-to-write-hello-world-efi-application-in-nasm 6 | ; and https://github.com/charlesap/nasm-uefi/blob/master/shoe-x64.asm 7 | ; PE https://wiki.osdev.org/PE 8 | ; GOP https://wiki.osdev.org/GOP 9 | ; Automatic boot: Assemble and save as /EFI/BOOT/BOOTX64.EFI 10 | ; Add payload up to 60KB 11 | ; dd if=PAYLOAD of=BOOTX64.EFI bs=4096 seek=1 conv=notrunc > /dev/null 2>&1 12 | ; ============================================================================= 13 | 14 | 15 | BITS 64 16 | ORG 0x00400000 17 | %define u(x) __utf16__(x) 18 | 19 | START: 20 | PE: 21 | HEADER: 22 | DOS_HEADER: ; 128 bytes 23 | DOS_SIGNATURE: db 'MZ', 0x00, 0x00 ; The DOS signature 24 | DOS_HEADERS: times 60-($-HEADER) db 0 ; The DOS Headers 25 | SIGNATURE_POINTER: dd PE_SIGNATURE - START ; Pointer to the PE Signature 26 | DOS_STUB: times 64 db 0 ; The DOS stub. Fill with zeros 27 | PE_HEADER: ; 24 bytes 28 | PE_SIGNATURE: db 'PE', 0x00, 0x00 ; This is the PE signature. The characters 'PE' followed by 2 null bytes 29 | MACHINE_TYPE: dw 0x8664 ; Targeting the x86-64 machine 30 | NUMBER_OF_SECTIONS: dw 2 ; Number of sections. Indicates size of section table that immediately follows the headers 31 | CREATED_DATE_TIME: dd 1670698099 ; Number of seconds since 1970 since when the file was created 32 | SYMBOL_TABLE_POINTER: dd 0 33 | NUMBER_OF_SYMBOLS: dd 0 34 | OHEADER_SIZE: dw O_HEADER_END - O_HEADER ; Size of the optional header 35 | CHARACTERISTICS: dw 0x222E ; Attributes of the file 36 | 37 | O_HEADER: 38 | MAGIC_NUMBER: dw 0x020B ; PE32+ (i.e. PE64) magic number 39 | MAJOR_LINKER_VERSION: db 0 40 | MINOR_LINKER_VERSION: db 0 41 | SIZE_OF_CODE: dd CODE_END - CODE ; The size of the code section 42 | INITIALIZED_DATA_SIZE: dd DATA_END - DATA ; Size of initialized data section 43 | UNINITIALIZED_DATA_SIZE: dd 0x00 ; Size of uninitialized data section 44 | ENTRY_POINT_ADDRESS: dd EntryPoint - START ; Address of entry point relative to image base when the image is loaded in memory 45 | BASE_OF_CODE_ADDRESS: dd CODE - START ; Relative address of base of code 46 | IMAGE_BASE: dq 0x400000 ; Where in memory we would prefer the image to be loaded at 47 | SECTION_ALIGNMENT: dd 0x1000 ; Alignment in bytes of sections when they are loaded in memory. Align to page boundary (4kb) 48 | FILE_ALIGNMENT: dd 0x1000 ; Alignment of sections in the file. Also align to 4kb 49 | MAJOR_OS_VERSION: dw 0 50 | MINOR_OS_VERSION: dw 0 51 | MAJOR_IMAGE_VERSION: dw 0 52 | MINOR_IMAGE_VERSION: dw 0 53 | MAJOR_SUBSYS_VERSION: dw 0 54 | MINOR_SUBSYS_VERSION: dw 0 55 | WIN32_VERSION_VALUE: dd 0 ; Reserved, must be 0 56 | IMAGE_SIZE: dd END - START ; The size in bytes of the image when loaded in memory including all headers 57 | HEADERS_SIZE: dd HEADER_END - HEADER ; Size of all the headers 58 | CHECKSUM: dd 0 59 | SUBSYSTEM: dw 10 ; The subsystem. In this case we're making a UEFI application. 60 | DLL_CHARACTERISTICS: dw 0 61 | STACK_RESERVE_SIZE: dq 0x200000 ; Reserve 2MB for the stack 62 | STACK_COMMIT_SIZE: dq 0x1000 ; Commit 4KB of the stack 63 | HEAP_RESERVE_SIZE: dq 0x200000 ; Reserve 2MB for the heap 64 | HEAP_COMMIT_SIZE: dq 0x1000 ; Commit 4KB of heap 65 | LOADER_FLAGS: dd 0x00 ; Reserved, must be zero 66 | NUMBER_OF_RVA_AND_SIZES: dd 0x00 ; Number of entries in the data directory 67 | O_HEADER_END: 68 | 69 | SECTION_HEADERS: 70 | SECTION_CODE: 71 | .name db ".text", 0x00, 0x00, 0x00 72 | .virtual_size dd CODE_END - CODE 73 | .virtual_address dd CODE - START 74 | .size_of_raw_data dd CODE_END - CODE 75 | .pointer_to_raw_data dd CODE - START 76 | .pointer_to_relocations dd 0 77 | .pointer_to_line_numbers dd 0 78 | .number_of_relocations dw 0 79 | .number_of_line_numbers dw 0 80 | .characteristics dd 0x70000020 81 | 82 | SECTION_DATA: 83 | .name db ".data", 0x00, 0x00, 0x00 84 | .virtual_size dd DATA_END - DATA 85 | .virtual_address dd DATA - START 86 | .size_of_raw_data dd DATA_END - DATA 87 | .pointer_to_raw_data dd DATA - START 88 | .pointer_to_relocations dd 0 89 | .pointer_to_line_numbers dd 0 90 | .number_of_relocations dw 0 91 | .number_of_line_numbers dw 0 92 | .characteristics dd 0xD0000040 93 | 94 | HEADER_END: 95 | 96 | align 16 97 | 98 | CODE: ; The code begins here with the entry point 99 | EntryPoint: 100 | ; Save the values passed by UEFI 101 | mov [EFI_IMAGE_HANDLE], rcx 102 | mov [EFI_SYSTEM_TABLE], rdx 103 | mov [EFI_RETURN], rsp 104 | sub rsp, 6*8+8 ; Fix stack 105 | 106 | ; When calling an EFI function the caller must pass the first 4 integer values in registers 107 | ; via RCX, RDX, R8, and R9 108 | ; 5 and onward are on the stack after the 32 byte shadow space 109 | 110 | ; Save entry addresses 111 | mov rax, [EFI_SYSTEM_TABLE] 112 | mov rax, [rax + EFI_SYSTEM_TABLE_BOOTSERVICES] 113 | mov [BS], rax 114 | mov rax, [EFI_SYSTEM_TABLE] 115 | mov rax, [rax + EFI_SYSTEM_TABLE_RUNTIMESERVICES] 116 | mov [RTS], rax 117 | mov rax, [EFI_SYSTEM_TABLE] 118 | mov rax, [rax + EFI_SYSTEM_TABLE_CONFIGURATION_TABLE] 119 | mov [CONFIG], rax 120 | mov rax, [EFI_SYSTEM_TABLE] 121 | mov rax, [rax + EFI_SYSTEM_TABLE_CONOUT] 122 | mov [OUTPUT], rax 123 | 124 | ; Set screen colour attributes 125 | mov rcx, [OUTPUT] ; IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This 126 | mov rdx, 0x07 ; IN UINTN Attribute - Black background, grey foreground 127 | call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_SET_ATTRIBUTE] 128 | 129 | ; Clear screen (This also sets the cursor position to 0,0) 130 | mov rcx, [OUTPUT] ; IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This 131 | call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_CLEAR_SCREEN] 132 | 133 | ; Output 'UEFI ' 134 | mov rcx, [OUTPUT] ; IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This 135 | lea rdx, [msg_uefi] ; IN CHAR16 *String 136 | call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUTSTRING] 137 | 138 | ; Find the address of the ACPI data from the UEFI configuration table 139 | mov rax, [EFI_SYSTEM_TABLE] 140 | mov rcx, [rax + EFI_SYSTEM_TABLE_NUMBEROFENTRIES] 141 | shl rcx, 3 ; Quick multiply by 4 142 | mov rsi, [CONFIG] 143 | nextentry: 144 | dec rcx 145 | cmp rcx, 0 146 | je error ; Bail out as no ACPI data was detected 147 | mov rdx, [ACPI_TABLE_GUID] ; First 64 bits of the ACPI GUID 148 | lodsq 149 | cmp rax, rdx ; Compare the table data to the expected GUID data 150 | jne nextentry 151 | mov rdx, [ACPI_TABLE_GUID+8] ; Second 64 bits of the ACPI GUID 152 | lodsq 153 | cmp rax, rdx ; Compare the table data to the expected GUID data 154 | jne nextentry 155 | lodsq ; Load the address of the ACPI table 156 | mov [ACPI], rax ; Save the address 157 | 158 | ; Find the interface to EFI_EDID_ACTIVE_PROTOCOL_GUID via its GUID 159 | mov rcx, EFI_EDID_ACTIVE_PROTOCOL_GUID ; IN EFI_GUID *Protocol 160 | mov rdx, 0 ; IN VOID *Registration OPTIONAL 161 | mov r8, EDID ; OUT VOID **Interface 162 | mov rax, [BS] 163 | mov rax, [rax + EFI_BOOT_SERVICES_LOCATEPROTOCOL] 164 | call rax 165 | cmp rax, EFI_SUCCESS 166 | je get_EDID ; If it exists, process EDID 167 | 168 | ; Find the interface to EFI_EDID_DISCOVERED_PROTOCOL_GUID via its GUID 169 | mov rcx, EFI_EDID_DISCOVERED_PROTOCOL_GUID ; IN EFI_GUID *Protocol 170 | mov rdx, 0 ; IN VOID *Registration OPTIONAL 171 | mov r8, EDID ; OUT VOID **Interface 172 | mov rax, [BS] 173 | mov rax, [rax + EFI_BOOT_SERVICES_LOCATEPROTOCOL] 174 | call rax 175 | cmp rax, EFI_SUCCESS 176 | je get_EDID ; If it exists, process EDID 177 | jmp use_GOP ; If not found, or other error, use GOP 178 | 179 | ; Gather preferred screen resolution 180 | get_EDID: 181 | ; Parse the EDID information 182 | ; 0 UINT32 - SizeOfEdid 183 | ; 4 UINT8 - *Edid 184 | mov rax, [EDID] 185 | mov ebx, [rax] 186 | cmp ebx, 128 ; Minimum size of 128 bytes 187 | jb use_GOP ; Fail out to GOP with default resolution 188 | mov rbx, [rax+8] ; Pointer to EDID. Why not +4? 189 | mov rax, [rbx] ; Load RAX with EDID header 190 | mov rcx, 0x00FFFFFFFFFFFF00 ; Required EDID header 191 | cmp rax, rcx ; Verify 8-byte header at 0x00 is 0x00FFFFFFFFFFFF00 192 | jne use_GOP ; Fail out to GOP with default resolution 193 | ; Preferred Timing Mode starts at 0x36 194 | ; 0x38 - Lower 8 bits of Horizontal pixels in bits 7:0 195 | ; 0x3A - Upper 4 bits of Horizontal pixels in bits 7:4 196 | ; 0x3B - Lower 8 bits of Vertical pixels in bits 7:0 197 | ; 0x3D - Upper 4 bits of Vertical pixels in bits 7:4 198 | xor eax, eax 199 | xor ecx, ecx 200 | mov al, [rbx+0x38] 201 | mov cl, [rbx+0x3A] 202 | and cl, 0xF0 ; Keep bits 7:4 203 | shl ecx, 4 204 | or eax, ecx 205 | mov [Horizontal_Resolution], eax 206 | xor eax, eax 207 | xor ecx, ecx 208 | mov al, [rbx+0x3B] 209 | mov cl, [rbx+0x3D] 210 | and cl, 0xF0 ; Keep bits 7:4 211 | shl ecx, 4 212 | or eax, ecx 213 | mov [Vertical_Resolution], eax 214 | 215 | ; Set video to desired resolution. By default it is 1024x768 unless EDID was found 216 | use_GOP: 217 | ; Find the interface to GRAPHICS_OUTPUT_PROTOCOL via its GUID 218 | mov rcx, EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID ; IN EFI_GUID *Protocol 219 | mov rdx, 0 ; IN VOID *Registration OPTIONAL 220 | mov r8, VIDEO ; OUT VOID **Interface 221 | mov rax, [BS] 222 | mov rax, [rax + EFI_BOOT_SERVICES_LOCATEPROTOCOL] 223 | call rax 224 | cmp rax, EFI_SUCCESS 225 | jne error 226 | 227 | ; Parse the current graphics information 228 | ; EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE Structure 229 | ; 0 UINT32 - MaxMode 230 | ; 4 UINT32 - Mode 231 | ; 8 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION - *Info; 232 | ; 16 UINTN - SizeOfInfo 233 | ; 24 EFI_PHYSICAL_ADDRESS - FrameBufferBase 234 | ; 32 UINTN - FrameBufferSize 235 | mov rax, [VIDEO] 236 | add rcx, EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE 237 | mov rcx, [rcx] ; RCX holds the address of the Mode structure 238 | mov eax, [rcx] ; EAX holds UINT32 MaxMode 239 | mov [vid_max], rax ; The maximum video modes we can check 240 | jmp vid_query 241 | 242 | next_video_mode: 243 | mov rax, [vid_index] 244 | add rax, 1 ; Increment the mode # to check 245 | mov [vid_index], rax 246 | mov rdx, [vid_max] 247 | cmp rax, rdx 248 | je skip_set_video ; If we have reached the max then bail out 249 | 250 | vid_query: 251 | ; Query a video mode 252 | mov rcx, [VIDEO] ; IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This 253 | mov rdx, [vid_index] ; IN UINT32 ModeNumber 254 | lea r8, [vid_size] ; OUT UINTN *SizeOfInfo 255 | lea r9, [vid_info] ; OUT EFI_GRAPHICS_OUTPUT_MODE_INFORMATION **Info 256 | call [rcx + EFI_GRAPHICS_OUTPUT_PROTOCOL_QUERY_MODE] 257 | 258 | ; Check mode settings 259 | mov rsi, [vid_info] 260 | lodsd ; UINT32 - Version 261 | lodsd ; UINT32 - HorizontalResolution 262 | cmp eax, [Horizontal_Resolution] 263 | jne next_video_mode 264 | lodsd ; UINT32 - VerticalResolution 265 | cmp eax, [Vertical_Resolution] 266 | jne next_video_mode 267 | lodsd ; EFI_GRAPHICS_PIXEL_FORMAT - PixelFormat (UINT32) 268 | bt eax, 0 ; Bit 0 is set for 32-bit colour mode 269 | jnc next_video_mode 270 | 271 | ; Set the video mode 272 | mov rcx, [VIDEO] ; IN EFI_GRAPHICS_OUTPUT_PROTOCOL *This 273 | mov rdx, [vid_index] ; IN UINT32 ModeNumber 274 | call [rcx + EFI_GRAPHICS_OUTPUT_PROTOCOL_SET_MODE] 275 | cmp rax, EFI_SUCCESS 276 | jne next_video_mode 277 | skip_set_video: 278 | 279 | ; Gather video mode details 280 | mov rcx, [VIDEO] 281 | add rcx, EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE 282 | mov rcx, [rcx] ; RCX holds the address of the Mode structure 283 | mov rax, [rcx+24] ; RAX holds the FB base 284 | mov [FB], rax ; Save the FB base 285 | mov rax, [rcx+32] ; RAX holds the FB size 286 | mov [FBS], rax ; Save the FB size 287 | mov rcx, [rcx+8] ; RCX holds the address of the EFI_GRAPHICS_OUTPUT_MODE_INFORMATION Structure 288 | ; EFI_GRAPHICS_OUTPUT_MODE_INFORMATION Structure 289 | ; 0 UINT32 - Version 290 | ; 4 UINT32 - HorizontalResolution 291 | ; 8 UINT32 - VerticalResolution 292 | ; 12 EFI_GRAPHICS_PIXEL_FORMAT - PixelFormat (UINT32) 293 | ; 16 EFI_PIXEL_BITMASK - PixelInformation (4 UINT32 - RedMask, GreenMask, BlueMask, ReservedMask) 294 | ; 32 UINT32 - PixelsPerScanLine - Defines the number of pixel elements per video memory line. Scan lines may be padded for memory alignment. 295 | mov eax, [rcx+4] ; RAX holds the Horizontal Resolution 296 | mov [HR], rax ; Save the Horizontal Resolution 297 | mov eax, [rcx+8] ; RAX holds the Vertical Resolution 298 | mov [VR], rax ; Save the Vertical Resolution 299 | mov eax, [rcx+32] ; RAX holds the PixelsPerScanLine 300 | mov [PPSL], rax ; Save the PixelsPerScanLine 301 | 302 | ; Check for payload 303 | mov rsi, PAYLOAD+6 304 | mov ax, [rsi] 305 | cmp ax, 0x3436 ; Match against the '64' in the Pure64 binary 306 | jne sig_fail ; Bail out if Pure64 isn't present 307 | 308 | ; Debug 309 | ; mov rbx, [FB] ; Display the framebuffer address 310 | ; call printhex 311 | 312 | get_memmap: 313 | ; Output 'OK' as we are about to leave UEFI 314 | mov rcx, [OUTPUT] ; IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This 315 | lea rdx, [msg_OK] ; IN CHAR16 *String 316 | call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUTSTRING] 317 | 318 | ; Get Memory Map from UEFI and save it [memmap] 319 | lea rcx, [memmapsize] ; IN OUT UINTN *MemoryMapSize 320 | mov rdx, [memmap] ; OUT EFI_MEMORY_DESCRIPTOR *MemoryMap 321 | lea r8, [memmapkey] ; OUT UINTN *MapKey 322 | lea r9, [memmapdescsize] ; OUT UINTN *DescriptorSize 323 | lea r10, [memmapdescver] ; OUT UINT32 *DescriptorVersion 324 | mov [rsp+32], r10 325 | mov rax, [BS] 326 | call [rax + EFI_BOOT_SERVICES_GETMEMORYMAP] 327 | cmp al, EFI_BUFFER_TOO_SMALL 328 | je get_memmap ; Attempt again as the memmapsize was updated by EFI 329 | cmp rax, EFI_SUCCESS 330 | jne exitfailure 331 | ; Each 48-byte record has the following format: 332 | ; 0 UINT32 - Type 333 | ; 4 UNIT32 - Padding 334 | ; 8 EFI_PHYSICAL_ADDRESS (UINT64) - PhysicalStart 335 | ; 16 EFI_VIRTUAL_ADDRESS (UINT64) - VirtualStart 336 | ; 24 UINT64 - NumberOfPages - This is a number of 4K pages (must be a non-zero value) 337 | ; 32 UINT64 - Attribute 338 | ; 40 UINT64 - Blank 339 | ; 340 | ; UEFI Type: 341 | ; 0x0 = EfiReservedMemoryType - Not usable 342 | ; 0x1 = EfiLoaderCode - Usable after ExitBootSerivces 343 | ; 0x2 = EfiLoaderData - Usable after ExitBootSerivces 344 | ; 0x3 = EfiBootServicesCode - Usable after ExitBootSerivces 345 | ; 0x4 = EfiBootServicesData - Usable after ExitBootSerivces 346 | ; 0x5 = EfiRuntimeServicesCode 347 | ; 0x6 = EfiRuntimeServicesData 348 | ; 0x7 = EfiConventionalMemory - Usable after ExitBootSerivces 349 | ; 0x8 = EfiUnusableMemory - Not usable - errors detected 350 | ; 0x9 = EfiACPIReclaimMemory - Usable after ACPI is enabled 351 | ; 0xA = EfiACPIMemoryNVS 352 | ; 0xB = EfiMemoryMappedIO 353 | ; 0xC = EfiMemoryMappedIOPortSpace 354 | ; 0xD = EfiPalCode 355 | ; 0xE = EfiPersistentMemory 356 | ; 0xF = EfiMaxMemoryTyp 357 | 358 | ; Exit Boot services as UEFI is no longer needed 359 | mov rcx, [EFI_IMAGE_HANDLE] ; IN EFI_HANDLE ImageHandle 360 | mov rdx, [memmapkey] ; IN UINTN MapKey 361 | mov rax, [BS] 362 | call [rax + EFI_BOOT_SERVICES_EXITBOOTSERVICES] 363 | cmp rax, EFI_SUCCESS 364 | jne get_memmap ; If it failed, get the memory map and try to exit again 365 | 366 | ; Stop interrupts 367 | cli 368 | 369 | ; Copy Pure64 to the correct memory address 370 | mov rsi, PAYLOAD 371 | mov rdi, 0x8000 372 | mov rcx, 32768 ; Copy 32 KiB to 0x8000 373 | rep movsb 374 | 375 | ; Save UEFI values to the area of memory where Pure64 expects them 376 | mov rdi, 0x00005F00 377 | mov rax, [FB] 378 | stosq ; 64-bit Frame Buffer Base 379 | mov rax, [FBS] 380 | stosq ; 64-bit Frame Buffer Size in bytes 381 | mov rax, [HR] 382 | stosw ; 16-bit Screen X 383 | mov rax, [VR] 384 | stosw ; 16-bit Screen Y 385 | mov rax, [PPSL] 386 | stosw ; 16-bit PixelsPerScanLine 387 | mov rax, 32 ; TODO - Verify this 388 | stosw ; 16-bit BitsPerPixel 389 | mov rax, [memmap] 390 | mov rdx, rax ; Save Memory Map Base address to RDX 391 | stosq ; Memory Map Base 392 | mov rax, [memmapsize] 393 | add rdx, rax ; Add Memory Map Size to RDX 394 | stosq ; Size of Memory Map in bytes 395 | mov rax, [memmapkey] 396 | stosq ; The key used to exit Boot Services 397 | mov rax, [memmapdescsize] 398 | stosq ; EFI_MEMORY_DESCRIPTOR size in bytes 399 | mov rax, [memmapdescver] 400 | stosq ; EFI_MEMORY_DESCRIPTOR version 401 | mov rax, [ACPI] 402 | stosq ; ACPI Table Address 403 | mov rax, [EDID] 404 | stosq ; EDID Data (Size and Address) 405 | 406 | ; Add blank entries to the end of the UEFI memory map 407 | mov rdi, rdx ; RDX holds address to end of memory map 408 | xor eax, eax 409 | mov ecx, 8 410 | rep stosq 411 | 412 | ; Set screen to black before jumping to Pure64 413 | mov rdi, [FB] 414 | mov eax, 0x00000000 ; 0x00RRGGBB 415 | mov rcx, [FBS] 416 | shr rcx, 2 ; Quick divide by 4 (32-bit colour) 417 | rep stosd 418 | 419 | ; Clear registers 420 | xor eax, eax 421 | xor ecx, ecx 422 | xor edx, edx 423 | xor ebx, ebx 424 | mov rsp, 0x8000 425 | xor ebp, ebp 426 | xor esi, esi 427 | xor edi, edi 428 | xor r8, r8 429 | xor r9, r9 430 | xor r10, r10 431 | xor r11, r11 432 | xor r12, r12 433 | xor r13, r13 434 | xor r14, r14 435 | xor r15, r15 436 | 437 | mov bl, 'U' 438 | jmp 0x8000 439 | 440 | exitfailure: 441 | ; Set screen to red on exit failure 442 | mov rdi, [FB] 443 | mov eax, 0x00FF0000 ; 0x00RRGGBB 444 | mov rcx, [FBS] 445 | shr rcx, 2 ; Quick divide by 4 (32-bit colour) 446 | rep stosd 447 | error: 448 | mov rcx, [OUTPUT] ; IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This 449 | lea rdx, [msg_error] ; IN CHAR16 *String 450 | call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUTSTRING] 451 | jmp halt 452 | sig_fail: 453 | mov rcx, [OUTPUT] ; IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This 454 | lea rdx, [msg_SigFail] ; IN CHAR16 *String 455 | call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUTSTRING] 456 | halt: 457 | hlt 458 | jmp halt 459 | 460 | ; ----------------------------------------------------------------------------- 461 | ; printhex - Display a 64-bit value in hex 462 | ; IN: RBX = Value 463 | printhex: 464 | mov rbp, 16 ; Counter 465 | push rax 466 | push rcx 467 | push rdx ; 3 pushes also align stack on 16 byte boundary 468 | ; (8+3*8)=32, 32 evenly divisible by 16 469 | sub rsp, 32 ; Allocate 32 bytes of shadow space 470 | printhex_loop: 471 | rol rbx, 4 472 | mov rax, rbx 473 | and rax, 0Fh 474 | lea rcx, [Hex] 475 | mov rax, [rax + rcx] 476 | mov byte [Num], al 477 | lea rdx, [Num] 478 | mov rcx, [OUTPUT] ; IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This 479 | call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUTSTRING] 480 | dec rbp 481 | jnz printhex_loop 482 | lea rdx, [newline] 483 | mov rcx, [OUTPUT] ; IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *This 484 | call [rcx + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUTSTRING] 485 | 486 | add rsp, 32 487 | pop rdx 488 | pop rcx 489 | pop rax 490 | ret 491 | ; ----------------------------------------------------------------------------- 492 | 493 | 494 | times 2048-$+$$ db 0 495 | CODE_END: 496 | 497 | ; Data begins here 498 | DATA: 499 | EFI_IMAGE_HANDLE: dq 0 ; EFI gives this in RCX 500 | EFI_SYSTEM_TABLE: dq 0 ; And this in RDX 501 | EFI_RETURN: dq 0 ; And this in RSP 502 | BS: dq 0 ; Boot services 503 | RTS: dq 0 ; Runtime services 504 | CONFIG: dq 0 ; Config Table address 505 | ACPI: dq 0 ; ACPI table address 506 | OUTPUT: dq 0 ; Output services 507 | VIDEO: dq 0 ; Video services 508 | EDID: dq 0 509 | FB: dq 0 ; Frame buffer base address 510 | FBS: dq 0 ; Frame buffer size 511 | HR: dq 0 ; Horizontal Resolution 512 | VR: dq 0 ; Vertical Resolution 513 | PPSL: dq 0 ; PixelsPerScanLine 514 | BPP: dq 0 ; BitsPerPixel 515 | memmap: dq 0x220000 ; Store the Memory Map from UEFI here 516 | memmapsize: dq 32768 ; Max size we are expecting in bytes 517 | memmapkey: dq 0 518 | memmapdescsize: dq 0 519 | memmapdescver: dq 0 520 | vid_orig: dq 0 521 | vid_index: dq 0 522 | vid_max: dq 0 523 | vid_size: dq 0 524 | vid_info: dq 0 525 | Horizontal_Resolution: dd 1024 ; Default resolution X - If no EDID found 526 | Vertical_Resolution: dd 768 ; Default resolution Y - If no EDID found 527 | 528 | ACPI_TABLE_GUID: 529 | dd 0xeb9d2d30 530 | dw 0x2d88, 0x11d3 531 | db 0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d 532 | 533 | EFI_EDID_ACTIVE_PROTOCOL_GUID: 534 | dd 0xbd8c1056 535 | dw 0x9f36, 0x44ec 536 | db 0x92, 0xa8, 0xa6, 0x33, 0x7f, 0x81, 0x79, 0x86 537 | 538 | EFI_EDID_DISCOVERED_PROTOCOL_GUID: 539 | dd 0x1c0c34f6 540 | dw 0xd380, 0x41fa 541 | db 0xa0, 0x49, 0x8a, 0xd0, 0x6c, 0x1a, 0x66, 0xaa 542 | 543 | EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID: 544 | dd 0x9042a9de 545 | dw 0x23dc, 0x4a38 546 | db 0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a 547 | 548 | msg_uefi: dw u('UEFI '), 0 549 | msg_OK: dw u('OK '), 0 550 | msg_error: dw u('Error'), 0 551 | msg_SigFail: dw u('Bad Sig!'), 0 552 | Hex: db '0123456789ABCDEF' 553 | Num: dw 0, 0 554 | newline: dw 13, 10, 0 555 | 556 | 557 | align 4096 ; Pad out to 4KiB for UEFI loader 558 | PAYLOAD: 559 | 560 | align 65536 ; Pad out to 64KiB for payload (Pure64, Kernel, etc) 561 | RAMDISK: 562 | 563 | times 65535+1048576-$+$$ db 0 ; 1MiB of padding for RAM disk image 564 | DATA_END: 565 | END: 566 | 567 | ; Define the needed EFI constants and offsets here. 568 | EFI_SUCCESS equ 0 569 | EFI_LOAD_ERROR equ 1 570 | EFI_INVALID_PARAMETER equ 2 571 | EFI_UNSUPPORTED equ 3 572 | EFI_BAD_BUFFER_SIZE equ 4 573 | EFI_BUFFER_TOO_SMALL equ 5 574 | EFI_NOT_READY equ 6 575 | EFI_DEVICE_ERROR equ 7 576 | EFI_WRITE_PROTECTED equ 8 577 | EFI_OUT_OF_RESOURCES equ 9 578 | EFI_VOLUME_CORRUPTED equ 10 579 | EFI_VOLUME_FULL equ 11 580 | EFI_NO_MEDIA equ 12 581 | EFI_MEDIA_CHANGED equ 13 582 | EFI_NOT_FOUND equ 14 583 | 584 | EFI_SYSTEM_TABLE_CONOUT equ 64 585 | EFI_SYSTEM_TABLE_RUNTIMESERVICES equ 88 586 | EFI_SYSTEM_TABLE_BOOTSERVICES equ 96 587 | EFI_SYSTEM_TABLE_NUMBEROFENTRIES equ 104 588 | EFI_SYSTEM_TABLE_CONFIGURATION_TABLE equ 112 589 | 590 | EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_RESET equ 0 591 | EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_OUTPUTSTRING equ 8 592 | EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_TEST_STRING equ 16 593 | EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_QUERY_MODE equ 24 594 | EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_SET_MODE equ 32 595 | EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_SET_ATTRIBUTE equ 40 596 | EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_CLEAR_SCREEN equ 48 597 | EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_SET_CURSOR_POSITION equ 56 598 | EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_ENABLE_CURSOR equ 64 599 | EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_MODE equ 70 600 | 601 | EFI_BOOT_SERVICES_GETMEMORYMAP equ 56 602 | EFI_BOOT_SERVICES_LOCATEHANDLE equ 176 603 | EFI_BOOT_SERVICES_LOADIMAGE equ 200 604 | EFI_BOOT_SERVICES_EXIT equ 216 605 | EFI_BOOT_SERVICES_EXITBOOTSERVICES equ 232 606 | EFI_BOOT_SERVICES_STALL equ 248 607 | EFI_BOOT_SERVICES_SETWATCHDOGTIMER equ 256 608 | EFI_BOOT_SERVICES_LOCATEPROTOCOL equ 320 609 | 610 | EFI_GRAPHICS_OUTPUT_PROTOCOL_QUERY_MODE equ 0 611 | EFI_GRAPHICS_OUTPUT_PROTOCOL_SET_MODE equ 8 612 | EFI_GRAPHICS_OUTPUT_PROTOCOL_BLT equ 16 613 | EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE equ 24 614 | 615 | EFI_RUNTIME_SERVICES_RESETSYSTEM equ 104 616 | 617 | ; EOF 618 | -------------------------------------------------------------------------------- /src/fdc/dma.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; DMA Driver 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; Implemented by Isa Isoux -- https://github.com/isoux/BMFLFS 5 | 6 | ; The DMA controller performs data transfers: 7 | ; DMA read – transfer from memory to I/O device 8 | ; DMA write - transfer from I/O device to memory 9 | ; Memory to memory transfers 10 | 11 | ; Here is a minimal simplified version intended only for floppy drive 12 | 13 | ; DMA - controller for 8 bit transfer 14 | ; ============================================================================= 15 | 16 | ;DMA_controler1: 17 | DMA1_CH2_CAR EQU 0x04 ; 16 bits 18 | DMA1_CH2_CWCR EQU 0x05 ; 16 bits 19 | DMA1_MASK_REG EQU 0x0A 20 | DMA1_MODE_REG EQU 0x0B ; 8 bits Mode register – MR; 21 | DMA1_CLEAR_REG EQU 0x0C 22 | 23 | ;DMA_page_register 24 | DMA1_CHR_ADDR EQU 0x81 25 | 26 | ; ----------------------------------------------------------------------------- 27 | ; dma_IO: 28 | ; DMA read write procedure 29 | ; IN: 30 | ; ecx = page:offset 31 | ; bl = channel 32 | ; bh = 1 = read, 0 = write 33 | ; esi = count 34 | dma_IO: 35 | 36 | ; Examine whether it is writing or reading 37 | ; what_mode 38 | or bh, bh 39 | jz write_mode 40 | mov bh, bl 41 | add bh, 0x48 ; + Ch2 = 0x4A 42 | jmp short read_mode 43 | write_mode: ; DMA write: transfer from I/O device to memory; 44 | mov bh, bl 45 | add bh, 0x44 ; + Ch2 = 0x46 46 | read_mode: ; DMA read: transfer from memory to I/O device; 47 | 48 | ; Disable 2. channel 49 | dec esi 50 | movzx dx, byte[mask_reg] 51 | mov al, bl 52 | or al, byte[ch_car] 53 | out dx, al ; disable the channel 54 | 55 | ; Setup flip flop 56 | movzx dx, byte[clear_reg] 57 | mov al, 0 58 | out dx, al ; init flip_flop 59 | 60 | ; At mode register put 0x46 for write or 0x4A for read 61 | ; Set mode for read or write 62 | movzx dx, byte[mode_reg] 63 | mov al, bh ; 1=read, 0=write 64 | out dx, al ; set DMA mode 65 | 66 | ; Write low-high offsets 67 | movzx dx, byte[ch_car] 68 | mov al, cl 69 | out dx, al ; write low offset 70 | mov al, ch 71 | out dx, al ; write high offset 72 | 73 | ; write_pg_port 74 | movzx dx, byte[pg_prt_reg] 75 | mov eax, ecx 76 | shr eax, 16 77 | out dx, al ; write page. 78 | 79 | ; Write_cnt_port 80 | movzx eax, bl 81 | movzx dx, byte[ch_cwcr] 82 | mov eax, esi 83 | out dx, al ; low count 84 | mov al, ah 85 | out dx, al ; high count 86 | 87 | ; Enable channel 2 at mask_reg 88 | movzx dx, byte[mask_reg] 89 | mov al, bl 90 | out dx, al ; enable channel 91 | ret 92 | ; ----------------------------------------------------------------------------- 93 | 94 | ; Everything is for dma1 channel2 = FDC 95 | ; dma_struct: 96 | ch_car db DMA1_CH2_CAR ; Chanel x ADDRES REGISTER 97 | ch_cwcr db DMA1_CH2_CWCR ; Chanel word count 98 | mask_reg db DMA1_MASK_REG ; Master register 99 | mode_reg db DMA1_MODE_REG ; Mode register 100 | clear_reg db DMA1_CLEAR_REG ; Clear MSB/LSB flip flop 101 | pg_prt_reg db DMA1_CHR_ADDR ; Page port reg = High 4 bit of DMA channel x address 102 | 103 | 104 | ; ============================================================================= 105 | ; EOF 106 | -------------------------------------------------------------------------------- /src/fdc/fdc_64.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; Floppy Disk Driver 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; Implemented by Isa Isoux -- https://github.com/isoux/BMFLFS 5 | 6 | ; Here is a minimal simplified version intended only for reading whole floppy 7 | ; disk into memory or writing back image from memory to whole disk at once. 8 | ; Errors are poorly handled due to the simplicity of the handling, one thing 9 | ; that can be easily noticed is that the disk is damaged and does not load 10 | ; completely. 11 | 12 | ; FDC - Floppy Disk Controler 13 | ; ============================================================================= 14 | 15 | STAT_REG_A EQU 0x3F0 ; read-only 16 | STAT_REG_B EQU 0x3F1 ; read-only 17 | DIG_OUT_REG EQU 0x3F2 ; write-only 18 | MAIN_STAT_REG EQU 0x3F4 ; read-only 19 | DATA_REG EQU 0x3F5 ; write-only 20 | DIG_IN_REG EQU 0x3F7 ; read-only 21 | 22 | READ EQU 0x00 23 | WRITE EQU 0x01 24 | 25 | DMA_BUFF EQU 0x210000 ; temporary buffer for DMA 26 | MEM_BUFF EQU 0x400000 ; Start address of memory buffer 27 | 28 | ; Some constants needed for increasing delay for 29 | ; recalibrate, seek track and interrupt 30 | ;right choices is slower... 31 | IRQ_DELAY_STEP EQU 0x40 ;0x60 ;0x80 ;0xa0 ;0xaa 32 | CALIB_DELAY_STEP EQU 0x60 ;0xa0 ;0x100 ;0x180 ;0x200 33 | SEEK_DELAY_STEP EQU 0x100 ;0x180 ;0x200 ;0x240 ;0x280 34 | SHL_HEAD_STEP EQU 8 ;8 ;8 ;8 ;8 35 | 36 | 37 | ; ----------------------------------------------------------------------------- 38 | fdc_sendbyte: 39 | push rcx 40 | push rax 41 | 42 | mov rcx, 0xff 43 | .delay: 44 | loop .delay 45 | 46 | .l1: 47 | mov dx, MAIN_STAT_REG ; 0x3F4 check status reg 48 | in al, dx 49 | and al, 11000000b ; 0xC0 MRQ=1 DIO=1 50 | cmp al, 10000000b ; 0x80 MRQ=1 DIO=0 ready for write? 51 | jnz .l1 52 | pop rax 53 | pop rcx 54 | mov dx, DATA_REG ; 0x3F5 send byte 55 | out dx, al 56 | ret 57 | ; ----------------------------------------------------------------------------- 58 | 59 | 60 | ; ----------------------------------------------------------------------------- 61 | fdc_getbyte: 62 | push rcx 63 | push rax 64 | 65 | mov rcx, 0xff 66 | .delay: 67 | loop .delay 68 | 69 | .l1: 70 | mov dx, MAIN_STAT_REG ; 0x3F4 check status reg 71 | in al, dx 72 | and al, 11000000b ; 0xC0 MRQ=1 DIO=1 73 | cmp al, 11000000b ; 0xC0 MRQ=1 DIO=0 ready for read? 74 | jnz .l1 75 | pop rax 76 | pop rcx 77 | mov dx, DATA_REG ; 0x3F5 get the byte 78 | in al, dx 79 | ret 80 | ; ----------------------------------------------------------------------------- 81 | 82 | 83 | ; ----------------------------------------------------------------------------- 84 | wait_for_irq: 85 | push rax 86 | push rcx 87 | 88 | xor ecx, ecx 89 | xor eax, eax 90 | mov eax, [track] 91 | shl eax, SHL_HEAD_STEP 92 | mov rcx, IRQ_DELAY_STEP 93 | add rcx, rax 94 | .delay: 95 | loop .delay 96 | 97 | .l1: 98 | mov rax, [int_done] 99 | or rax, rax 100 | jz .l1 101 | 102 | clc 103 | pop rcx 104 | pop rax 105 | ret 106 | ; ----------------------------------------------------------------------------- 107 | 108 | 109 | ; ----------------------------------------------------------------------------- 110 | ; Sense interrupt status command 111 | sense_isc: 112 | push rax 113 | 114 | mov al, 0x08 ; fdc command 115 | call fdc_sendbyte 116 | call fdc_getbyte 117 | mov ah, al ; save ST0 in ah 118 | call fdc_getbyte ; read PCN 119 | clc 120 | test ah, 0x80 ; test for error: 121 | jz .end ; "invalid command" 122 | stc 123 | .end: 124 | pop rax 125 | ret 126 | ; ----------------------------------------------------------------------------- 127 | 128 | 129 | ; ----------------------------------------------------------------------------- 130 | fdd_mot_on: 131 | push rcx 132 | 133 | mov dx, DIG_OUT_REG ; 0x3F2 134 | mov al, 00011100b ; 0x1C motor 0 on 135 | out dx, al 136 | 137 | mov rcx, 0xffff 138 | delay2: 139 | loop delay2 140 | 141 | mov byte [motor_on], 1 142 | 143 | pop rcx 144 | ret 145 | ; ----------------------------------------------------------------------------- 146 | 147 | 148 | ; ----------------------------------------------------------------------------- 149 | fdd_mot_off: 150 | push rcx 151 | 152 | mov dx, DIG_OUT_REG ; 0x3F2 153 | mov al, 00000000b ; 0x0 motor 0 off 154 | out dx, al 155 | 156 | mov rcx, 0xffff 157 | delay3: 158 | loop delay3 159 | 160 | mov byte [motor_on], 0 161 | 162 | pop rcx 163 | ret 164 | ; ----------------------------------------------------------------------------- 165 | 166 | 167 | ; ----------------------------------------------------------------------------- 168 | fdc_recalib: 169 | push rax 170 | push rcx 171 | test byte [motor_on], 1 172 | jnz .l1 173 | call fdd_mot_on ; turn motor on 174 | .l1: 175 | mov al, 0x07 ; recalibrate command 176 | call fdc_sendbyte 177 | mov al, 0x00 ; selects drive a: 178 | call fdc_sendbyte 179 | mov byte [res_C], 0 180 | 181 | xor ecx, ecx 182 | xor eax, eax 183 | mov eax, [track] 184 | shl eax, SHL_HEAD_STEP 185 | mov rcx, CALIB_DELAY_STEP 186 | add rcx, rax 187 | .delay: 188 | loop .delay 189 | 190 | mov word [int_done], 0 191 | call wait_for_irq ; wait for floppy int. 192 | jc .error 193 | 194 | call sense_isc ; sense interrupt status command 195 | jc .error 196 | 197 | .error: 198 | stc 199 | pop rcx 200 | pop rax 201 | ret 202 | ; ----------------------------------------------------------------------------- 203 | 204 | 205 | ; ----------------------------------------------------------------------------- 206 | fdd_seek: 207 | push rcx 208 | push rax 209 | 210 | mov al, 0x0F ; seek command 211 | call fdc_sendbyte 212 | mov al, [driveno] ; drive # (00 = A) 213 | call fdc_sendbyte 214 | mov al, [track] ; cylinder # 215 | call fdc_sendbyte 216 | 217 | xor ecx, ecx 218 | xor eax, eax 219 | mov eax, [track] 220 | shl eax, SHL_HEAD_STEP 221 | mov rcx, SEEK_DELAY_STEP 222 | add rcx, rax 223 | .delay: 224 | loop .delay 225 | 226 | mov word [int_done], 0 227 | call wait_for_irq ; wait for floppy int. 228 | jc .error 229 | 230 | call sense_isc ; sense interrupt status command 231 | jc .error 232 | 233 | pop rax 234 | clc 235 | jmp short .end 236 | .error: 237 | pop rax 238 | .end: 239 | pop rcx 240 | ret 241 | ; ----------------------------------------------------------------------------- 242 | 243 | 244 | ; ----------------------------------------------------------------------------- 245 | ; Read or Write whole track from both sides (heads) 246 | fdd_rw: 247 | push rcx 248 | 249 | mov byte[errorcode], 4 ; put error code in ah, just incase 250 | 251 | test byte [motor_on], 1 252 | jnz .l1 253 | call fdd_mot_on 254 | .l1: 255 | mov dx, DIG_IN_REG ; 0x3F7 256 | mov al, 00000000b ; 500Kb/sec mode 257 | out dx, al 258 | mov byte [errorcode], 0x80 ; put basic error code, just in case. 259 | 260 | call fdc_recalib 261 | 262 | xor rcx, rcx 263 | mov cx, 3 ; we want to try seek 3 times 264 | .l2: 265 | call fdd_seek ; we need to move to the right track. 266 | jnc .l3 ; we should be on the right track. 267 | loop .l2 268 | jmp .error ; timeout. 269 | .l3: 270 | mov dx, MAIN_STAT_REG ; check status reg (to see if DMA bit set) 271 | in al, dx 272 | test al, 00100000b ; test sr0 is 0x80 273 | jnz .error 274 | 275 | cmp byte [rw], READ 276 | je .read_fdd 277 | 278 | ; write_fdd: 279 | mov bl, 2 ; channel 2 280 | mov esi, 0x4800 ; bytes to write 281 | mov rcx, [dma_buff] ; load address of buffer for transfer 282 | mov bh, 1 ; "read DMA" from writing at floppy, 283 | call dma_IO 284 | 285 | mov al, 0xC5 ; write sector command 286 | call fdc_sendbyte 287 | jmp .cont 288 | 289 | .read_fdd: 290 | mov bl, 2 ; channel 2 291 | mov esi, 0x4800 ; bytes to read 292 | mov rcx, [dma_buff] ; load address of buffer for transfer 293 | mov bh, 0 ; "write DMA" from reading of floppy, 294 | call dma_IO 295 | 296 | mov al, 0xE6 ; 0xe6 read sector command 297 | call fdc_sendbyte 298 | 299 | .cont: 300 | mov al, [driveno] ; head no. 0, drive A: 301 | call fdc_sendbyte 302 | mov al, [track] ; cylinder 303 | call fdc_sendbyte 304 | 305 | mov al, 0 ; head side 0, but DMA menage to be and 1 306 | call fdc_sendbyte 307 | mov al, 1 ; sector number, starts at 1 308 | call fdc_sendbyte 309 | mov al, 0x02 ; sector size - 512 bytes 310 | call fdc_sendbyte 311 | 312 | mov al, 0x12 ; 18 decimal sectors to a track 313 | call fdc_sendbyte 314 | mov al, 27 ; gap length for a 3.5" 1.44Mb 315 | call fdc_sendbyte 316 | mov al, 0xFF ; not used data length cause sec size has been filled 317 | call fdc_sendbyte 318 | 319 | mov word [int_done], 0 320 | call wait_for_irq ; wait for floppy int. 321 | jc .error 322 | 323 | call fdc_getbyte 324 | mov [res_ST0], al ; save res_ of ST0 in var 325 | call fdc_getbyte 326 | mov [dummy_var], al ; save res_ of ST1 in var 327 | call fdc_getbyte 328 | mov [dummy_var], al ; save res_ of ST2 in var 329 | call fdc_getbyte 330 | mov [res_C], al ; save res_ of cylinder 331 | call fdc_getbyte 332 | mov [dummy_var], al ; save res_ of head 333 | call fdc_getbyte 334 | mov [res_R], al ; save res_ of sector number. 335 | call fdc_getbyte 336 | mov [dummy_var], al ; save res_ of sector size 337 | 338 | jmp short .end 339 | 340 | .error: 341 | stc 342 | .end: 343 | pop rcx 344 | ret 345 | ; ----------------------------------------------------------------------------- 346 | 347 | 348 | ; ----------------------------------------------------------------------------- 349 | ; This procedure is related with pure64.asm file from Pure64 repo 350 | show_status: 351 | xor ebx, ebx 352 | mov bl, [progressbar] 353 | call debug_progressbar 354 | add byte [progressbar], 2 355 | ret 356 | ; ----------------------------------------------------------------------------- 357 | 358 | 359 | ; ----------------------------------------------------------------------------- 360 | ; Because DMA has a 64Kb limit, in one shut it is possible to read only 3 361 | ; cylinders from both sides: 3 * 0x4800 = 0xd800 or 55296 bytes * 26 repetitions 362 | ; = 78 + 2 = 80 cylinders on 1.44 Mb floppy 363 | ; Read whole disk 364 | read_floppy: 365 | mov rcx, 26 366 | .l_26: 367 | push rcx 368 | mov rcx, 3 369 | .l_3: 370 | push rcx 371 | call fdd_rw ; load whole track * 2 side 372 | call show_status ; show progress 373 | pop rcx 374 | inc byte [track] 375 | add dword [dma_buff], 0x4800 ; track * 2 sides of head 376 | loop .l_3 377 | 378 | ; Copy 3 tracks (* 2 head ) to mem_buff 379 | xor esi, esi 380 | xor edi, edi 381 | xor ecx, ecx 382 | cld 383 | mov esi, DMA_BUFF 384 | mov edi, [mem_buff] 385 | cmp byte [track], 80 386 | je .final 387 | mov rcx, 0x1b00 388 | rep movsq 389 | mov dword [dma_buff], DMA_BUFF 390 | mov [mem_buff], rdi 391 | pop rcx 392 | loop .l_26 393 | 394 | ; load last 2 tracks 395 | mov rcx, 2 396 | jmp .l_3 397 | .final: 398 | mov rcx, 0x1200 399 | rep movsq 400 | call fdd_mot_off 401 | ret 402 | ; ----------------------------------------------------------------------------- 403 | 404 | 405 | int_done dw 0 406 | motor_on db 0 407 | driveno db 0 408 | track db 0 409 | errorcode db 0 410 | res_ST0 db 0 411 | dummy_var db 0 ; For simplicity and memory minimizing 412 | res_C db 0 413 | res_R db 0 414 | rw db 0 415 | 416 | progressbar db 0 417 | dma_buff dd DMA_BUFF 418 | mem_buff dd MEM_BUFF 419 | 420 | 421 | ; ============================================================================= 422 | ; EOF 423 | 424 | -------------------------------------------------------------------------------- /src/init/acpi.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; Pure64 -- a 64-bit OS/software loader written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; INIT ACPI 6 | ; 7 | ; Comments reference the following document: 8 | ; Advanced Configuration and Power Interface (ACPI) Specification Release 6.5 9 | ; https://uefi.org/sites/default/files/resources/ACPI_Spec_6_5_Aug29.pdf 10 | ; ============================================================================= 11 | 12 | 13 | init_acpi: 14 | mov rbx, 'RSD PTR ' ; This in the Signature for the ACPI Structure Table (0x2052545020445352) 15 | mov al, [p_BootMode] ; Check how the system was booted 16 | cmp al, 'U' ; UEFI? 17 | je foundACPIfromUEFI ; If so, jump - otherwise fall thru for BIOS 18 | 19 | ; Find the ACPI RSDP Structure on a BIOS system 20 | ; It's supposed to be somewhere in the first MiB of memory but some systems don't adhere to that 21 | mov esi, 0x00000007 ; Start looking for the Root System Description Pointer Structure 22 | searchingforACPI: 23 | sub esi, 0x7 24 | lodsq ; Load a quad word from RSI and store in RAX, then increment RSI by 8 25 | cmp rax, rbx ; Verify the Signature 26 | je foundACPI 27 | cmp esi, 0xFFFFFFF8 ; Keep looking until we get here 28 | ja noACPI ; ACPI tables couldn't be found, fail 29 | jmp searchingforACPI 30 | 31 | ; Find the ACPI RSDP Structure on a UEFI system 32 | foundACPIfromUEFI: 33 | mov rsi, [0x400830] ; TODO This should be passed properly 34 | lodsq ; Signature 35 | cmp rax, rbx ; Verify the Signature 36 | jne noACPI ; If it isn't a match then fail 37 | 38 | ; Parse the Root System Description Pointer (RSDP) Structure (5.2.5.3) 39 | foundACPI: ; Found a Pointer Structure, verify the checksum 40 | push rsi ; Save the RSDP location - currently pointing to the checksum 41 | push rbx 42 | xor ebx, ebx 43 | mov ecx, 20 ; As per the spec only the first 20 bytes matter 44 | sub esi, 8 ; Bytes 0 thru 19 must sum to zero 45 | nextchecksum: 46 | lodsb ; Get a byte 47 | add bl, al ; Add it to the running total 48 | dec cl 49 | jnz nextchecksum ; 'dec' will set the zero flag 50 | mov al, bl ; Save the value to AL before RBX gets popped 51 | pop rbx 52 | pop rsi ; Restore the RSDP location 53 | cmp al, 0 ; Verify the checksum is zero 54 | jne searchingforACPI ; Checksum didn't check out? Keep looking for a valid record 55 | 56 | lodsb ; Checksum 57 | lodsd ; OEMID (First 4 bytes) 58 | lodsw ; OEMID (Last 2 bytes) 59 | lodsb ; Revision (0 is v1.0, 1 is v2.0, 2 is v3.0, etc) 60 | cmp al, 0 61 | je foundACPIv1 ; If AL is 0 then the system is using ACPI v1.0 62 | jmp foundACPIv2 ; Otherwise it is v2.0 or higher 63 | 64 | foundACPIv1: ; Root System Description Table (RSDT) 65 | xor eax, eax 66 | lodsd ; RsdtAddress - 32 bit physical address of the RSDT (Offset 16) 67 | mov rsi, rax ; RSI now points to the RSDT 68 | lodsd ; Load Signature 69 | cmp eax, 'RSDT' ; Make sure the signature is valid 70 | jne novalidacpi ; Not the same? Bail out 71 | sub rsi, 4 72 | mov [p_ACPITableAddress], rsi ; Save the RSDT Table Address 73 | add rsi, 4 74 | xor eax, eax 75 | lodsd ; Length 76 | add rsi, 28 ; Skip to the Entry offset 77 | sub eax, 36 ; EAX holds the table size. Subtract the preamble 78 | shr eax, 2 ; Divide by 4 79 | mov rdx, rax ; RDX is the entry count 80 | xor ecx, ecx 81 | foundACPIv1_nextentry: 82 | lodsd ; Load a 32-bit Entry address 83 | push rax ; Push it to the stack as a 64-bit value 84 | add ecx, 1 85 | cmp ecx, edx 86 | je findACPITables 87 | jmp foundACPIv1_nextentry 88 | 89 | foundACPIv2: ; Extended System Description Table (XSDT) 90 | lodsd ; RsdtAddress - 32 bit physical address of the RSDT (Offset 16) 91 | lodsd ; Length 92 | lodsq ; XsdtAddress - 64 bit physical address of the XSDT (Offset 24). 93 | mov rsi, rax ; RSI now points to the XSDT 94 | lodsd ; Load Signature 95 | cmp eax, 'XSDT' ; Make sure the signature is valid 96 | jne novalidacpi ; Not the same? Bail out 97 | sub rsi, 4 98 | mov [p_ACPITableAddress], rsi ; Save the XSDT Table Address 99 | add rsi, 4 100 | xor eax, eax 101 | lodsd ; Length 102 | add rsi, 28 ; Skip to the start of the Entries (offset 36) 103 | sub eax, 36 ; EAX holds the table size. Subtract the preamble 104 | shr eax, 3 ; Divide by 8 105 | mov rdx, rax ; RDX is the entry count 106 | xor ecx, ecx 107 | foundACPIv2_nextentry: 108 | lodsq ; Load a 64-bit Entry address 109 | push rax ; Push it to the stack 110 | add ecx, 1 111 | cmp ecx, edx 112 | jne foundACPIv2_nextentry 113 | 114 | findACPITables: 115 | xor ecx, ecx 116 | nextACPITable: 117 | cmp ecx, edx ; Compare current count to entry count 118 | je init_smp_acpi_done 119 | pop rsi ; Pop an Entry address from the stack 120 | lodsd 121 | add ecx, 1 122 | mov ebx, 'APIC' ; Signature for the Multiple APIC Description Table 123 | cmp eax, ebx 124 | je foundAPICTable 125 | mov ebx, 'HPET' ; Signature for the HPET Description Table 126 | cmp eax, ebx 127 | je foundHPETTable 128 | mov ebx, 'MCFG' ; Signature for the PCIe Enhanced Configuration Mechanism 129 | cmp eax, ebx 130 | je foundMCFGTable 131 | mov ebx, 'FACP' ; Signature for the Fixed ACPI Description Table 132 | cmp eax, ebx 133 | je foundFADTTable 134 | jmp nextACPITable 135 | 136 | foundAPICTable: 137 | call parseAPICTable 138 | jmp nextACPITable 139 | 140 | foundHPETTable: 141 | call parseHPETTable 142 | jmp nextACPITable 143 | 144 | foundMCFGTable: 145 | call parseMCFGTable 146 | jmp nextACPITable 147 | 148 | foundFADTTable: 149 | call parseFADTTable 150 | jmp nextACPITable 151 | 152 | init_smp_acpi_done: 153 | ret 154 | 155 | noACPI: 156 | novalidacpi: 157 | ; Set screen to Teal 158 | mov rdi, [0x00005F00] ; Frame buffer base 159 | mov rcx, [0x00005F08] ; Frame buffer size 160 | shr rcx, 2 ; Quick divide by 4 161 | mov eax, 0x0000FFFF ; 0x00RRGGBB 162 | rep stosd 163 | jmp $ 164 | 165 | 166 | ; ----------------------------------------------------------------------------- 167 | ; 5.2.12 Multiple APIC Description Table (MADT) 168 | ; Chapter 5.2.12 169 | parseAPICTable: 170 | push rcx 171 | push rdx 172 | 173 | lodsd ; Length of MADT in bytes 174 | mov ecx, eax ; Store the length in ECX 175 | xor ebx, ebx ; EBX is the counter 176 | lodsb ; Revision 177 | lodsb ; Checksum 178 | lodsd ; OEMID (First 4 bytes) 179 | lodsw ; OEMID (Last 2 bytes) 180 | lodsq ; OEM Table ID 181 | lodsd ; OEM Revision 182 | lodsd ; Creator ID 183 | lodsd ; Creator Revision 184 | lodsd ; Local APIC Address (This should match what was pulled already via the MSR) 185 | lodsd ; Flags (1 = Dual 8259 Legacy PICs Installed) 186 | add ebx, 44 187 | mov rdi, 0x0000000000005100 ; Valid CPU IDs 188 | 189 | readAPICstructures: 190 | cmp ebx, ecx 191 | jae parseAPICTable_done 192 | lodsb ; APIC Structure Type 193 | cmp al, 0x00 ; Processor Local APIC 194 | je APICapic 195 | cmp al, 0x01 ; I/O APIC 196 | je APICioapic 197 | cmp al, 0x02 ; Interrupt Source Override 198 | je APICinterruptsourceoverride 199 | ; cmp al, 0x03 ; Non-maskable Interrupt Source (NMI) 200 | ; je APICnmi 201 | ; cmp al, 0x04 ; Local APIC NMI 202 | ; je APIClocalapicnmi 203 | ; cmp al, 0x05 ; Local APIC Address Override 204 | ; je APICaddressoverride 205 | ; cmp al, 0x06 ; I/O SAPIC Structure 206 | ; je APICiosapic 207 | ; cmp al, 0x07 ; Local SAPIC Structure 208 | ; je APIClocalsapic 209 | ; cmp al, 0x08 ; Platform Interrupt Source Structure 210 | ; je APICplatformint 211 | ; cmp al, 0x09 ; Processor Local x2APIC 212 | ; je APICx2apic 213 | ; cmp al, 0x0A ; Local x2APIC NMI 214 | ; je APICx2nmi 215 | 216 | jmp APICignore 217 | 218 | ; Processor Local APIC Structure - 5.2.12.2 219 | APICapic: ; Entry Type 0 220 | xor eax, eax 221 | xor edx, edx 222 | lodsb ; Length (will be set to 8) 223 | add ebx, eax 224 | lodsb ; ACPI Processor ID 225 | lodsb ; APIC ID 226 | xchg eax, edx ; Save the APIC ID to EDX 227 | lodsd ; Flags (Bit 0 set if enabled/usable) 228 | bt eax, 0 ; Test to see if usable 229 | jnc readAPICstructures ; Read the next structure if CPU not usable 230 | inc word [p_cpu_detected] 231 | xchg eax, edx ; Restore the APIC ID back to EAX 232 | stosb ; Store the 8-bit APIC ID 233 | jmp readAPICstructures ; Read the next structure 234 | 235 | ; I/O APIC Structure - 5.2.12.3 236 | APICioapic: ; Entry Type 1 237 | xor eax, eax 238 | lodsb ; Length (will be set to 12) 239 | add ebx, eax 240 | push rdi 241 | push rcx 242 | mov rdi, IM_IOAPICAddress ; Copy this data directly to the InfoMap 243 | xor ecx, ecx 244 | mov cl, [p_IOAPICCount] 245 | shl cx, 4 ; Quick multiply by 16 246 | add rdi, rcx 247 | pop rcx 248 | xor eax, eax 249 | lodsb ; IO APIC ID 250 | stosd 251 | lodsb ; Reserved 252 | lodsd ; I/O APIC Address 253 | stosd 254 | lodsd ; Global System Interrupt Base 255 | stosd 256 | pop rdi 257 | inc byte [p_IOAPICCount] 258 | jmp readAPICstructures ; Read the next structure 259 | 260 | ; Interrupt Source Override Structure - 5.2.12.5 261 | APICinterruptsourceoverride: ; Entry Type 2 262 | xor eax, eax 263 | lodsb ; Length (will be set to 10) 264 | add ebx, eax 265 | push rdi 266 | push rcx 267 | mov rdi, IM_IOAPICIntSource ; Copy this data directly to the InfoMap 268 | xor ecx, ecx 269 | mov cl, [p_IOAPICIntSourceC] 270 | shl cx, 3 ; Quick multiply by 8 271 | add rdi, rcx 272 | lodsb ; Bus Source 273 | stosb 274 | lodsb ; IRQ Source 275 | stosb 276 | lodsd ; Global System Interrupt 277 | stosd 278 | lodsw ; Flags - bit 1 Low(1)/High(0), Bit 3 Level(1)/Edge(0) 279 | stosw 280 | pop rcx 281 | pop rdi 282 | inc byte [p_IOAPICIntSourceC] 283 | jmp readAPICstructures ; Read the next structure 284 | 285 | ; Processor Local x2APIC Structure - 5.2.12.12 286 | ;APICx2apic: ; Entry Type 9 287 | ; xor eax, eax 288 | ; xor edx, edx 289 | ; lodsb ; Length (will be set to 16) 290 | ; add ebx, eax 291 | ; lodsw ; Reserved; Must be Zero 292 | ; lodsd 293 | ; xchg eax, edx ; Save the x2APIC ID to EDX 294 | ; lodsd ; Flags (Bit 0 set if enabled/usable) 295 | ; bt eax, 0 ; Test to see if usable 296 | ; jnc APICx2apicEnd ; Read the next structure if CPU not usable 297 | ; xchg eax, edx ; Restore the x2APIC ID back to EAX 298 | ; ; TODO - Save the ID's somewhere 299 | ;APICx2apicEnd: 300 | ; lodsd ; ACPI Processor UID 301 | ; jmp readAPICstructures ; Read the next structure 302 | 303 | APICignore: 304 | xor eax, eax 305 | lodsb ; We have a type that we ignore, read the next byte 306 | add ebx, eax 307 | add rsi, rax 308 | sub rsi, 2 ; For the two bytes just read 309 | jmp readAPICstructures ; Read the next structure 310 | 311 | parseAPICTable_done: 312 | pop rdx 313 | pop rcx 314 | ret 315 | ; ----------------------------------------------------------------------------- 316 | 317 | 318 | ; ----------------------------------------------------------------------------- 319 | ; High Precision Event Timer (HPET) 320 | ; http://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/software-developers-hpet-spec-1-0a.pdf 321 | parseHPETTable: 322 | lodsd ; Length of HPET in bytes 323 | lodsb ; Revision 324 | lodsb ; Checksum 325 | lodsd ; OEMID (First 4 bytes) 326 | lodsw ; OEMID (Last 2 bytes) 327 | lodsq ; OEM Table ID 328 | lodsd ; OEM Revision 329 | lodsd ; Creator ID 330 | lodsd ; Creator Revision 331 | 332 | lodsb ; Hardware Revision ID 333 | lodsb ; # of Comparators (5:0), COUNT_SIZE_CAP (6), Legacy IRQ (7) 334 | lodsw ; PCI Vendor ID 335 | lodsd ; Generic Address Structure 336 | lodsq ; Base Address Value 337 | mov [p_HPET_Address], rax ; Save the Address of the HPET 338 | lodsb ; HPET Number 339 | lodsw ; Main Counter Minimum 340 | mov [p_HPET_CounterMin], ax ; Save the Counter Minimum 341 | lodsb ; Page Protection And OEM Attribute 342 | ret 343 | ; ----------------------------------------------------------------------------- 344 | 345 | 346 | ; ----------------------------------------------------------------------------- 347 | ; PCI Express Memory-mapped Configuration (MCFG) 348 | ; Locked behind a paywall - Search Google for "pcie specification pdf" 349 | parseMCFGTable: 350 | push rdi 351 | push rcx 352 | xor eax, eax 353 | xor ecx, ecx 354 | mov cx, [p_PCIECount] 355 | shl ecx, 4 356 | mov rdi, IM_PCIE 357 | add rdi, rcx 358 | lodsd ; Length of MCFG in bytes 359 | sub eax, 44 ; Subtract the size of the table header 360 | shr eax, 4 ; Quick divide by 16 361 | mov ecx, eax ; ECX now stores the number of 16-byte records 362 | add word [p_PCIECount], cx 363 | lodsb ; Revision 364 | lodsb ; Checksum 365 | lodsd ; OEMID (First 4 bytes) 366 | lodsw ; OEMID (Last 2 bytes) 367 | lodsq ; OEM Table ID 368 | lodsd ; OEM Revision 369 | lodsd ; Creator ID 370 | lodsd ; Creator Revision 371 | lodsq ; Reserved 372 | 373 | ; Loop through each entry 374 | parseMCFGTable_next: 375 | lodsq ; Base address of enhanced configuration mechanism 376 | stosq 377 | lodsw ; PCI Segment Group Number 378 | stosw 379 | lodsb ; Start PCI bus number decoded by this host bridge 380 | stosb 381 | lodsb ; End PCI bus number decoded by this host bridge 382 | stosb 383 | lodsd ; Reserved 384 | stosd 385 | sub ecx, 1 386 | jnz parseMCFGTable_next 387 | xor eax, eax 388 | not rax ; 0xFFFFFFFFFFFFFFFF 389 | stosq ; Mark the end of the table 390 | stosq 391 | 392 | pop rcx 393 | pop rdi 394 | ret 395 | ; ----------------------------------------------------------------------------- 396 | 397 | 398 | ; ----------------------------------------------------------------------------- 399 | ; Fixed ACPI Description Table (FADT) 400 | ; Chapter 5.2.9 401 | parseFADTTable: 402 | ; At this point RSI points to offset 4 for the FADT 403 | sub rsi, 4 ; Set RSI back to start to make offsets easier below 404 | 405 | ; Gather IAPC_BOOT_ARCH 406 | mov eax, [rsi+10] ; Check start of OEMID 407 | cmp eax, 0x48434F42 ; Is it "BOCH"? 408 | je parseFADTTable_end ; If so, bail out 409 | mov ax, [rsi+109] ; IAPC_BOOT_ARCH (IA-PC Boot Architecture Flags - 5.2.9.3) 410 | mov [p_IAPC_BOOT_ARCH], ax ; Save the IAPC_BOOT_ARCH word 411 | 412 | ; add rsi, 116 ; RESET_REG (Generic Address Structure - 5.2.3.2) 413 | ; lodsb ; Address Space ID (0x00 = Memory, 0x01 = I/O, 0x02 = PCI) 414 | ; lodsb ; Register Width 415 | ; lodsb ; Register Offset 416 | ; lodsb ; Access Size 417 | ; lodsq ; Address 418 | ; lodsb ; RESET_VALUE 419 | 420 | ; add rsi, 36 421 | ; lodsd ; DSDT 422 | ; add rsi, 20 423 | ; lodsd ; PM1a_CNT_BLK 424 | 425 | parseFADTTable_end: 426 | ret 427 | ; ----------------------------------------------------------------------------- 428 | 429 | 430 | ; ============================================================================= 431 | ; EOF 432 | -------------------------------------------------------------------------------- /src/init/cpu.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; Pure64 -- a 64-bit OS/software loader written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; INIT CPU - This code is called by all activated CPU cores in the system 6 | ; ============================================================================= 7 | 8 | 9 | init_cpu: 10 | 11 | ; Disable Cache 12 | mov rax, cr0 13 | btr rax, 29 ; Clear No Write Thru (Bit 29) 14 | bts rax, 30 ; Set Cache Disable (Bit 30) 15 | mov cr0, rax 16 | 17 | ; Flush Cache 18 | wbinvd 19 | 20 | ; Flush TLB 21 | mov rax, cr3 22 | mov cr3, rax 23 | 24 | ; Read the MTRR Cap (IA32_MTRRCAP) Register - 64-bit 25 | ; SMRR (bit 11) — SMRR interface supported 26 | ; WC (bit 10) — Write-combining memory type supported 27 | ; FIX (bit 8) — Fixed range registers supported 28 | ; VCNT (bits 7:0)— Number of variable range registers 29 | ; mov ecx, IA32_MTRRCAP 30 | ; rdmsr ; Returns the 64-bit value in EDX:EAX 31 | 32 | ; Read/write the MTRR Default Type (IA32_MTRR_DEF_TYPE) Register - 64-bit 33 | ; E (bit 11) — MTRR enable/disable 34 | ; FE (bit 10) — Fixed-range MTRRs enable/disable 35 | ; Type (bits 7:0)— Default memory type 36 | ; mov ecx, IA32_MTRR_DEF_TYPE 37 | ; rdmsr 38 | ; btc eax, 11 ; Disable MTRR 39 | ; btc eax, 10 ; Disable Fixed-range MTRRs 40 | ; wrmsr ; Write the 64-bit value from EDX:EAX 41 | 42 | ; Configure Page Attribute Table (IA32_PAT) 43 | ; 44 | ; MTRR Memory Types 45 | ; 0x00 - Uncacheable (UC) 46 | ; 0x01 - Write Combining (WC) 47 | ; 0x04 - Write Through (WT) 48 | ; 0x05 - Write Protected (WP) 49 | ; 0x06 - Write Back (WB) 50 | ; 0x07 - Uncached (UC-) 51 | ; 52 | ; On system power-up/reset the PAT is configured as WB, WT, UC-, UC, WB, WT, UC-, UC for entries 0-7 53 | ; PA0-3 are left at system default 54 | ; PA4-7 are reconfigured to enable WP and WC 55 | ; The following table is used to set a mapped page to a specific PAT 56 | ; PAT = Bit 12, PCD = Bit 4, PWT = Bit 3 57 | ; PAT PCD PWT PAT Entry Type 58 | ; 0 0 0 PAT0 WB 59 | ; 0 0 1 PAT1 WT 60 | ; 0 1 0 PAT2 UC- 61 | ; 0 1 1 PAT3 UC 62 | ; 1 0 0 PAT4 WP 63 | ; 1 0 1 PAT5 WC 64 | ; 1 1 0 PAT6 UC 65 | ; 1 1 1 PAT7 UC 66 | mov edx, 0x00000105 ; PA7 UC (00), PA6 UC (00), PA5 WC (01), PA4 WP (05) 67 | mov eax, 0x00070406 ; PA3 UC (00), PA2 UC- (07), PA1 WT (04), PA0 WB (06) 68 | mov ecx, IA32_PAT 69 | wrmsr 70 | 71 | ; Enable MTRRs 72 | ; mov ecx, IA32_MTRR_DEF_TYPE 73 | ; rdmsr 74 | ; bts eax, 11 ; Set MTRR Enable (Bit 11), Only enables Variable Range MTRR's 75 | ; wrmsr 76 | 77 | ; Flush TLB 78 | mov rax, cr3 79 | mov cr3, rax 80 | 81 | ; Flush Cache 82 | wbinvd 83 | 84 | ; Enable Cache 85 | mov rax, cr0 86 | btr rax, 29 ; Clear No Write Thru (Bit 29) 87 | btr rax, 30 ; Clear CD (Bit 30) 88 | mov cr0, rax 89 | 90 | ; Enable Floating Point 91 | mov rax, cr0 92 | bts rax, 1 ; Set Monitor co-processor (Bit 1) 93 | btr rax, 2 ; Clear Emulation (Bit 2) 94 | mov cr0, rax 95 | 96 | ; Enable SSE 97 | mov rax, cr4 98 | bts rax, 9 ; Set Operating System Support for FXSAVE and FXSTOR instructions (Bit 9) 99 | bts rax, 10 ; Set Operating System Support for Unmasked SIMD Floating-Point Exceptions (Bit 10) 100 | mov cr4, rax 101 | 102 | ; Enable Math Co-processor 103 | finit 104 | 105 | ; Enable AVX-1 and AVX-2 106 | mov eax, 1 ; CPUID Feature information 1 107 | cpuid ; Sets info in ECX and EDX 108 | bt ecx, 28 ; AVX-1 is supported if bit 28 is set in ECX 109 | ; AVX-2 is supported if bit 5 is set in EBX on CPUID (EAX=7, ECX=0) 110 | jnc avx_not_supported ; Skip activating AVX if not supported 111 | avx_supported: 112 | mov rax, cr4 113 | bts rax, 18 ; Enable OSXSAVE (Bit 18) 114 | mov cr4, rax 115 | xor ecx, ecx ; Set load XCR0 116 | xgetbv ; Load XCR0 register 117 | bts rax, 0 ; Set X87 enable (Bit 0) 118 | bts rax, 1 ; Set SSE enable (Bit 1) 119 | bts rax, 2 ; Set AVX enable (Bit 2) 120 | xsetbv ; Save XCR0 register 121 | avx_not_supported: 122 | 123 | ; Enable AVX-512 124 | mov eax, 7 ; CPUID Feature information 7 125 | xor ecx, ecx ; Extended Features 0 126 | cpuid ; Sets info in EBX, ECX, and EDX 127 | bt ebx, 16 ; AVX-512 is supported if bit 16 is set in EBX 128 | jnc avx512_not_supported 129 | avx512_supported: 130 | xor ecx, ecx ; Set load XCR0 131 | xgetbv ; Load XCR0 register 132 | bts rax, 5 ; Set OPMASK (Bit 5) 133 | bts rax, 6 ; Set ZMM_Hi256 (Bit 6) 134 | bts rax, 7 ; Set Hi16_ZMM (Bit 7) 135 | xsetbv ; Save XCR0 register 136 | avx512_not_supported: 137 | 138 | ; Enable and Configure Local APIC 139 | ; mov ecx, IA32_APIC_BASE 140 | ; rdmsr 141 | ; bts eax, 11 ; APIC Global Enable 142 | ; cmp byte [p_x2APIC], 1 143 | ; jne skip_x2APIC_enable 144 | ; bts eax, 10 ; Enable x2APIC mode 145 | ;skip_x2APIC_enable: 146 | ; wrmsr 147 | mov ecx, APIC_TPR 148 | mov eax, 0x00000020 149 | call apic_write ; Disable softint delivery 150 | mov ecx, APIC_LVT_TMR 151 | mov eax, 0x00010000 152 | call apic_write ; Disable timer interrupts 153 | mov ecx, APIC_LVT_PERF 154 | mov eax, 0x00010000 155 | call apic_write ; Disable performance counter interrupts 156 | mov ecx, APIC_LDR 157 | xor eax, eax 158 | call apic_write ; Set Logical Destination Register 159 | mov ecx, APIC_DFR 160 | not eax ; Set EAX to 0xFFFFFFFF; Bits 31-28 set for Flat Mode 161 | call apic_write ; Set Destination Format Register 162 | mov ecx, APIC_LVT_LINT0 163 | mov eax, 0x00008700 ; Bit 15 (1 = Level), Bits 10:8 for Ext 164 | call apic_write ; Enable normal external interrupts 165 | mov ecx, APIC_LVT_LINT1 166 | mov eax, 0x00000400 167 | call apic_write ; Enable normal NMI processing 168 | mov ecx, APIC_LVT_ERR 169 | mov eax, 0x00010000 170 | call apic_write ; Disable error interrupts 171 | mov ecx, APIC_SPURIOUS 172 | mov eax, 0x000001FF 173 | call apic_write ; Enable the APIC (bit 8) and set spurious vector to 0xFF 174 | 175 | lock inc word [p_cpu_activated] 176 | mov ecx, APIC_ID 177 | call apic_read ; APIC ID is stored in bits 31:24 178 | shr eax, 24 ; AL now holds the CPU's APIC ID (0 - 255) 179 | mov rdi, IM_ActivedCoreIDs ; The location where the activated cores set their record to 1 180 | add rdi, rax ; RDI points to InfoMap CPU area + APIC ID. ex 0x5E01 would be APIC ID 1 181 | mov al, 1 182 | stosb ; Store a 1 as the core is activated 183 | 184 | ret 185 | 186 | ; ----------------------------------------------------------------------------- 187 | ; apic_read -- Read from a register in the APIC 188 | ; IN: ECX = Register to read 189 | ; OUT: EAX = Register value 190 | ; All other registers preserved 191 | apic_read: 192 | mov rax, [p_LocalAPICAddress] 193 | mov eax, [rax + rcx] 194 | ret 195 | ; ----------------------------------------------------------------------------- 196 | 197 | 198 | ; ----------------------------------------------------------------------------- 199 | ; apic_write -- Write to a register in the APIC 200 | ; IN: ECX = Register to write 201 | ; EAX = Value to write 202 | ; OUT: All registers preserved 203 | apic_write: 204 | push rcx 205 | add rcx, [p_LocalAPICAddress] 206 | mov [rcx], eax 207 | pop rcx 208 | ret 209 | ; ----------------------------------------------------------------------------- 210 | 211 | 212 | ; APIC Register list 213 | ; 0x000 - 0x010 are Reserved 214 | APIC_ID equ 0x020 ; ID Register 215 | APIC_VER equ 0x030 ; Version Register 216 | ; 0x040 - 0x070 are Reserved 217 | APIC_TPR equ 0x080 ; Task Priority Register 218 | APIC_APR equ 0x090 ; Arbitration Priority Register 219 | APIC_PPR equ 0x0A0 ; Processor Priority Register 220 | APIC_EOI equ 0x0B0 ; End Of Interrupt 221 | APIC_RRD equ 0x0C0 ; Remote Read Register 222 | APIC_LDR equ 0x0D0 ; Logical Destination Register 223 | APIC_DFR equ 0x0E0 ; Destination Format Register 224 | APIC_SPURIOUS equ 0x0F0 ; Spurious Interrupt Vector Register 225 | APIC_ISR equ 0x100 ; In-Service Register (Starting Address) 226 | APIC_TMR equ 0x180 ; Trigger Mode Register (Starting Address) 227 | APIC_IRR equ 0x200 ; Interrupt Request Register (Starting Address) 228 | APIC_ESR equ 0x280 ; Error Status Register 229 | ; 0x290 - 0x2E0 are Reserved 230 | APIC_ICRL equ 0x300 ; Interrupt Command Register (low 32 bits) 231 | APIC_ICRH equ 0x310 ; Interrupt Command Register (high 32 bits) 232 | APIC_LVT_TMR equ 0x320 ; LVT Timer Register 233 | APIC_LVT_TSR equ 0x330 ; LVT Thermal Sensor Register 234 | APIC_LVT_PERF equ 0x340 ; LVT Performance Monitoring Counters Register 235 | APIC_LVT_LINT0 equ 0x350 ; LVT LINT0 Register 236 | APIC_LVT_LINT1 equ 0x360 ; LVT LINT1 Register 237 | APIC_LVT_ERR equ 0x370 ; LVT Error Register 238 | APIC_TMRINITCNT equ 0x380 ; Initial Count Register (for Timer) 239 | APIC_TMRCURRCNT equ 0x390 ; Current Count Register (for Timer) 240 | ; 0x3A0 - 0x3D0 are Reserved 241 | APIC_TMRDIV equ 0x3E0 ; Divide Configuration Register (for Timer) 242 | ; 0x3F0 is Reserved 243 | 244 | 245 | ; MSR List 246 | IA32_APIC_BASE equ 0x01B 247 | IA32_MTRRCAP equ 0x0FE 248 | IA32_MISC_ENABLE equ 0x1A0 249 | IA32_MTRR_PHYSBASE0 equ 0x200 250 | IA32_MTRR_PHYSMASK0 equ 0x201 251 | IA32_MTRR_PHYSBASE1 equ 0x202 252 | IA32_MTRR_PHYSMASK1 equ 0x203 253 | IA32_PAT equ 0x277 254 | IA32_MTRR_DEF_TYPE equ 0x2FF 255 | 256 | 257 | ; ============================================================================= 258 | ; EOF 259 | -------------------------------------------------------------------------------- /src/init/hpet.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; Pure64 -- a 64-bit OS/software loader written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; INIT HPET 6 | ; ============================================================================= 7 | 8 | 9 | init_hpet: 10 | ; Verify there is a valid HPET address 11 | mov rax, [p_HPET_Address] 12 | jz os_hpet_init_error 13 | 14 | ; Verify the capabilities of HPET 15 | mov ecx, HPET_GEN_CAP 16 | call os_hpet_read 17 | mov rbx, rax ; Save results for # of timers 18 | shr ebx, 8 ; Bits 12:8 contain the # of timers 19 | and ebx, 11111b ; Save only the lower 5 bits 20 | inc bl ; Increment the number of timers by 1 21 | mov [p_HPET_Timers], bl ; Save the # of HPET timers 22 | shr rax, 32 ; EAX contains the tick period in femtoseconds 23 | 24 | ; Verify the Counter Clock Period is valid 25 | cmp eax, 0x05F5E100 ; 100,000,000 femtoseconds is the maximum 26 | ja os_hpet_init_error 27 | cmp eax, 0 ; The Period has to be greater than 1 femtosecond 28 | je os_hpet_init_error 29 | 30 | ; Calculate the HPET frequency 31 | mov rbx, rax ; Move Counter Clock Period to RBX 32 | xor rdx, rdx 33 | mov rax, 1000000000000000 ; femotoseconds per second 34 | div rbx ; RDX:RAX / RBX 35 | mov [p_HPET_Frequency], eax ; Save the HPET frequency 36 | 37 | ; Disable interrupts on all timers 38 | xor ebx, ebx 39 | mov bl, [p_HPET_Timers] 40 | mov ecx, 0xE0 ; HPET_TIMER_0_CONF - 0x20 41 | os_hpet_init_disable_int: 42 | add ecx, 0x20 43 | call os_hpet_read 44 | btc ax, 2 45 | btc ax, 3 46 | call os_hpet_write 47 | dec bl 48 | jnz os_hpet_init_disable_int 49 | 50 | ; Clear the main counter before it is enabled 51 | mov ecx, HPET_MAIN_COUNTER 52 | xor eax, eax 53 | call os_hpet_write 54 | 55 | ; Enable HPET main counter (bit 0) 56 | mov eax, 1 ; Bit 0 is set 57 | mov ecx, HPET_GEN_CONF 58 | call os_hpet_write 59 | 60 | os_hpet_init_error: 61 | ret 62 | ; ----------------------------------------------------------------------------- 63 | 64 | 65 | ; ----------------------------------------------------------------------------- 66 | ; os_hpet_read -- Read from a register in the High Precision Event Timer 67 | ; IN: ECX = Register to read 68 | ; OUT: RAX = Register value 69 | ; All other registers preserved 70 | os_hpet_read: 71 | mov rax, [p_HPET_Address] 72 | mov rax, [rax + rcx] 73 | ret 74 | ; ----------------------------------------------------------------------------- 75 | 76 | 77 | ; ----------------------------------------------------------------------------- 78 | ; os_hpet_write -- Write to a register in the High Precision Event Timer 79 | ; IN: ECX = Register to write 80 | ; RAX = Value to write 81 | ; OUT: All registers preserved 82 | os_hpet_write: 83 | push rcx 84 | add rcx, [p_HPET_Address] 85 | mov [rcx], rax 86 | pop rcx 87 | ret 88 | ; ----------------------------------------------------------------------------- 89 | 90 | 91 | ; ----------------------------------------------------------------------------- 92 | ; os_hpet_delay -- Delay by X microseconds 93 | ; IN: RAX = Time microseconds 94 | ; OUT: All registers preserved 95 | ; Note: There are 1,000,000 microseconds in a second 96 | ; There are 1,000 milliseconds in a second 97 | os_hpet_delay: 98 | push rdx 99 | push rcx 100 | push rbx 101 | push rax 102 | 103 | mov rbx, rax ; Save delay to RBX 104 | xor edx, edx 105 | xor ecx, ecx 106 | call os_hpet_read ; Get HPET General Capabilities and ID Register 107 | shr rax, 32 108 | mov rcx, rax ; RCX = RAX >> 32 (timer period in femtoseconds) 109 | mov rax, 1000000000 110 | div rcx ; Divide 1000000000 (RDX:RAX) / RCX (converting from period in femtoseconds to frequency in MHz) 111 | mul rbx ; RAX *= RBX, should get number of HPET cycles to wait, save result in RBX 112 | mov rbx, rax 113 | mov ecx, HPET_MAIN_COUNTER 114 | call os_hpet_read ; Get HPET counter in RAX 115 | add rbx, rax ; RBX += RAX Until when to wait 116 | os_hpet_delay_loop: ; Stay in this loop until the HPET timer reaches the expected value 117 | mov ecx, HPET_MAIN_COUNTER 118 | call os_hpet_read ; Get HPET counter in RAX 119 | cmp rax, rbx ; If RAX >= RBX then jump to end, otherwise jump to loop 120 | jae os_hpet_delay_end 121 | jmp os_hpet_delay_loop 122 | os_hpet_delay_end: 123 | 124 | pop rax 125 | pop rbx 126 | pop rcx 127 | pop rdx 128 | ret 129 | ; ----------------------------------------------------------------------------- 130 | 131 | 132 | ; Register list (64-bits wide) 133 | HPET_GEN_CAP equ 0x000 ; COUNTER_CLK_PERIOD (63:32), LEG_RT_CAP (15), COUNT_SIZE_CAP (13), NUM_TIM_CAP (12:8) 134 | ; 0x008 - 0x00F are Reserved 135 | HPET_GEN_CONF equ 0x010 ; LEG_RT_CNF (1), ENABLE_CNF (0) 136 | ; 0x018 - 0x01F are Reserved 137 | HPET_GEN_INT_STATUS equ 0x020 138 | ; 0x028 - 0x0EF are Reserved 139 | HPET_MAIN_COUNTER equ 0x0F0 140 | ; 0x0F8 - 0x0FF are Reserved 141 | HPET_TIMER_0_CONF equ 0x100 142 | HPET_TIMER_0_COMP equ 0x108 143 | HPET_TIMER_0_INT equ 0x110 144 | ; 0x118 - 0x11F are Reserved 145 | HPET_TIMER_1_CONF equ 0x120 146 | HPET_TIMER_1_COMP equ 0x128 147 | HPET_TIMER_1_INT equ 0x130 148 | ; 0x138 - 0x13F are Reserved 149 | HPET_TIMER_2_CONF equ 0x140 150 | HPET_TIMER_2_COMP equ 0x148 151 | HPET_TIMER_2_INT equ 0x150 152 | ; 0x158 - 0x15F are Reserved 153 | ; 0x160 - 0x3FF are Reserved for Timers 3-31 154 | 155 | 156 | ; ============================================================================= 157 | ; EOF -------------------------------------------------------------------------------- /src/init/smp.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; Pure64 -- a 64-bit OS/software loader written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; INIT SMP 6 | ; ============================================================================= 7 | 8 | 9 | init_smp: 10 | ; Check if we want the AP's to be enabled.. if not then skip to end 11 | cmp byte [cfg_smpinit], 1 ; Check if SMP should be enabled 12 | jne noMP ; If not then skip SMP init 13 | 14 | ; Start the AP's one by one 15 | xor eax, eax 16 | xor edx, edx 17 | mov rsi, [p_LocalAPICAddress] 18 | mov eax, [rsi+0x20] ; Add the offset for the APIC ID location 19 | shr rax, 24 ; APIC ID is stored in bits 31:24 20 | mov dl, al ; Store BSP APIC ID in DL 21 | 22 | mov esi, IM_DetectedCoreIDs 23 | xor eax, eax 24 | xor ecx, ecx 25 | mov cx, [p_cpu_detected] 26 | smp_send_INIT: 27 | cmp cx, 0 28 | je smp_send_INIT_done 29 | lodsb 30 | 31 | cmp al, dl ; Is it the BSP? 32 | je smp_send_INIT_skipcore 33 | 34 | ; Send 'INIT' IPI to APIC ID in AL 35 | mov rdi, [p_LocalAPICAddress] 36 | shl eax, 24 37 | mov dword [rdi+0x310], eax ; Interrupt Command Register (ICR); bits 63-32 38 | mov eax, 0x00004500 39 | mov dword [rdi+0x300], eax ; Interrupt Command Register (ICR); bits 31-0 40 | smp_send_INIT_verify: 41 | mov eax, [rdi+0x300] ; Interrupt Command Register (ICR); bits 31-0 42 | bt eax, 12 ; Verify that the command completed 43 | jc smp_send_INIT_verify 44 | 45 | smp_send_INIT_skipcore: 46 | dec cl 47 | jmp smp_send_INIT 48 | 49 | smp_send_INIT_done: 50 | 51 | ; Wait 500 microseconds 52 | mov eax, 500 53 | call os_hpet_delay 54 | 55 | mov esi, IM_DetectedCoreIDs 56 | xor ecx, ecx 57 | mov cx, [p_cpu_detected] 58 | smp_send_SIPI: 59 | cmp cx, 0 60 | je smp_send_SIPI_done 61 | lodsb 62 | 63 | cmp al, dl ; Is it the BSP? 64 | je smp_send_SIPI_skipcore 65 | 66 | ; Send 'Startup' IPI to destination using vector 0x08 to specify entry-point is at the memory-address 0x00008000 67 | mov rdi, [p_LocalAPICAddress] 68 | shl eax, 24 69 | mov dword [rdi+0x310], eax ; Interrupt Command Register (ICR); bits 63-32 70 | mov eax, 0x00004608 ; Vector 0x08 71 | mov dword [rdi+0x300], eax ; Interrupt Command Register (ICR); bits 31-0 72 | smp_send_SIPI_verify: 73 | mov eax, [rdi+0x300] ; Interrupt Command Register (ICR); bits 31-0 74 | bt eax, 12 ; Verify that the command completed 75 | jc smp_send_SIPI_verify 76 | 77 | smp_send_SIPI_skipcore: 78 | dec cl 79 | jmp smp_send_SIPI 80 | 81 | smp_send_SIPI_done: 82 | 83 | ; Wait 10000 microseconds for the AP's to finish 84 | mov eax, 10000 85 | call os_hpet_delay 86 | 87 | noMP: 88 | ; Gather and store the APIC ID of the BSP 89 | xor eax, eax 90 | mov rsi, [p_LocalAPICAddress] 91 | add rsi, 0x20 ; Add the offset for the APIC ID location 92 | lodsd ; APIC ID is stored in bits 31:24 93 | shr rax, 24 ; AL now holds the CPU's APIC ID (0 - 255) 94 | mov [p_BSP], eax ; Store the BSP APIC ID 95 | 96 | ; Calculate base speed of CPU 97 | cpuid 98 | xor edx, edx 99 | xor eax, eax 100 | rdtsc 101 | push rax 102 | mov rax, 1024 103 | call os_hpet_delay 104 | rdtsc 105 | pop rdx 106 | sub rax, rdx 107 | xor edx, edx 108 | mov rcx, 1024 109 | div rcx 110 | mov [p_cpu_speed], ax 111 | 112 | ret 113 | 114 | 115 | ; ============================================================================= 116 | ; EOF 117 | -------------------------------------------------------------------------------- /src/init/smp_ap.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; Pure64 -- a 64-bit OS/software loader written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; INIT SMP AP 6 | ; ============================================================================= 7 | 8 | 9 | BITS 16 10 | 11 | init_smp_ap: 12 | 13 | ; Check boot method of BSP 14 | cmp byte [p_BootMode], 'U' 15 | je skip_a20_ap ; If UEFI, then skip A20 code 16 | 17 | ; Enable the A20 gate 18 | set_A20_ap: 19 | in al, 0x64 20 | test al, 0x02 21 | jnz set_A20_ap 22 | mov al, 0xD1 23 | out 0x64, al 24 | check_A20_ap: 25 | in al, 0x64 26 | test al, 0x02 27 | jnz check_A20_ap 28 | mov al, 0xDF 29 | out 0x60, al 30 | skip_a20_ap: 31 | 32 | ; At this point we are done with real mode and BIOS interrupts. Jump to 32-bit mode. 33 | lgdt [cs:GDTR32] ; Load GDT register 34 | 35 | mov eax, cr0 ; Switch to 32-bit protected mode 36 | or al, 1 37 | mov cr0, eax 38 | 39 | jmp 8:startap32 40 | 41 | align 16 42 | 43 | 44 | ; ============================================================================= 45 | ; 32-bit mode 46 | BITS 32 47 | 48 | startap32: 49 | mov eax, 16 ; Load 4 GB data descriptor 50 | mov ds, ax ; to all data segment registers 51 | mov es, ax 52 | mov fs, ax 53 | mov gs, ax 54 | mov ss, ax 55 | xor eax, eax 56 | xor ebx, ebx 57 | xor ecx, ecx 58 | xor edx, edx 59 | xor esi, esi 60 | xor edi, edi 61 | xor ebp, ebp 62 | mov esp, 0x7000 ; Set a known free location for the temporary stack (shared by all APs) 63 | 64 | ; Load the GDT 65 | lgdt [GDTR64] 66 | 67 | ; Enable extended properties 68 | mov eax, cr4 69 | or eax, 0x0000000B0 ; PGE (Bit 7), PAE (Bit 5), and PSE (Bit 4) 70 | mov cr4, eax 71 | 72 | ; Point cr3 at PML4 73 | mov eax, 0x00002008 ; Write-thru (Bit 3) 74 | mov cr3, eax 75 | 76 | ; Enable long mode and SYSCALL/SYSRET 77 | mov ecx, 0xC0000080 ; EFER MSR number 78 | rdmsr ; Read EFER 79 | or eax, 0x00000101 ; LME (Bit 8) 80 | wrmsr ; Write EFER 81 | 82 | ; Enable paging to activate long mode 83 | mov eax, cr0 84 | or eax, 0x80000000 ; PG (Bit 31) 85 | mov cr0, eax 86 | 87 | ; Make the jump directly from 16-bit real mode to 64-bit long mode 88 | jmp SYS64_CODE_SEL:startap64 89 | 90 | align 16 91 | 92 | 93 | ; ============================================================================= 94 | ; 64-bit mode 95 | BITS 64 96 | 97 | startap64: 98 | xor eax, eax ; aka r0 99 | xor ebx, ebx ; aka r3 100 | xor ecx, ecx ; aka r1 101 | xor edx, edx ; aka r2 102 | xor esi, esi ; aka r6 103 | xor edi, edi ; aka r7 104 | xor ebp, ebp ; aka r5 105 | xor esp, esp ; aka r4 106 | xor r8, r8 107 | xor r9, r9 108 | xor r10, r10 109 | xor r11, r11 110 | xor r12, r12 111 | xor r13, r13 112 | xor r14, r14 113 | xor r15, r15 114 | 115 | mov ax, 0x10 ; TODO Is this needed? 116 | mov ds, ax ; Clear the legacy segment registers 117 | mov es, ax 118 | mov ss, ax 119 | mov fs, ax 120 | mov gs, ax 121 | 122 | ; Reset the stack. Each CPU gets a 1024-byte unique stack location 123 | mov rsi, [p_LocalAPICAddress] ; We would call p_smp_get_id here but the stack is not ... 124 | add rsi, 0x20 ; ... yet defined. It is safer to find the value directly. 125 | lodsd ; Load a 32-bit value. We only want the high 8 bits 126 | shr rax, 24 ; Shift to the right and AL now holds the CPU's APIC ID 127 | shl rax, 10 ; shift left 10 bits for a 1024byte stack 128 | add rax, 0x0000000000090000 ; stacks decrement when you "push", start at 1024 bytes in 129 | mov rsp, rax ; Pure64 leaves 0x50000-0x9FFFF free so we use that 130 | 131 | lgdt [GDTR64] ; Load the GDT 132 | lidt [IDTR64] ; Load the IDT 133 | 134 | call init_cpu ; Setup CPU 135 | 136 | sti ; Activate interrupts for SMP 137 | jmp ap_sleep 138 | 139 | align 16 140 | 141 | ap_sleep: 142 | hlt ; Suspend CPU until an interrupt is received. opcode for hlt is 0xF4 143 | jmp ap_sleep ; just-in-case of an NMI 144 | 145 | 146 | ; ============================================================================= 147 | ; EOF 148 | -------------------------------------------------------------------------------- /src/interrupt.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; Pure64 -- a 64-bit OS/software loader written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; Interrupts 6 | ; ============================================================================= 7 | 8 | 9 | ; ----------------------------------------------------------------------------- 10 | ; Default exception handler 11 | exception_gate: 12 | exception_gate_halt: 13 | cli ; Disable interrupts 14 | hlt ; Halt the system 15 | jmp exception_gate_halt 16 | ; ----------------------------------------------------------------------------- 17 | 18 | 19 | ; ----------------------------------------------------------------------------- 20 | ; Default interrupt handler 21 | interrupt_gate: ; handler for all other interrupts 22 | iretq 23 | ; ----------------------------------------------------------------------------- 24 | 25 | 26 | ; ----------------------------------------------------------------------------- 27 | ; Floppy drive interrupt. IRQ 0x06, INT 0x26 28 | ; This IRQ runs when floppy drive reads from or writes to whole disk 29 | %ifdef BIOS 30 | align 16 31 | floppy_irq: 32 | push rdi 33 | push rbx 34 | push rax 35 | 36 | mov word [int_done], 1 37 | mov al, 0x20 ; Acknowledge the IRQ 38 | out 0x20, al 39 | 40 | pop rax 41 | pop rbx 42 | pop rdi 43 | iretq 44 | %endif 45 | ; ----------------------------------------------------------------------------- 46 | 47 | 48 | ; ----------------------------------------------------------------------------- 49 | ; Spurious interrupt. INT 0xFF 50 | align 16 51 | spurious: ; handler for spurious interrupts 52 | iretq 53 | ; ----------------------------------------------------------------------------- 54 | 55 | 56 | ; ----------------------------------------------------------------------------- 57 | ; CPU Exception Gates 58 | exception_gate_00: 59 | mov al, 0x00 60 | jmp exception_gate_main 61 | 62 | exception_gate_01: 63 | mov al, 0x01 64 | jmp exception_gate_main 65 | 66 | exception_gate_02: 67 | mov al, 0x02 68 | jmp exception_gate_main 69 | 70 | exception_gate_03: 71 | mov al, 0x03 72 | jmp exception_gate_main 73 | 74 | exception_gate_04: 75 | mov al, 0x04 76 | jmp exception_gate_main 77 | 78 | exception_gate_05: 79 | mov al, 0x05 80 | jmp exception_gate_main 81 | 82 | exception_gate_06: 83 | mov al, 0x06 84 | jmp exception_gate_main 85 | 86 | exception_gate_07: 87 | mov al, 0x07 88 | jmp exception_gate_main 89 | 90 | exception_gate_08: 91 | mov al, 0x08 92 | jmp exception_gate_main 93 | 94 | exception_gate_09: 95 | mov al, 0x09 96 | jmp exception_gate_main 97 | 98 | exception_gate_10: 99 | mov al, 0x0A 100 | jmp exception_gate_main 101 | 102 | exception_gate_11: 103 | mov al, 0x0B 104 | jmp exception_gate_main 105 | 106 | exception_gate_12: 107 | mov al, 0x0C 108 | jmp exception_gate_main 109 | 110 | exception_gate_13: 111 | mov al, 0x0D 112 | jmp exception_gate_main 113 | 114 | exception_gate_14: 115 | mov al, 0x0E 116 | jmp exception_gate_main 117 | 118 | exception_gate_15: 119 | mov al, 0x0F 120 | jmp exception_gate_main 121 | 122 | exception_gate_16: 123 | mov al, 0x10 124 | jmp exception_gate_main 125 | 126 | exception_gate_17: 127 | mov al, 0x11 128 | jmp exception_gate_main 129 | 130 | exception_gate_18: 131 | mov al, 0x12 132 | jmp exception_gate_main 133 | 134 | exception_gate_19: 135 | mov al, 0x13 136 | jmp exception_gate_main 137 | 138 | exception_gate_20: 139 | mov al, 0x14 140 | jmp exception_gate_main 141 | 142 | exception_gate_21: 143 | mov al, 0x15 144 | jmp exception_gate_main 145 | 146 | exception_gate_main: 147 | ; Set screen to Red 148 | mov rdi, [0x00005F00] ; Frame buffer base 149 | mov rcx, [0x00005F08] ; Frame buffer size 150 | shr rcx, 2 ; Quick divide by 4 151 | mov eax, 0x00FF0000 ; 0x00RRGGBB 152 | rep stosd 153 | exception_gate_main_hang: 154 | hlt 155 | jmp exception_gate_main_hang ; Hang. User must reset machine at this point 156 | ; ----------------------------------------------------------------------------- 157 | 158 | 159 | ; ----------------------------------------------------------------------------- 160 | ; create_gate 161 | ; rax = address of handler 162 | ; rdi = gate # to configure 163 | create_gate: 164 | push rdi 165 | push rax 166 | 167 | shl rdi, 4 ; quickly multiply rdi by 16 168 | stosw ; store the low word (15:0) 169 | shr rax, 16 170 | add rdi, 4 ; skip the gate marker 171 | stosw ; store the high word (31:16) 172 | shr rax, 16 173 | stosd ; store the high dword (63:32) 174 | 175 | pop rax 176 | pop rdi 177 | ret 178 | ; ----------------------------------------------------------------------------- 179 | 180 | 181 | ; ============================================================================= 182 | ; EOF 183 | -------------------------------------------------------------------------------- /src/pure64.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; Pure64 -- a 64-bit OS/software loader written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; The first stage loader is required to gather information about the system 6 | ; while the BIOS or UEFI is still available and load the Pure64 binary to 7 | ; 0x00008000. Setup a minimal 64-bit environment, copy the 64-bit kernel from 8 | ; the end of the Pure64 binary to the 1MiB memory mark and jump to it. 9 | ; 10 | ; Pure64 requires a payload for execution! The stand-alone pure64.sys file 11 | ; is not sufficient. You must append your kernel or software to the end of 12 | ; the Pure64 binary. The maximum size of the kernel or software is 26KiB. 13 | ; 14 | ; Windows - copy /b pure64.sys + kernel64.sys 15 | ; Unix - cat pure64.sys kernel64.sys > pure64.sys 16 | ; Max size of the resulting pure64.sys is 32768 bytes (32KiB) 17 | ; ============================================================================= 18 | 19 | 20 | BITS 64 21 | ORG 0x00008000 22 | PURE64SIZE equ 6144 ; Pad Pure64 to this length 23 | 24 | start: 25 | jmp bootmode ; This command will be overwritten with 'NOP's before the AP's are started 26 | nop 27 | db 0x36, 0x34 ; '64' marker 28 | 29 | ; ============================================================================= 30 | ; Code for AP startup 31 | BITS 16 32 | cli ; Disable all interrupts 33 | xor eax, eax 34 | xor ebx, ebx 35 | xor ecx, ecx 36 | xor edx, edx 37 | xor esi, esi 38 | xor edi, edi 39 | xor ebp, ebp 40 | mov ds, ax 41 | mov es, ax 42 | mov ss, ax 43 | mov fs, ax 44 | mov gs, ax 45 | mov esp, 0x7000 ; Set a known free location for the stack 46 | jmp 0x0000:init_smp_ap 47 | 48 | %include "init/smp_ap.asm" ; AP's will start execution at 0x8000 and fall through to this code 49 | 50 | ; ============================================================================= 51 | ; This is 32-bit code so it's important that the encoding of the first few instructions also 52 | ; work in 64-bit mode. If a 'U' is stored at 0x5FFF then we know it was a UEFI boot and can 53 | ; immediately proceed to start64. Otherwise we need to set up a minimal 64-bit environment. 54 | BITS 32 55 | bootmode: 56 | cmp bl, 'U' ; If it is 'U' then we booted via UEFI and are already in 64-bit mode for the BSP 57 | je start64 ; Jump to the 64-bit code, otherwise fall through to 32-bit init 58 | 59 | %ifdef BIOS 60 | mov [p_BootDisk], bh ; Save disk from where system was booted from 61 | 62 | mov eax, 16 ; Set the correct segment registers 63 | mov ds, ax 64 | mov es, ax 65 | mov ss, ax 66 | mov fs, ax 67 | mov gs, ax 68 | 69 | xor eax, eax ; Clear all registers 70 | xor ebx, ebx 71 | xor ecx, ecx 72 | xor edx, edx 73 | xor esi, esi 74 | xor edi, edi 75 | xor ebp, ebp 76 | mov esp, 0x8000 ; Set a known free location for the stack 77 | 78 | ; Save the frame buffer address, size (after its calculated), and the screen x,y 79 | xor eax, eax 80 | xor ebx, ebx 81 | mov ax, [0x5F00 + 16] ; BytesPerScanLine 82 | push eax 83 | mov bx, [0x5F00 + 20] ; YResolution 84 | push ebx 85 | mov ax, [0x5F00 + 18] ; XResolution 86 | push eax 87 | mul ebx 88 | mov ecx, eax 89 | shl ecx, 2 ; Quick multiply by 4 90 | mov edi, 0x5F00 91 | mov eax, [0x5F00 + 40] 92 | stosd ; 64-bit Frame Buffer Base (low) 93 | xor eax, eax 94 | stosd ; 64-bit Frame Buffer Base (high) 95 | mov eax, ecx 96 | stosd ; 64-bit Frame Buffer Size in bytes (low) 97 | xor eax, eax 98 | stosd ; 64-bit Frame Buffer Size in bytes (high) 99 | pop eax 100 | stosw ; 16-bit Screen X 101 | pop eax 102 | stosw ; 16-bit Screen Y 103 | pop eax 104 | shr eax, 2 ; Quick divide by 4 105 | stosw ; PixelsPerScanLine 106 | mov eax, 32 107 | stosw ; BitsPerPixel 108 | 109 | ; Clear memory for the Page Descriptor Entries (0x10000 - 0x5FFFF) 110 | mov edi, 0x00210000 111 | mov ecx, 81920 112 | rep stosd ; Write 320KiB 113 | 114 | ; Create the temporary Page Map Level 4 Entries (PML4E) 115 | ; PML4 is stored at 0x0000000000202000, create the first entry there 116 | ; A single PML4 entry can map 512GiB with 2MiB pages 117 | ; A single PML4 entry is 8 bytes in length 118 | cld 119 | mov edi, 0x00202000 ; Create a PML4 entry for the first 4GiB of RAM 120 | mov eax, 0x00203007 ; Bits 0 (P), 1 (R/W), 2 (U/S), location of low PDP (4KiB aligned) 121 | stosd 122 | xor eax, eax 123 | stosd 124 | 125 | ; Create the temporary Page-Directory-Pointer-Table Entries (PDPTE) 126 | ; PDPTE is stored at 0x0000000000203000, create the first entry there 127 | ; A single PDPTE can map 1GiB with 2MiB pages 128 | ; A single PDPTE is 8 bytes in length 129 | ; 4 entries are created to map the first 4GiB of RAM 130 | mov ecx, 4 ; number of PDPE's to make.. each PDPE maps 1GiB of physical memory 131 | mov edi, 0x00203000 ; location of low PDPE 132 | mov eax, 0x00210007 ; Bits 0 (P), 1 (R/W), 2 (U/S), location of first low PD (4KiB aligned) 133 | pdpte_low_32: 134 | stosd 135 | push eax 136 | xor eax, eax 137 | stosd 138 | pop eax 139 | add eax, 0x00001000 ; 4KiB later (512 records x 8 bytes) 140 | dec ecx 141 | cmp ecx, 0 142 | jne pdpte_low_32 143 | 144 | ; Create the temporary low Page-Directory Entries (PDE). 145 | ; A single PDE can map 2MiB of RAM 146 | ; A single PDE is 8 bytes in length 147 | mov edi, 0x00210000 ; Location of first PDE 148 | mov eax, 0x0000008F ; Bits 0 (P), 1 (R/W), 2 (U/S), 3 (PWT), and 7 (PS) set 149 | xor ecx, ecx 150 | pde_low_32: ; Create a 2 MiB page 151 | stosd 152 | push eax 153 | xor eax, eax 154 | stosd 155 | pop eax 156 | add eax, 0x00200000 ; Increment by 2MiB 157 | inc ecx 158 | cmp ecx, 2048 159 | jne pde_low_32 ; Create 2048 2 MiB page maps. 160 | 161 | ; Load the GDT 162 | lgdt [tGDTR64] 163 | 164 | ; Enable extended properties 165 | mov eax, cr4 166 | or eax, 0x0000000B0 ; PGE (Bit 7), PAE (Bit 5), and PSE (Bit 4) 167 | mov cr4, eax 168 | 169 | ; Point cr3 at PML4 170 | mov eax, 0x00202008 ; Write-thru enabled (Bit 3) 171 | mov cr3, eax 172 | 173 | ; Enable long mode and SYSCALL/SYSRET 174 | mov ecx, 0xC0000080 ; EFER MSR number 175 | rdmsr ; Read EFER 176 | or eax, 0x00000101 ; LME (Bit 8) 177 | wrmsr ; Write EFER 178 | 179 | mov bl, 'B' 180 | mov bh, byte [p_BootDisk] 181 | 182 | ; Enable paging to activate long mode 183 | mov eax, cr0 184 | or eax, 0x80000000 ; PG (Bit 31) 185 | mov cr0, eax 186 | 187 | jmp SYS64_CODE_SEL:start64 ; Jump to 64-bit mode 188 | %endif 189 | 190 | ; ============================================================================= 191 | ; 64-bit mode 192 | BITS 64 193 | start64: 194 | mov esp, 0x8000 ; Set a known free location for the stack 195 | 196 | mov edi, 0x5000 ; Clear the info map and system variable memory 197 | xor eax, eax 198 | mov ecx, 960 ; 3840 bytes (Range is 0x5000 - 0x5EFF) 199 | rep stosd ; Don't overwrite the UEFI/BIOS data at 0x5F00 200 | 201 | mov [p_BootMode], bl 202 | mov [p_BootDisk], bh 203 | 204 | mov ax, 0x03 ; Set flags for legacy ports (in case of no ACPI data) 205 | mov [p_IAPC_BOOT_ARCH], ax 206 | 207 | ; Mask all PIC interrupts 208 | mov al, 0xFF 209 | out 0x21, al 210 | out 0xA1, al 211 | 212 | ; Initialize and remap PIC IRQ's 213 | ; ICW1 214 | mov al, 0x11; ; Initialize PIC 1, init (bit 4) and ICW4 (bit 0) 215 | out 0x20, al 216 | mov al, 0x11; ; Initialize PIC 2, init (bit 4) and ICW4 (bit 0) 217 | out 0xA0, al 218 | ; ICW2 219 | mov al, 0x20 ; IRQ 0-7: interrupts 20h-27h 220 | out 0x21, al 221 | mov al, 0x28 ; IRQ 8-15: interrupts 28h-2Fh 222 | out 0xA1, al 223 | ; ICW3 224 | mov al, 4 225 | out 0x21, al 226 | mov al, 2 227 | out 0xA1, al 228 | ; ICW4 229 | mov al, 1 230 | out 0x21, al 231 | mov al, 1 232 | out 0xA1, al 233 | 234 | ; Disable NMIs 235 | in al, 0x70 236 | or al, 0x80 237 | out 0x70, al 238 | in al, 0x71 239 | 240 | ; Disable PIT 241 | mov al, 0x30 ; Channel 0 (7:6), Access Mode lo/hi (5:4), Mode 0 (3:1), Binary (0) 242 | out 0x43, al 243 | mov al, 0x00 244 | out 0x40, al 245 | 246 | ; Clear screen 247 | xor eax, eax 248 | xor ecx, ecx 249 | xor edx, edx 250 | mov ax, [0x00005F10] 251 | mov cx, [0x00005F12] 252 | mul ecx 253 | mov ecx, eax 254 | mov rdi, [0x00005F00] 255 | mov eax, 0x00404040 256 | rep stosd 257 | 258 | ; Visual Debug (1/4) 259 | mov ebx, 0 260 | call debug_block 261 | 262 | ; Clear out the first 20KiB of memory. This will store the 64-bit IDT, GDT, PML4, PDP Low, and PDP High 263 | mov ecx, 5120 264 | xor eax, eax 265 | mov edi, eax 266 | rep stosd 267 | 268 | ; Clear memory for the Page Descriptor Entries (0x10000 - 0x5FFFF) 269 | mov edi, 0x00010000 270 | mov ecx, 81920 271 | rep stosd ; Write 320KiB 272 | 273 | ; Copy the GDT to its final location in memory 274 | mov esi, gdt64 275 | mov edi, 0x00001000 ; GDT address 276 | mov ecx, (gdt64_end - gdt64) 277 | rep movsb ; Copy it to final location 278 | 279 | ; Create the Page Map Level 4 Entries (PML4E) 280 | ; PML4 is stored at 0x0000000000002000, create the first entry there 281 | ; A single PML4 entry can map 512GiB 282 | ; A single PML4 entry is 8 bytes in length 283 | mov edi, 0x00002000 ; Create a PML4 entry for physical memory 284 | mov eax, 0x00003003 ; Bits 0 (P), 1 (R/W), location of low PDP (4KiB aligned) 285 | stosq 286 | mov edi, 0x00002800 ; Create a PML4 entry for higher half (starting at 0xFFFF800000000000) 287 | mov eax, 0x00004003 ; Bits 0 (P), 1 (R/W), location of high PDP (4KiB aligned) 288 | stosq 289 | 290 | ; Check to see if the system supports 1 GiB pages 291 | ; If it does we will use that for identity mapping the lower memory 292 | mov eax, 0x80000001 293 | cpuid 294 | bt edx, 26 ; Page1GB 295 | jc pdpte_1GB 296 | 297 | ; 2MiB Pages 298 | ; Create the Low Page-Directory-Pointer-Table Entries (PDPTE) 299 | ; PDPTE starts at 0x0000000000003000, create the first entry there 300 | ; A single PDPTE can map 1GiB 301 | ; A single PDPTE is 8 bytes in length 302 | ; A PDPTE points to 4KiB of memory which contains 512 PDEs 303 | ; FIXME - This will completely fill the 64K set for the low PDE (only 16GiB identity mapped) 304 | mov ecx, 16 ; number of PDPE's to make.. each PDPE maps 1GiB of physical memory 305 | mov edi, 0x00003000 ; location of low PDPE 306 | mov eax, 0x00010003 ; Bits 0 (P), 1 (R/W), location of first low PD (4KiB aligned) 307 | pdpte_low: 308 | stosq 309 | add rax, 0x00001000 ; 4KiB later (512 records x 8 bytes) 310 | dec ecx 311 | jnz pdpte_low 312 | 313 | ; Create the Low Page-Directory Entries (PDE) 314 | ; A single PDE can map 2MiB of RAM 315 | ; A single PDE is 8 bytes in length 316 | mov edi, 0x00010000 ; Location of first PDE 317 | mov eax, 0x00000083 ; Bits 0 (P), 1 (R/W), and 7 (PS) set 318 | mov ecx, 8192 ; Create 8192 2MiB page maps 319 | pde_low: ; Create a 2MiB page 320 | stosq 321 | add rax, 0x00200000 ; Increment by 2MiB 322 | dec ecx 323 | jnz pde_low 324 | jmp skip1GB 325 | 326 | ; 1GiB Pages 327 | ; Create the Low Page-Directory-Pointer Table Entries (PDPTE) 328 | ; PDPTE starts at 0x0000000000010000, create the first entry there 329 | ; A single PDPTE can map 1GiB 330 | ; A single PDPTE is 8 bytes in length 331 | ; A PDPTE points to 4KiB of memory which contains 512 PDEs 332 | ; 8191 entries are created to map the first 8191GiB of RAM 333 | ; The last entry is reserved for the virtual LFB address 334 | pdpte_1GB: 335 | mov byte [p_1GPages], 1 336 | 337 | ; Overwrite the original PML4 entry for physical memory 338 | mov ecx, 16 339 | mov edi, 0x00002000 ; Create a PML4 entry for physical memory 340 | mov eax, 0x00010003 ; Bits 0 (P), 1 (R/W), location of low PDP (4KiB aligned) 341 | pml4_low_1GB: 342 | stosq 343 | add rax, 0x00001000 ; 4KiB later (512 records x 8 bytes) 344 | dec ecx 345 | jnz pml4_low_1GB 346 | 347 | mov ecx, 8191 ; number of PDPE's to make.. each PDPE maps 1GiB of physical memory 348 | mov edi, 0x00010000 ; location of low PDPE 349 | mov eax, 0x00000083 ; Bits 0 (P), 1 (R/W), 7 (PS) 350 | pdpte_low_1GB: ; Create a 1GiB page 351 | stosq 352 | add rax, 0x40000000 ; Increment by 1GiB 353 | dec ecx 354 | jnz pdpte_low_1GB 355 | 356 | skip1GB: 357 | 358 | ; Load the GDT 359 | lgdt [GDTR64] 360 | 361 | ; Point cr3 at PML4 362 | mov rax, 0x00002008 ; Write-thru enabled (Bit 3) 363 | mov cr3, rax 364 | 365 | xor eax, eax ; aka r0 366 | xor ebx, ebx ; aka r3 367 | xor ecx, ecx ; aka r1 368 | xor edx, edx ; aka r2 369 | xor esi, esi ; aka r6 370 | xor edi, edi ; aka r7 371 | xor ebp, ebp ; aka r5 372 | mov esp, 0x8000 ; aka r4 373 | xor r8, r8 374 | xor r9, r9 375 | xor r10, r10 376 | xor r11, r11 377 | xor r12, r12 378 | xor r13, r13 379 | xor r14, r14 380 | xor r15, r15 381 | 382 | mov ax, 0x10 ; TODO Is this needed? 383 | mov ds, ax 384 | mov es, ax 385 | mov ss, ax 386 | mov fs, ax 387 | mov gs, ax 388 | 389 | ; Set CS with a far return 390 | push SYS64_CODE_SEL 391 | push clearcs64 392 | retfq 393 | clearcs64: 394 | 395 | lgdt [GDTR64] ; Reload the GDT 396 | 397 | ; Visual Debug (2/4) 398 | mov ebx, 2 399 | call debug_block 400 | 401 | ; Build the IDT 402 | xor edi, edi ; create the 64-bit IDT (at linear address 0x0000000000000000) 403 | 404 | mov rcx, 32 405 | make_exception_gates: ; make gates for exception handlers 406 | mov rax, exception_gate 407 | push rax ; save the exception gate to the stack for later use 408 | stosw ; store the low word (15:0) of the address 409 | mov ax, SYS64_CODE_SEL 410 | stosw ; store the segment selector 411 | mov ax, 0x8E00 412 | stosw ; store exception gate marker 413 | pop rax ; get the exception gate back 414 | shr rax, 16 415 | stosw ; store the high word (31:16) of the address 416 | shr rax, 16 417 | stosd ; store the extra high dword (63:32) of the address. 418 | xor rax, rax 419 | stosd ; reserved 420 | dec rcx 421 | jnz make_exception_gates 422 | 423 | mov rcx, 256-32 424 | make_interrupt_gates: ; make gates for the other interrupts 425 | mov rax, interrupt_gate 426 | push rax ; save the interrupt gate to the stack for later use 427 | stosw ; store the low word (15:0) of the address 428 | mov ax, SYS64_CODE_SEL 429 | stosw ; store the segment selector 430 | mov ax, 0x8F00 431 | stosw ; store interrupt gate marker 432 | pop rax ; get the interrupt gate back 433 | shr rax, 16 434 | stosw ; store the high word (31:16) of the address 435 | shr rax, 16 436 | stosd ; store the extra high dword (63:32) of the address. 437 | xor eax, eax 438 | stosd ; reserved 439 | dec rcx 440 | jnz make_interrupt_gates 441 | 442 | ; Set up the exception gates for all of the CPU exceptions 443 | ; The following code depends on exception gates being below 16MB 444 | mov word [0x00*16], exception_gate_00 ; #DE 445 | mov word [0x01*16], exception_gate_01 ; #DB 446 | mov word [0x02*16], exception_gate_02 447 | mov word [0x03*16], exception_gate_03 ; #BP 448 | mov word [0x04*16], exception_gate_04 ; #OF 449 | mov word [0x05*16], exception_gate_05 ; #BR 450 | mov word [0x06*16], exception_gate_06 ; #UD 451 | mov word [0x07*16], exception_gate_07 ; #NM 452 | mov word [0x08*16], exception_gate_08 ; #DF 453 | mov word [0x09*16], exception_gate_09 ; #MF 454 | mov word [0x0A*16], exception_gate_10 ; #TS 455 | mov word [0x0B*16], exception_gate_11 ; #NP 456 | mov word [0x0C*16], exception_gate_12 ; #SS 457 | mov word [0x0D*16], exception_gate_13 ; #GP 458 | mov word [0x0E*16], exception_gate_14 ; #PF 459 | mov word [0x0F*16], exception_gate_15 460 | mov word [0x10*16], exception_gate_16 ; #MF 461 | mov word [0x11*16], exception_gate_17 ; #AC 462 | mov word [0x12*16], exception_gate_18 ; #MC 463 | mov word [0x13*16], exception_gate_19 ; #XM 464 | mov word [0x14*16], exception_gate_20 ; #VE 465 | mov word [0x15*16], exception_gate_21 ; #CP 466 | 467 | lidt [IDTR64] ; load IDT register 468 | 469 | ; Patch Pure64 AP code ; The AP's will be told to start execution at 0x8000 470 | mov edi, start ; We need to remove the BSP Jump call to get the AP's 471 | mov eax, 0x90909090 ; to fall through to the AP Init code 472 | stosd 473 | stosd ; Write 8 bytes in total to overwrite the 'far jump' and marker 474 | 475 | %ifdef UEFI 476 | ; Parse the memory map provided by UEFI 477 | uefi_memmap: 478 | ; Stage 1 - Process the UEFI memory map to find all usable memory 479 | ; Types 1-7 are ok to use once Boot Services were exited. Anything else should be considered unusable. 480 | ; Build an usable memory map at 0x200000 481 | xor r9, r9 482 | xor ebx, ebx 483 | mov esi, 0x00220000 - 48 ; The start of the UEFI map minus 48 bytes for the loop start 484 | mov edi, 0x00200000 ; Where to build the clean map 485 | uefi_memmap_next: 486 | add esi, 48 ; Skip to start of next record 487 | mov rax, [rsi+24] ; Check for end of records 488 | cmp rax, 0 ; If there are 0 pages we are at the end of the list 489 | je uefi_memmap_end 490 | mov rax, [rsi+8] 491 | cmp rax, 0x100000 ; Test if the Physical Address less than 0x100000 492 | jb uefi_memmap_next ; If so, skip it 493 | mov rax, [rsi] 494 | cmp rax, 0 ; EfiReservedMemoryType (Not usable) 495 | je uefi_memmap_next 496 | cmp rax, 7 ; EfiConventionalMemory (Free) 497 | jbe uefi_memmap_usable 498 | mov bl, 0 499 | jmp uefi_memmap_next 500 | uefi_memmap_usable: 501 | cmp bl, 1 502 | je uefi_memmap_usable_contiguous 503 | mov rax, [rsi+8] 504 | stosq ; Store Physical Start 505 | mov rax, [rsi+24] 506 | stosq ; Store NumberOfPages 507 | uefi_memmap_usable_contiguous_next: 508 | mov r9, rax 509 | shl r9, 12 ; Quick multiply by 4096 510 | add r9, [rsi+8] ; R9 should match the physical address of the next record if they are contiguous 511 | mov bl, 0 ; Non-contiguous 512 | cmp r9, [rsi+56] ; Check R9 against the next Physical Start 513 | jne uefi_memmap_next 514 | mov bl, 1 ; Contiguous 515 | jmp uefi_memmap_next 516 | uefi_memmap_usable_contiguous: 517 | sub rdi, 8 518 | mov rax, [rsi+24] 519 | add rax, [rdi] 520 | stosq 521 | mov rax, [rsi+24] 522 | jmp uefi_memmap_usable_contiguous_next 523 | uefi_memmap_end: 524 | xor eax, eax ; Store a blank record 525 | stosq 526 | stosq 527 | 528 | ; Stage 2 - Clear entries less than 3MiB in length 529 | mov esi, 0x00200000 ; Start at the beginning of the records 530 | mov edi, 0x00200000 531 | uefi_purge: 532 | lodsq 533 | cmp rax, 0 534 | je uefi_purge_end 535 | stosq 536 | lodsq 537 | cmp rax, 0x300 538 | jb uefi_purge_remove 539 | stosq 540 | jmp uefi_purge 541 | uefi_purge_remove: 542 | sub edi, 8 543 | jmp uefi_purge 544 | uefi_purge_end: 545 | xor eax, eax ; Store a blank record 546 | stosq 547 | stosq 548 | 549 | ; Stage 3 - Round up Physical Address to next 2MiB boundary if needed and convert 4KiB pages to 1MiB pages 550 | mov esi, 0x00200000 - 16 ; Start at the beginning of the records 551 | xor ecx, ecx ; MiB counter 552 | uefi_round: 553 | add esi, 16 554 | mov rax, [rsi] ; Load the Physical Address 555 | cmp rax, 0 ; Is it zero? (End of list) 556 | je uefi_round_end ; If so, bail out 557 | mov rbx, rax ; Copy Physical Address to RBX 558 | and rbx, 0x1FFFFF ; Check if any bits between 20-0 are set 559 | cmp rbx, 0 ; If not, RBX should be 0 560 | jz uefi_convert 561 | ; At this point one of the bits between 20 and 0 in the starting address are set 562 | ; Round the starting address up to the next 2MiB 563 | shr rax, 21 564 | shl rax, 21 565 | add rax, 0x200000 566 | mov [rsi], rax 567 | mov rax, [rsi+8] 568 | shr rax, 8 ; Convert 4K blocks to MiB 569 | sub rax, 1 ; Subtract 1MiB 570 | mov [rsi+8], rax 571 | add rcx, rax ; Add to MiB counter 572 | jmp uefi_round 573 | uefi_convert: 574 | mov rax, [rsi+8] 575 | shr rax, 8 ; Convert 4K blocks to MiB 576 | mov [rsi+8], rax 577 | add rcx, rax ; Add to MiB counter 578 | jmp uefi_round 579 | uefi_round_end: 580 | sub ecx, 2 581 | mov dword [p_mem_amount], ecx 582 | xor eax, eax ; Store a blank record 583 | stosq 584 | stosq 585 | jmp memmap_end 586 | %endif 587 | 588 | %ifdef BIOS 589 | ; Parse the memory map provided by BIOS 590 | bios_memmap: 591 | ; Stage 1 - Process the E820 memory map to find all possible 2MiB pages that are free to use 592 | ; Build an available memory map at 0x200000 593 | xor ecx, ecx 594 | xor ebx, ebx ; Running counter of available MiBs 595 | mov esi, 0x00006000 ; E820 Map location 596 | mov edi, 0x00200000 ; 2MiB 597 | bios_memmap_nextentry: 598 | add esi, 16 ; Skip ESI to type marker 599 | mov eax, [esi] ; Load the 32-bit type marker 600 | cmp eax, 0 ; End of the list? 601 | je bios_memmap_end820 602 | cmp eax, 1 ; Is it marked as free? 603 | je bios_memmap_processfree 604 | add esi, 16 ; Skip ESI to start of next entry 605 | jmp bios_memmap_nextentry 606 | bios_memmap_processfree: 607 | ; TODO Check ACPI 3.0 Extended Attributes - Bit 0 should be set 608 | sub esi, 16 609 | mov rax, [rsi] ; Physical start address 610 | add esi, 8 611 | mov rcx, [rsi] ; Physical length 612 | add esi, 24 613 | shr rcx, 20 ; Convert bytes to MiB 614 | cmp rcx, 0 ; Do we have at least 1 page? 615 | je bios_memmap_nextentry 616 | stosq 617 | mov rax, rcx 618 | stosq 619 | add ebx, ecx 620 | jmp bios_memmap_nextentry 621 | bios_memmap_end820: 622 | 623 | ; Stage 2 - Sanitize the records 624 | mov esi, 0x00200000 625 | memmap_sani: 626 | mov rax, [rsi] 627 | cmp rax, 0 628 | je memmap_saniend 629 | bt rax, 20 630 | jc memmap_itsodd 631 | add esi, 16 632 | jmp memmap_sani 633 | memmap_itsodd: 634 | add rax, 0x100000 635 | mov [rsi], rax 636 | mov rax, [rsi+8] 637 | sub rax, 1 638 | mov [rsi+8], rax 639 | add esi, 16 640 | jmp memmap_sani 641 | memmap_saniend: 642 | sub ebx, 2 ; Subtract 2MiB for the CPU stacks 643 | mov dword [p_mem_amount], ebx 644 | xor eax, eax 645 | stosq 646 | stosq 647 | %endif 648 | 649 | memmap_end: 650 | 651 | ; Create the High Page-Directory-Pointer-Table Entries (PDPTE) 652 | ; High PDPTE is stored at 0x0000000000004000, create the first entry there 653 | ; A single PDPTE can map 1GiB with 2MiB pages 654 | ; A single PDPTE is 8 bytes in length 655 | mov ecx, dword [p_mem_amount] 656 | shr ecx, 10 ; MBs -> GBs 657 | add rcx, 1 ; Add 1. This is the number of PDPE's to make 658 | mov edi, 0x00004000 ; location of high PDPE 659 | mov eax, 0x00020003 ; location of first high PD. Bits 0 (P) and 1 (R/W) set 660 | create_pdpe_high: 661 | stosq 662 | add rax, 0x00001000 ; 4K later (512 records x 8 bytes) 663 | dec ecx 664 | cmp ecx, 0 665 | jne create_pdpe_high 666 | 667 | ; Create the High Page-Directory Entries (PDE). 668 | ; A single PDE can map 2MiB of RAM 669 | ; A single PDE is 8 bytes in length 670 | mov esi, 0x00200000 ; Location of the available memory map 671 | mov edi, 0x00020000 ; Location of first PDE 672 | pde_next_range: 673 | lodsq ; Load the base 674 | xchg rax, rcx 675 | lodsq ; Load the length 676 | xchg rax, rcx 677 | cmp rax, 0 ; Check if at end of records 678 | je pde_end ; Bail out if so 679 | cmp rax, 0x00200000 680 | ja skipfirst4mb 681 | add rax, 0x00200000 ; Add 2 MiB to the base 682 | sub rcx, 2 ; Subtract 2 MiB from the length 683 | skipfirst4mb: 684 | shr ecx, 1 ; Quick divide by 2 for 2 MB pages 685 | add rax, 0x00000083 ; Bits 0 (P), 1 (R/W), and 7 (PS) set 686 | pde_high: ; Create a 2MiB page 687 | stosq 688 | add rax, 0x00200000 ; Increment by 2MiB 689 | cmp ecx, 0 690 | je pde_next_range 691 | dec ecx 692 | cmp ecx, 0 693 | jne pde_high 694 | jmp pde_next_range 695 | pde_end: 696 | 697 | ; Read APIC Address from MSR and enable it (if not done so already) 698 | mov ecx, IA32_APIC_BASE 699 | rdmsr ; Returns APIC in EDX:EAX 700 | bts eax, 11 ; APIC Global Enable 701 | wrmsr 702 | and eax, 0xFFFFF000 ; Clear lower 12 bits 703 | shl rdx, 32 ; Shift lower 32 bits to upper 32 bits 704 | add rax, rdx 705 | mov [p_LocalAPICAddress], rax 706 | 707 | ; Check for x2APIC support 708 | mov eax, 1 709 | cpuid ; x2APIC is supported if bit 21 is set 710 | shr ecx, 21 711 | and cl, 1 712 | mov byte [p_x2APIC], cl 713 | 714 | call init_acpi ; Find and process the ACPI tables 715 | call init_cpu ; Configure the BSP CPU 716 | call init_hpet ; Configure the HPET 717 | 718 | ; Visual Debug (3/4) 719 | mov ebx, 4 720 | call debug_block 721 | 722 | call init_smp ; Init of SMP, deactivate interrupts 723 | 724 | ; Reset the stack to the proper location (was set to 0x8000 previously) 725 | mov rsi, [p_LocalAPICAddress] ; We would call p_smp_get_id here but the stack is not ... 726 | add rsi, 0x20 ; ... yet defined. It is safer to find the value directly. 727 | lodsd ; Load a 32-bit value. We only want the high 8 bits 728 | shr rax, 24 ; Shift to the right and AL now holds the CPU's APIC ID 729 | shl rax, 10 ; shift left 10 bits for a 1024byte stack 730 | add rax, 0x0000000000050400 ; stacks decrement when you "push", start at 1024 bytes in 731 | mov rsp, rax ; Pure64 leaves 0x50000-0x9FFFF free so we use that 732 | 733 | ; Build the InfoMap 734 | xor edi, edi 735 | mov di, 0x5000 736 | mov rax, [p_ACPITableAddress] 737 | stosq 738 | mov eax, [p_BSP] 739 | stosd 740 | 741 | mov di, 0x5010 742 | mov ax, [p_cpu_speed] 743 | stosw 744 | mov ax, [p_cpu_activated] 745 | stosw 746 | mov ax, [p_cpu_detected] 747 | stosw 748 | 749 | mov di, 0x5018 750 | xor eax, eax 751 | cpuid 752 | stosd ; Store maximum supported CPUID standard level 753 | mov eax, 0x80000000 754 | cpuid 755 | stosd ; Store maximum supported CPUID extended level 756 | cmp eax, 0x80000008 757 | jb no_address_size 758 | mov eax, 0x80000008 759 | cpuid 760 | mov [0x5016], ax ; Store virtual/physical address bits 761 | no_address_size: 762 | 763 | mov di, 0x5020 764 | mov eax, [p_mem_amount] 765 | and eax, 0xFFFFFFFE 766 | stosd 767 | 768 | mov di, 0x5030 769 | mov al, [p_IOAPICCount] 770 | stosb 771 | mov al, [p_IOAPICIntSourceC] 772 | stosb 773 | 774 | mov di, 0x5040 775 | mov rax, [p_HPET_Address] 776 | stosq 777 | mov eax, [p_HPET_Frequency] 778 | stosd 779 | mov ax, [p_HPET_CounterMin] 780 | stosw 781 | mov al, [p_HPET_Timers] 782 | stosb 783 | 784 | mov di, 0x5060 785 | mov rax, [p_LocalAPICAddress] 786 | stosq 787 | 788 | ; Copy the data we received from UEFI/BIOS 789 | mov di, 0x5080 790 | mov rax, [0x00005F00] ; Base address of video memory 791 | stosq 792 | mov eax, [0x00005F00 + 0x10] ; X and Y resolution (16-bits each) 793 | stosd 794 | mov eax, [0x00005F00 + 0x14] ; Pixels per scan line 795 | stosw 796 | mov ax, 32 797 | stosw 798 | 799 | ; Store the PCI(e) data 800 | mov di, 0x5090 801 | mov ax, [p_PCIECount] 802 | stosw 803 | mov ax, [p_IAPC_BOOT_ARCH] 804 | stosw 805 | 806 | ; Store miscellaneous flags 807 | mov di, 0x50E0 808 | mov al, [p_1GPages] 809 | stosb 810 | mov al, [p_x2APIC] 811 | stosb 812 | 813 | ; Set the Linear Frame Buffer to use write-combining 814 | mov eax, 0x80000001 815 | cpuid 816 | bt edx, 26 ; Page1GB 817 | jnc lfb_wc_2MB 818 | 819 | ; Set the 1GB page the frame buffer is in to WC - PAT = 1, PCD = 0, PWT = 1 820 | lfb_wc_1GB: 821 | mov rax, [0x00005F00] ; Base address of video memory 822 | mov rbx, 0x100000000 ; Compare to 4GB 823 | cmp rax, rbx 824 | jbe lfb_wc_end ; If less, don't set WC 825 | 826 | mov rbx, rax 827 | mov rcx, 0xFFFFFFFFC0000000 828 | and rax, rcx 829 | mov rcx, 0x000000003FFFFFFF 830 | and rbx, rcx 831 | mov ax, 0x108B ; P (0), R/W (1), PWT (3), PS (7), PAT (12) 832 | mov rdi, 0x1FFF8 833 | mov [rdi], rax ; Write updated PDPTE 834 | 835 | mov rax, 0x000007FFC0000000 ; 8191GiB 836 | add rax, rbx ; Add offset within 1GiB page 837 | mov [0x00005080], rax ; Write out new virtual address to LFB 838 | 839 | jmp lfb_wc_end 840 | 841 | ; Set the relevant 2MB pages the frame buffer is in to WC 842 | lfb_wc_2MB: 843 | mov ecx, 4 ; 4 2MiB pages - TODO only set the pages needed 844 | mov edi, 0x00010000 845 | mov rax, [0x00005F00] ; Base address of video memory 846 | shr rax, 18 847 | add rdi, rax 848 | lfb_wc_2MB_nextpage: 849 | mov eax, [edi] ; Load the 8-byte value 850 | or ax, 0x1008 ; Set bits 12 (PAT) and 3 (PWT) 851 | and ax, 0xFFEF ; Clear bit 4 (PCD) 852 | mov [edi], eax ; Write it back 853 | add edi, 8 854 | sub ecx, 1 855 | jnz lfb_wc_2MB_nextpage 856 | lfb_wc_end: 857 | mov rax, cr3 ; Flush TLB 858 | mov cr3, rax 859 | wbinvd ; Flush Cache 860 | 861 | ; Move the trailing binary to its final location 862 | mov esi, 0x8000+PURE64SIZE ; Memory offset to end of pure64.sys 863 | mov edi, 0x100000 ; Destination address at the 1MiB mark 864 | mov ecx, ((32768 - PURE64SIZE) / 8) 865 | rep movsq ; Copy 8 bytes at a time 866 | 867 | ; Visual Debug (4/4) 868 | mov ebx, 6 869 | call debug_block 870 | 871 | %ifdef BIOS 872 | cmp byte [p_BootDisk], 'F' ; Check if sys is booted from floppy? 873 | jnz clear_regs 874 | call read_floppy ; Then load whole floppy at memory 875 | %endif 876 | 877 | ; Clear all registers (skip the stack pointer) 878 | clear_regs: 879 | xor eax, eax ; These 32-bit calls also clear the upper bits of the 64-bit registers 880 | xor ebx, ebx 881 | xor ecx, ecx 882 | xor edx, edx 883 | xor esi, esi 884 | xor edi, edi 885 | xor ebp, ebp 886 | xor r8, r8 887 | xor r9, r9 888 | xor r10, r10 889 | xor r11, r11 890 | xor r12, r12 891 | xor r13, r13 892 | xor r14, r14 893 | xor r15, r15 894 | jmp 0x00100000 ; Done with Pure64, jump to the kernel 895 | 896 | 897 | %include "init/acpi.asm" 898 | %include "init/cpu.asm" 899 | %include "init/hpet.asm" 900 | %include "init/smp.asm" 901 | %ifdef BIOS 902 | %include "fdc/dma.asm" 903 | %include "fdc/fdc_64.asm" 904 | %endif 905 | %include "interrupt.asm" 906 | %include "sysvar.asm" 907 | 908 | 909 | ; ----------------------------------------------------------------------------- 910 | ; debug_block - Create a block of colour on the screen 911 | ; IN: EBX = Index # 912 | debug_block: 913 | push rax 914 | push rbx 915 | push rcx 916 | push rdx 917 | push rdi 918 | 919 | ; Calculate parameters 920 | push rbx 921 | push rax 922 | xor edx, edx 923 | xor eax, eax 924 | xor ebx, ebx 925 | mov ax, [0x00005F00 + 0x12] ; Screen Y 926 | sub ax, 16 ; Upper row 927 | shr ax, 1 ; Quick divide by 2 928 | mov bx, [0x00005F00 + 0x10] ; Screen X 929 | shl ebx, 2 ; Quick multiply by 4 930 | mul ebx ; Multiply EDX:EAX by EBX 931 | mov rdi, [0x00005F00] ; Frame buffer base 932 | add rdi, rax ; Offset is ((screeny - 8) / 2 + screenx * 4) 933 | pop rax 934 | pop rbx 935 | xor edx, edx 936 | mov dx, [0x00005F00 + 0x14] ; PixelsPerScanLine 937 | shl edx, 2 ; Quick multiply by 4 for line offset 938 | xor ecx, ecx 939 | mov cx, [0x00005F00 + 0x10] ; Screen X 940 | shr cx, 4 ; CX = total amount of 8-pixel wide blocks 941 | sub cx, 4 942 | add ebx, ecx 943 | shl ebx, 5 ; Quick multiply by 32 (8 pixels by 4 bytes each) 944 | add rdi, rbx 945 | 946 | ; Draw the 8x8 pixel block 947 | mov ebx, 8 ; 8 pixels tall 948 | mov eax, 0x00F7CA54 ; Return Infinity Yellow/Orange 949 | nextline: 950 | mov ecx, 8 ; 8 pixels wide 951 | rep stosd 952 | add rdi, rdx ; Add line offset 953 | sub rdi, 8*4 ; 8 pixels by 4 bytes each 954 | dec ebx 955 | jnz nextline 956 | 957 | pop rdi 958 | pop rdx 959 | pop rcx 960 | pop rbx 961 | pop rax 962 | ret 963 | ; ----------------------------------------------------------------------------- 964 | 965 | 966 | %ifdef BIOS 967 | ; ----------------------------------------------------------------------------- 968 | ; debug_progressbar 969 | ; IN: EBX = Index # 970 | ; Note: During a floppy load this function gets called 40 times 971 | debug_progressbar: 972 | mov rdi, [0x00005F00] ; Frame buffer base 973 | add rdi, rbx 974 | ; Offset to start of progress bar location 975 | ; 1024 - screen width 976 | ; 367 - Line # to display bar 977 | ; 32 - Offset to start of the progress blocks 978 | ; 512 - Middle of screen 979 | ; 4 - 32-bit pixels 980 | add rdi, ((1024 * 387 - 32) + 512) * 4 981 | 982 | ; Draw the pixel 983 | mov eax, 0x00FFFFFF ; White 984 | stosd 985 | add rdi, (1024 * 4) - 4 986 | stosd 987 | 988 | ret 989 | ; ----------------------------------------------------------------------------- 990 | %endif 991 | 992 | 993 | EOF: 994 | db 0xDE, 0xAD, 0xC0, 0xDE 995 | 996 | ; Pad to an even KB file 997 | times PURE64SIZE-($-$$) db 0x90 998 | 999 | 1000 | ; ============================================================================= 1001 | ; EOF 1002 | -------------------------------------------------------------------------------- /src/sysvar.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; Pure64 -- a 64-bit OS/software loader written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; System Variables 6 | ; ============================================================================= 7 | 8 | 9 | ;CONFIG 10 | cfg_smpinit: db 1 ; By default SMP is enabled. Set to 0 to disable. 11 | 12 | ; Memory locations 13 | InfoMap: equ 0x0000000000005000 14 | IM_DetectedCoreIDs: equ 0x0000000000005100 ; 1 byte per entry - Each byte is the APIC ID of a core 15 | IM_PCIE: equ 0x0000000000005400 ; 16 bytes per entry 16 | IM_IOAPICAddress: equ 0x0000000000005600 ; 16 bytes per entry 17 | IM_IOAPICIntSource: equ 0x0000000000005700 ; 8 bytes per entry 18 | SystemVariables: equ 0x0000000000005800 19 | IM_ActivedCoreIDs: equ 0x0000000000005E00 ; 1 byte per entry - 1 if the core was activated 20 | VBEModeInfoBlock: equ 0x0000000000005F00 ; 256 bytes 21 | 22 | ; DQ - Starting at offset 0, increments by 0x8 23 | p_ACPITableAddress: equ SystemVariables + 0x00 24 | p_LocalAPICAddress: equ SystemVariables + 0x10 25 | p_Counter_Timer: equ SystemVariables + 0x18 26 | p_Counter_RTC: equ SystemVariables + 0x20 27 | p_HPET_Address: equ SystemVariables + 0x28 28 | 29 | ; DD - Starting at offset 0x80, increments by 4 30 | p_BSP: equ SystemVariables + 0x80 31 | p_mem_amount: equ SystemVariables + 0x84 ; in MiB 32 | p_HPET_Frequency: equ SystemVariables + 0x88 33 | 34 | ; DW - Starting at offset 0x100, increments by 2 35 | p_cpu_speed: equ SystemVariables + 0x100 36 | p_cpu_activated: equ SystemVariables + 0x102 37 | p_cpu_detected: equ SystemVariables + 0x104 38 | p_PCIECount: equ SystemVariables + 0x106 39 | p_HPET_CounterMin: equ SystemVariables + 0x108 40 | p_IAPC_BOOT_ARCH: equ SystemVariables + 0x10A 41 | 42 | ; DB - Starting at offset 0x180, increments by 1 43 | p_IOAPICCount: equ SystemVariables + 0x180 44 | p_BootMode: equ SystemVariables + 0x181 ; 'U' for UEFI, otherwise BIOS 45 | p_IOAPICIntSourceC: equ SystemVariables + 0x182 46 | p_x2APIC: equ SystemVariables + 0x183 47 | p_HPET_Timers: equ SystemVariables + 0x184 48 | p_BootDisk: equ SystemVariables + 0x185 ; 'F' for Floppy drive 49 | p_1GPages: equ SystemVariables + 0x186 ; 1 if 1GB pages are supported 50 | 51 | align 16 52 | GDTR32: ; Global Descriptors Table Register 53 | dw gdt32_end - gdt32 - 1 ; limit of GDT (size minus one) 54 | dq gdt32 ; linear address of GDT 55 | 56 | align 16 57 | gdt32: 58 | SYS32_NULL_SEL equ $-gdt32 ; Null Segment 59 | dq 0x0000000000000000 60 | SYS32_CODE_SEL equ $-gdt32 ; 32-bit code descriptor 61 | dq 0x00CF9A000000FFFF ; 55 Granularity 4KiB, 54 Size 32bit, 47 Present, 44 Code/Data, 43 Executable, 41 Readable 62 | SYS32_DATA_SEL equ $-gdt32 ; 32-bit data descriptor 63 | dq 0x00CF92000000FFFF ; 55 Granularity 4KiB, 54 Size 32bit, 47 Present, 44 Code/Data, 41 Writeable 64 | gdt32_end: 65 | 66 | align 16 67 | tGDTR64: ; Global Descriptors Table Register 68 | dw gdt64_end - gdt64 - 1 ; limit of GDT (size minus one) 69 | dq gdt64 ; linear address of GDT 70 | 71 | align 16 72 | GDTR64: ; Global Descriptors Table Register 73 | dw gdt64_end - gdt64 - 1 ; limit of GDT (size minus one) 74 | dq 0x0000000000001000 ; linear address of GDT 75 | 76 | gdt64: ; This structure is copied to 0x0000000000001000 77 | SYS64_NULL_SEL equ $-gdt64 ; Null Segment 78 | dq 0x0000000000000000 79 | SYS64_CODE_SEL equ $-gdt64 ; Code segment, read/execute, nonconforming 80 | dq 0x00209A0000000000 ; 53 Long mode code, 47 Present, 44 Code/Data, 43 Executable, 41 Readable 81 | SYS64_DATA_SEL equ $-gdt64 ; Data segment, read/write, expand down 82 | dq 0x0000920000000000 ; 47 Present, 44 Code/Data, 41 Writable 83 | gdt64_end: 84 | 85 | IDTR64: ; Interrupt Descriptor Table Register 86 | dw 256*16-1 ; limit of IDT (size minus one) (4096 bytes - 1) 87 | dq 0x0000000000000000 ; linear address of IDT 88 | 89 | 90 | ; ============================================================================= 91 | ; EOF 92 | --------------------------------------------------------------------------------