├── .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 | Start Address | End Address | Size | Description |
142 | 0x0000000000000000 | 0x0000000000000FFF | 4 KiB | IDT - 256 descriptors (each descriptor is 16 bytes) |
143 | 0x0000000000001000 | 0x0000000000001FFF | 4 KiB | GDT - 256 descriptors (each descriptor is 16 bytes) |
144 | 0x0000000000002000 | 0x0000000000002FFF | 4 KiB | PML4 - 512 entries, first entry points to PDP at 0x3000 |
145 | 0x0000000000003000 | 0x0000000000003FFF | 4 KiB | PDP Low - 512 entries |
146 | 0x0000000000004000 | 0x0000000000004FFF | 4 KiB | PDP High - 512 entries |
147 | 0x0000000000005000 | 0x0000000000007FFF | 12 KiB | Pure64 Data |
148 | 0x0000000000008000 | 0x000000000000FFFF | 32 KiB | Pure64 - After the OS is loaded and running this memory is free again |
149 | 0x0000000000010000 | 0x000000000001FFFF | 64 KiB | PD Low - Entries are 8 bytes per 2MiB page |
150 | 0x0000000000020000 | 0x000000000005FFFF | 256 KiB | PD High - Entries are 8 bytes per 2MiB page |
151 | 0x0000000000060000 | 0x000000000009FFFF | 256 KiB | Free |
152 | 0x00000000000A0000 | 0x00000000000FFFFF | 384 KiB | BIOS ROM Area |
153 | | | | VGA mem at 0xA0000 (128 KiB) Color text starts at 0xB8000 |
154 | | | | Video BIOS at 0xC0000 (64 KiB) |
155 | | | | Motherboard BIOS at F0000 (64 KiB) |
156 | 0x0000000000100000 | 0xFFFFFFFFFFFFFFFF | 1+ MiB | The software payload is loaded here |
157 |
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 | Memory Address | Variable Size | Name | Description |
170 | 0x5000 | 64-bit | ACPI | Address of the ACPI tables |
171 | 0x5008 | 32-bit | CPU_BSP_ID | APIC ID of the BSP |
172 | 0x5010 | 16-bit | CPU_SPEED | Speed of the CPUs in MegaHertz (MHz) |
173 | 0x5012 | 16-bit | CPU_CORES_ACTIVE | The number of CPU cores that were activated in the system |
174 | 0x5014 | 16-bit | CPU_CORES_DETECT | The number of CPU cores that were detected in the system |
175 | 0x5016 | 8-bit | CPU_MEM_PHYSICAL | The number of bits that are valid for a physical address |
176 | 0x5017 | 8-bit | CPU_MEM_VIRTUAL | The number of bits that are valid for a virtual address |
177 | 0x5018 | 32-bit | CPU_ID_MAX_STANDARD | The maximum CPUID standard leaf |
178 | 0x501C | 32-bit | CPU_ID_MAX_EXTENDED | The maximum CPUID extended leaf |
179 | 0x5020 | 32-bit | RAMAMOUNT | Amount of system RAM in Mebibytes (MiB) |
180 | 0x5022 - 0x502F | | | For future use |
181 | 0x5030 | 8-bit | IOAPIC_COUNT | Number of I/O APICs in the system |
182 | 0x5031 | 8-bit | IOAPIC_INTSOURCE_COUNT | Number of I/O APIC Interrupt Source Override |
183 | 0x5032 - 0x503F | | | For future use |
184 | 0x5040 | 64-bit | HPET Address | Base memory address for the High Precision Event Timer |
185 | 0x5048 | 32-bit | HPET Frequency | Frequency for the High Precision Event Timer |
186 | 0x504C | 16-bit | HPET Counter Minumum | Minimum Counter for the High Precision Event Timer |
187 | 0x504E | 8-bit | HPET Counters | Number of Counter in the High Precision Event Timer |
188 | 0x504F | | | For future use |
189 | 0x5060 | 64-bit | LAPIC | Local APIC address |
190 | 0x5068 - 0x507F | | | For future use |
191 | 0x5080 | 64-bit | VIDEO_BASE | Base memory for video (if graphics mode set) |
192 | 0x5088 | 16-bit | VIDEO_X | X resolution |
193 | 0x508A | 16-bit | VIDEO_Y | Y resolution |
194 | 0x508C | 16-bit | VIDEO_PPSL | # of pixels per scan line |
195 | 0x508E | 16-bit | VIDEO_BPP | # of bits per pixel |
196 | 0x5090 | 16-bit | PCIE_COUNT | Number of PCIe buses |
197 | 0x5092 | 16-bit | IAPC_BOOT_ARCH | IA-PC Boot Architecture Flags |
198 | 0x5094 - 0x50DF | | | For future use |
199 | 0x50E0 | 8-bit | FLAG_1GBPAGE | 1 if 1GB Pages are supported |
200 | 0x50E1 | 8-bit | FLAG_X2APIC | 1 if X2APIC is supported |
201 | 0x50E2 - 0x50FF | | | For future use |
202 | 0x5100 - 0x51FF | 8-bit | APIC_ID | APIC ID's for valid CPU cores (based on CORES_ACTIVE) |
203 | 0x5200 - 0x53FF | | | For future use |
204 | 0x5400 - 0x55FF | 16 byte entries | PCIE | PCIe bus data |
205 | 0x5600 - 0x56FF | 16 byte entries | IOAPIC | I/O APIC addresses (based on IOAPIC_COUNT) |
206 | 0x5700 - 0x57FF | 8 byte entries | IOAPIC_INTSOURCE | I/O APIC Interrupt Source Override Entries (based on IOAPIC_INTSOURCE_COUNT) |
207 |
208 |
209 | PCIE list format:
210 |
211 | Offset | Variable Size | Name | Description |
212 | 0x00 | 64-bit | Base | The base address of enhanced configuration mechanism |
213 | 0x08 | 16-bit | Group | The PCI segment group number |
214 | 0x0A | 8-bit | Start Bus | Start PCI bus number decoded by this host bridge |
215 | 0x0B | 8-bit | End Bus | End PCI bus number decoded by this host bridge |
216 | 0x0C | 32-bit | Reserved | This value should be 0 |
217 |
218 |
219 | IOAPIC list format:
220 |
221 | Offset | Variable Size | Name | Description |
222 | 0x00 | 32-bit | I/O APIC ID | The ID of an I/O APIC |
223 | 0x00 | 32-bit | I/O APIC Address | The 32-bit physical address to access this I/O APIC |
224 | 0x00 | 32-bit | Global System Interrupt Base | The global system interrupt number where this I/O APIC’s interrupt inputs start |
225 | 0x00 | 32-bit | Reserved | This value should be 0 |
226 |
227 |
228 | IOAPIC_INTSOURCE list format:
229 |
230 | Offset | Variable Size | Name | Description |
231 | 0x00 | 8-bit | Bus | 0 |
232 | 0x00 | 8-bit | Source | Bus-relative interrupt source |
233 | 0x00 | 32-bit | Global System Interrupt | The Global System Interrupt that this bus-relative interrupt source will signal |
234 | 0x00 | 16-bit | Flags | MPS INTI flags |
235 |
236 |
237 | MPS INTI flags:
238 |
239 | Flags | Bit Length | Bit Offset | Description |
240 | Polarity | 2 | 0 | 01 Active high, 11 Active low |
241 | Trigger Mode | 2 | 2 | 01 Edge-triggered, 11 Level-triggered |
242 |
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 | Variable | Variable Size | Description |
251 | Base | 64-bit | Base Address |
252 | Length | 64-bit | Length in bytes |
253 | Type | 32-bit | Type of memory (1 is usable) |
254 | ACPI | 32-bit | See document linked below |
255 |
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 | Variable | Variable Size | Description |
263 | Type | 64-bit | The type of the memory region |
264 | Physical Start | 64-bit | Physical Address - 4K aligned |
265 | Virtual Start | 64-bit | Virtual Address - 4K aligned |
266 | NumberOfPages | 64-bit | The number of 4K pages in this section |
267 | Attribute | 64-bit | See document linked below |
268 | Padding | 64-bit | Padding |
269 |
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 |
--------------------------------------------------------------------------------