├── .editorconfig ├── .github └── workflows │ └── main.yml ├── .gitignore ├── LICENSE ├── README.md ├── api ├── libBareMetal.asm ├── libBareMetal.c └── libBareMetal.h ├── build.sh ├── clean.sh ├── doc ├── BareMetal-Model.png ├── Debugging.md ├── Kernel API.md └── Supported Hardware.md └── src ├── README.md ├── drivers.asm ├── drivers ├── apic.asm ├── bus │ ├── pci.asm │ ├── pcie.asm │ └── xhci.asm ├── hpet.asm ├── ioapic.asm ├── lfb │ ├── fonts │ │ ├── baremetal.fnt │ │ ├── departuremono.fnt │ │ ├── ibm.fnt │ │ └── smol.fnt │ └── lfb.asm ├── net │ ├── i8254x.asm │ ├── i8257x.asm │ ├── i8259x.asm │ ├── r8169.asm │ └── virtio-net.asm ├── nvs │ ├── ahci.asm │ ├── ata.asm │ ├── nvme.asm │ └── virtio-blk.asm ├── ps2.asm ├── serial.asm └── virtio.asm ├── init.asm ├── init ├── 64.asm ├── bus.asm ├── hid.asm ├── net.asm └── nvs.asm ├── interrupt.asm ├── kernel.asm ├── syscalls.asm ├── syscalls ├── bus.asm ├── debug.asm ├── io.asm ├── net.asm ├── nvs.asm ├── smp.asm └── system.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 | *.a 3 | *.o 4 | *.elf 5 | *.sys 6 | *.bin 7 | *.asm~ 8 | *.md~ 9 | bin/* 10 | .nova/* 11 | -------------------------------------------------------------------------------- /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 | [![CI](https://github.com/ReturnInfinity/BareMetal/actions/workflows/main.yml/badge.svg)](https://github.com/ReturnInfinity/BareMetal/actions/workflows/main.yml) 2 | 3 | # BareMetal 4 | 5 | _Just enough kernel_ 6 | 7 | Official repo of the BareMetal [exokernel](http://en.wikipedia.org/wiki/Exokernel). It's written from scratch in Assembly, designed for x86-64 hardware, with no dependencies except for the virtual/physical hardware. An ARM and/or RISC-V rewrite would be considered once hardware is standardized. 8 | 9 | 10 | ## What is this? 11 | 12 | BareMetal is a _very_ lean kernel. The name is a play on the phrase "bare metal" which means to run directly on physical or virtualized hardware. BareMetal also only offers the "bare essentials" required for a working operating system. 13 | 14 | BareMetal provides basic support for symmetric multiprocessing, network, and drive access via a low-level abstraction layer. 15 | 16 | ![BareMetal Model](./doc/BareMetal-Model.png) 17 | 18 | 19 | ### Key features 20 | * **64-bit**: Make use of the extra-wide and additional registers available in 64-bit mode. 21 | * **Mono-processing, multi-core**: The system is able to execute a single program but can spread the work load amongst available CPU cores. 22 | * **Extremely tiny memory footprint**: A minimal bootable image, including boot-loader and operating system components, is currently 16K. 23 | * **Physical and virtual hardware support** with full virtualization, using [x86 hardware virtualization](https://en.wikipedia.org/wiki/X86_virtualization) whenever available (it is on most modern x86-64 CPU's). In principle BareMetal should run on any x86-64 hardware platform, even on a physical x86-64 computer, given appropriate drivers. Officially, we develop on [QEMU](http://www.qemu.org) and [VirtualBox](https://www.virtualbox.org), which means that you can run BareMetal on both Linux, Microsoft Windows, and Apple macOS. 24 | 25 | 26 | ## Try it out! 27 | 28 | See the [BareMetal-OS](https://github.com/ReturnInfinity/BareMetal-OS) repo for a full build environment. 29 | 30 | 31 | // EOF 32 | -------------------------------------------------------------------------------- /api/libBareMetal.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; Version 1.0 6 | ; ============================================================================= 7 | 8 | ; Kernel functions 9 | b_input equ 0x0000000000100010 ; Scans keyboard for input. OUT: AL = 0 if no key pressed, otherwise ASCII code 10 | b_output equ 0x0000000000100018 ; Displays a number of characters. IN: RSI = message location, RCX = number of characters 11 | 12 | b_net_tx equ 0x0000000000100020 ; Transmit a packet via a network interface. IN: RSI = Memory location where packet is stored, RCX = Length of packet, RDX = Device 13 | b_net_rx equ 0x0000000000100028 ; Polls the network interface for received packet. IN: RDI = Memory location where packet will be stored, RDX = Device. OUT: RCX = Length of packet 14 | 15 | b_nvs_read equ 0x0000000000100030 ; Read data from a non-volatile storage device. IN: RAX = Starting sector, RCX = Number of sectors to read, RDX = Device, RDI = Memory location to store data 16 | b_nvs_write equ 0x0000000000100038 ; Write data to a non-volatile storage device. IN: RAX = Starting sector, RCX = Number of sectors to write, RDX = Device, RSI = Memory location of data to store 17 | 18 | b_system equ 0x0000000000100040 ; Configure system. IN: RCX = Function, RAX = Variable 1, RDX = Variable 2. OUT: RAX = Result 19 | 20 | 21 | ; Index for b_system calls 22 | TIMECOUNTER equ 0x00 23 | FREE_MEMORY equ 0x01 24 | GET_MOUSE equ 0x02 25 | SMP_ID equ 0x10 26 | SMP_NUMCORES equ 0x11 27 | SMP_SET equ 0x12 28 | SMP_GET equ 0x13 29 | SMP_LOCK equ 0x14 30 | SMP_UNLOCK equ 0x15 31 | SMP_BUSY equ 0x16 32 | TSC equ 0x1F 33 | SCREEN_LFB_GET equ 0x20 34 | SCREEN_X_GET equ 0x21 35 | SCREEN_Y_GET equ 0x22 36 | SCREEN_PPSL_GET equ 0x23 37 | SCREEN_BPP_GET equ 0x24 38 | MAC_GET equ 0x30 39 | BUS_READ equ 0x50 40 | BUS_WRITE equ 0x51 41 | STDOUT_SET equ 0x52 42 | STDOUT_GET equ 0x53 43 | CALLBACK_TIMER equ 0x60 44 | CALLBACK_NETWORK equ 0x61 45 | CALLBACK_KEYBOARD equ 0x62 46 | CALLBACK_MOUSE equ 0x63 47 | DUMP_MEM equ 0x70 48 | DUMP_RAX equ 0x71 49 | DELAY equ 0x72 50 | RESET equ 0x7D 51 | REBOOT equ 0x7E 52 | SHUTDOWN equ 0x7F 53 | 54 | 55 | ; ============================================================================= 56 | ; EOF 57 | -------------------------------------------------------------------------------- /api/libBareMetal.c: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | // Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | // 5 | // Version 1.0 6 | // ============================================================================= 7 | 8 | 9 | #include "libBareMetal.h" 10 | 11 | 12 | // Input/Output 13 | 14 | u8 b_input(void) { 15 | u8 chr; 16 | asm volatile ("call *0x00100010" : "=a" (chr)); 17 | return chr; 18 | } 19 | 20 | void b_output(const char *str, u64 nbr) { 21 | asm volatile ("call *0x00100018" : : "S"(str), "c"(nbr)); 22 | } 23 | 24 | 25 | // Network 26 | 27 | void b_net_tx(void *mem, u64 len, u64 iid) { 28 | asm volatile ("call *0x00100020" : : "S"(mem), "c"(len), "d"(iid)); 29 | } 30 | 31 | u64 b_net_rx(void *mem, u64 iid) { 32 | u64 tlong; 33 | asm volatile ("call *0x00100028" : "=c"(tlong) : "D"(mem), "d"(iid)); 34 | return tlong; 35 | } 36 | 37 | 38 | // Non-volatile Storage 39 | 40 | u64 b_nvs_read(void *mem, u64 start, u64 num, u64 drivenum) { 41 | u64 tlong; 42 | asm volatile ("call *0x00100030" : "=c"(tlong) : "a"(start), "c"(num), "d"(drivenum), "D"(mem)); 43 | return tlong; 44 | } 45 | 46 | u64 b_nvs_write(void *mem, u64 start, u64 num, u64 drivenum) { 47 | u64 tlong = 0; 48 | asm volatile ("call *0x00100038" : "=c"(tlong) : "a"(start), "c"(num), "d"(drivenum), "S"(mem)); 49 | return tlong; 50 | } 51 | 52 | 53 | // System 54 | 55 | u64 b_system(u64 function, u64 var1, u64 var2) { 56 | u64 tlong; 57 | asm volatile ("call *0x00100040" : "=a"(tlong) : "c"(function), "a"(var1), "d"(var2)); 58 | return tlong; 59 | } 60 | 61 | 62 | // ============================================================================= 63 | // EOF 64 | -------------------------------------------------------------------------------- /api/libBareMetal.h: -------------------------------------------------------------------------------- 1 | // ============================================================================= 2 | // BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | // Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | // 5 | // Version 1.0 6 | // ============================================================================= 7 | 8 | // Headers 9 | #include // For uint*_t 10 | #ifndef _LIBBAREMETAL_H 11 | #define _LIBBAREMETAL_H 12 | 13 | // Typedefs 14 | typedef uint8_t u8; 15 | typedef uint16_t u16; 16 | typedef uint32_t u32; 17 | typedef uint64_t u64; 18 | 19 | // Input/Output 20 | u8 b_input(void); 21 | void b_output(const char *str, u64 nbr); 22 | 23 | // Network 24 | void b_net_tx(void *mem, u64 len, u64 iid); 25 | u64 b_net_rx(void *mem, u64 iid); 26 | 27 | // Non-volatile Storage 28 | u64 b_nvs_read(void *mem, u64 start, u64 num, u64 drivenum); 29 | u64 b_nvs_write(void *mem, u64 start, u64 num, u64 drivenum); 30 | 31 | // System 32 | u64 b_system(u64 function, u64 var1, u64 var2); 33 | 34 | // Index for b_config calls 35 | #define TIMECOUNTER 0x00 36 | #define FREE_MEMORY 0x01 37 | #define GET_MOUSE 0x02 38 | #define SMP_ID 0x10 39 | #define SMP_NUMCORES 0x11 40 | #define SMP_SET 0x12 41 | #define SMP_GET 0x13 42 | #define SMP_LOCK 0x14 43 | #define SMP_UNLOCK 0x15 44 | #define SMP_BUSY 0x16 45 | #define TSC 0x1F 46 | #define SCREEN_LFB_GET 0x20 47 | #define SCREEN_X_GET 0x21 48 | #define SCREEN_Y_GET 0x22 49 | #define SCREEN_PPSL_GET 0x23 50 | #define SCREEN_BPP_GET 0x24 51 | #define MAC_GET 0x30 52 | #define BUS_READ 0x50 53 | #define BUS_WRITE 0x51 54 | #define STDOUT_SET 0x52 55 | #define STDOUT_GET 0x53 56 | #define CALLBACK_TIMER 0x60 57 | #define CALLBACK_NETWORK 0x61 58 | #define CALLBACK_KEYBOARD 0x62 59 | #define CALLBACK_MOUSE 0x63 60 | #define DUMP_MEM 0x70 61 | #define DUMP_RAX 0x71 62 | #define DELAY 0x72 63 | #define RESET 0x7D 64 | #define REBOOT 0x7E 65 | #define SHUTDOWN 0x7F 66 | 67 | #endif 68 | 69 | 70 | // ============================================================================= 71 | // EOF 72 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mkdir -p bin 4 | cd src 5 | nasm kernel.asm -o ../bin/kernel.sys -l ../bin/kernel-debug.txt 6 | cd .. 7 | -------------------------------------------------------------------------------- /clean.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cd bin 4 | rm -f *.sys 5 | rm -f kernel-debug.txt 6 | cd .. 7 | -------------------------------------------------------------------------------- /doc/BareMetal-Model.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ReturnInfinity/BareMetal/c639d63f09946141ed53297d52a83b232fc200cd/doc/BareMetal-Model.png -------------------------------------------------------------------------------- /doc/Debugging.md: -------------------------------------------------------------------------------- 1 | # Debugging with GDB and QEMU 2 | 3 | This document deals with debugging in [GDB](https://www.gnu.org/software/gdb/). 4 | 5 | ## Prerequisites 6 | 7 | This document expects the reader to understand some basic fundamentals about x86-64 assembly instructions and hexadecimal notation. 8 | 9 | This document was written while using an Ubuntu 18.04 virtual machine within VirtualBox. 10 | 11 | 12 | ## Building a binary for QEMU to boot 13 | 14 | The instructions below require the multiboot.bin and pure64.sys binaries from [Pure64](https://github.com/ReturnInfinity/Pure64). 15 | 16 | A couple steps need to be completed prior to compiling Pure64! 17 | 18 | 1. Adjust the Pure64 `multiboot.asm` file to not use graphics. QEMU does not support multiboot graphics mode. 19 | 20 | Change the line ``FLAG_VIDEO equ 1<<2 ; set video mode`` to `FLAG_VIDEO equ 0<<2 ; clear video mode` 21 | 22 | 2. Adjust Pure64 to just start the kernel instead of a stage 3 loader. 23 | 24 | `$ sed -i 's/call STAGE3/jmp 0x100000/g' pure64.asm` 25 | 26 | Use the following to build boot.bin: 27 | 28 | cat multiboot.sys pure64.sys kernel.sys > boot.bin 29 | 30 | 31 | ## Debugging with GDB 32 | 33 | ### Terminal 1 34 | 35 | Set a 'jmp $' somewhere in the source code. 36 | 37 | qemu-system-x86_64 -smp 2 -m 256 -serial file:serial.log -curses -kernel ./boot.bin -s 38 | 39 | 40 | ### Terminal 2 41 | 42 | Start the GNU debugger 43 | 44 | gdb 45 | 46 | Set our parameters and connect to the local QEMU instance (You can also copy the following lines into `./.gdbinit` to have GDB execute the commands automatically on startup) 47 | 48 | set arch i386:x86-64 49 | set disassembly-flavor intel 50 | layout asm 51 | layout regs 52 | target remote localhost:1234 53 | 54 | Execution will be stopped where you put the 'jmp $' in the code. Take a look at the address of the next instruction and use it for the two lines below. 55 | 56 | break *0xXXXXXXX 57 | jump *0xXXXXXXX 58 | 59 | stepi 60 | 61 | QEMU will now be running the code directly after the `jmp $` you had inserted. After the first `stepi` command is executed you can hit enter to repeat the action and want the CPU step through the assembly code. 62 | 63 | 64 | ## Debugging with QEMU (at a known address) 65 | 66 | When the kernel is compiled a file called `kernel-debug.txt` is generated. This file can be used as a reference for opcode addresses within the kernel. Add `0x100000` to any address in the text file for the actual in-memory address. 67 | 68 | Start QEMU with the `-S` switch to start the virtual machine in a paused mode if you need to add a breakpoint somewhere in the kernel startup code. You can un-pause the execution by typing `c` into GDB after you create the breakpoint. 69 | 70 | 71 | ## The QEMU monitor 72 | 73 | QEMU has a built in monitor to allow you to query the state of the VM. 74 | 75 | `Escape+2` will switch to the QEMU monitor console and `Escape+1` will switch back to the VM. Enter `quit` on the QEMU monitor console to stop the VM. 76 | 77 | 78 | ### Debugging via QEMU monitor 79 | 80 | Some useful commands: 81 | 82 | info version QEMU version (latest as of the writing of this doc was 8.0.2) 83 | info registers the CPU registers 84 | info cpus list the CPUs 85 | info mem list the active virtual memory mappings 86 | info block block devices such as hard drives, floppy drives, cdrom 87 | info blockstats read and write statistics on block devices 88 | info pci list pci information 89 | info network list network information 90 | 91 | Dumping memory: 92 | 93 | The 'x' command dumps virtual memory and the 'xp' command dumps physical memory. It takes a format option via '/' as well as a memory address. 94 | 95 | Example: 96 | 97 | xp /8xb 0x100000 98 | 99 | Dump 8 bytes in hexadecimal format starting at address 0x100000 100 | 101 | The "count" parameter is the number of items to be dumped. 102 | The "format" can be x (hex), d (signed decimal), u (unsigned decimal), o (octal), c (char) or i (assembly instruction). 103 | The "size" parameter can be b (8 bits), h (16 bits), w (32 bits) or g (64 bits). 104 | 105 | 106 | ## GDB instructions 107 | 108 | Dump some memory 109 | 110 | x 0xXXXXX 111 | 112 | 113 | ## Capturing QEMU network traffic 114 | 115 | Add the following to the network definition 116 | 117 | -net dump,file=net.pcap 118 | 119 | 120 | ## Connecting two QEMU VMs via network 121 | 122 | VM 1 123 | 124 | -net socket,listen=:30000 125 | 126 | VM 2 127 | 128 | -net socket,connect=:30000 129 | 130 | 131 | // EOF 132 | -------------------------------------------------------------------------------- /doc/Kernel API.md: -------------------------------------------------------------------------------- 1 | # BareMetal x86-64 API 2 | 3 | Version 1.1 - September 8, 2024 4 | 5 | 6 | ### Notes 7 | 8 | This document details the API calls built into the BareMetal exokernel. 9 | 10 | 11 | ### Contents 12 | 13 | 1. Input/Output 14 | - b\_input 15 | - b\_output 16 | 2. Network 17 | - b\_net\_tx 18 | - b\_net\_rx 19 | 3. Non-volatile Storage 20 | - b\_nvs\_read 21 | - b\_nvs\_write 22 | 4. Misc 23 | - b\_system 24 | 25 | 26 | ## Input/Output 27 | 28 | 29 | ### b\_input 30 | 31 | Scans for input from keyboard or serial. 32 | 33 | Assembly Registers: 34 | 35 | IN: Nothing 36 | OUT: AL = 0 if no key pressed, otherwise ASCII code, other regs preserved 37 | All other registers preserved 38 | 39 | Assembly Example: 40 | 41 | call [b_input] 42 | mov byte [KeyChar], al 43 | ... 44 | KeyChar: db 0 45 | 46 | C Example: 47 | 48 | char KeyChar; 49 | KeyChar = b_input(); 50 | if (KeyChar == 'a') 51 | ... 52 | 53 | 54 | ### b\_output 55 | 56 | Output a number of characters via the standard output method. 57 | 58 | Assembly Registers: 59 | 60 | IN: RSI = message location 61 | RCX = number of characters to output 62 | OUT: All registers preserved 63 | 64 | Assembly Example: 65 | 66 | mov rsi, Message 67 | mov rcx, 4 68 | call [b_output] ; Only output the word 'This' 69 | ... 70 | Message: db 'This is a test', 0 71 | 72 | C Example: 73 | 74 | b_output("This is a test", 4); // Output 'This' 75 | 76 | char Message[] = "Hello, world!"; 77 | b_output(Message, 5); // Output 'Hello' 78 | 79 | 80 | ## Network 81 | 82 | 83 | ### b\_net\_tx 84 | 85 | Transmit data via a network interface. 86 | 87 | Assembly Registers: 88 | 89 | IN: RSI = memory location of packet 90 | RCX = length of packet 91 | RDX = Interface ID 92 | OUT: All registers preserved 93 | 94 | Assembly Example: 95 | 96 | mov rsi, Packet 97 | mov rcx, 1500 98 | mod rdx, 0 99 | call [b_net_tx] 100 | ... 101 | Packet: 102 | Packet_Dest: db 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF ; Broadcast 103 | Packet_Src: db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 104 | Packet_Type: dw 0xABBA 105 | Packet_Data: db 'This is a test', 0 106 | 107 | The packet must contain a proper 14-byte header. 108 | 109 | 110 | ### b\_net\_rx 111 | 112 | Receive data via a network interface. 113 | 114 | Assembly Registers: 115 | 116 | IN: RDI = memory location to store packet 117 | RDX = Interface ID 118 | OUT: RCX = length of packet, 0 if nothing to receive 119 | 120 | Assembly Example: 121 | 122 | mov rdi, Packet 123 | mov rdx, 0 124 | call [b_net_rx] 125 | ... 126 | Packet: times 1518 db 0 127 | 128 | Note: BareMetal does not keep a buffer of received packets. This means that the OS will overwrite the last packet as soon as a new one is received. Continuously polling the network by checking `os_net_rx` often, is possible, but this is not ideal. BareMetal allows for a network interrupt callback handler to be run whenever a packet is received. With a callback, a program will always be aware of when a packet is received. 129 | 130 | 131 | ## Non-volatile Storage 132 | 133 | BareMetal uses 4096 byte sectors for all device access. Device sectors start at 0. 134 | 135 | 136 | ### b\_nvs\_read 137 | 138 | Read a number of sectors from a non-volatile storage device to memory. 139 | 140 | Assembly Registers: 141 | 142 | IN: RAX = Starting sector # 143 | RCX = Number of sectors to read 144 | RDX = Device # 145 | RDI = Destination memory address 146 | OUT: RCX = Number of sectors read 147 | All other registers preserved 148 | 149 | Assembly Example: 150 | 151 | mov rax, 0 ; Read sector 0 152 | mov rcx, 1 ; Read one sector 153 | mov rdx, 0 ; Read from device 0 154 | mov rdi, buffer ; Read device data to this memory address 155 | call [b_nvs_read] 156 | 157 | 158 | ### b\_nvs\_write 159 | 160 | Write a number of sectors from memory to a non-volatile storage device. 161 | 162 | Assembly Registers: 163 | 164 | IN: RAX = Starting sector # 165 | RCX = Number of sectors to write 166 | RDX = Device # 167 | RSI = Source memory address 168 | OUT: RCX = Number of sectors written 169 | All other registers preserved 170 | 171 | Assembly Example: 172 | 173 | mov rax, 0 ; Write to sector 0 174 | mov rcx, 1 ; Write one sector 175 | mov rdx, 0 ; Write to device 0 176 | mov rsi, buffer ; Write the contents from this memory address to the device 177 | call [b_nvs_write] 178 | 179 | 180 | ## Misc 181 | 182 | 183 | ### b\_system 184 | 185 | Call system functions 186 | 187 | Assembly Registers: 188 | 189 | IN: RCX = Function 190 | RAX = Variable 1 191 | RDX = Variable 2 192 | OUT: RAX = Result 1 193 | 194 | Currently the following functions are supported: 195 | 196 | #### TIMECOUNTER 197 | 198 | Read the HPET Main Timer. 199 | 200 | IN: Nothing 201 | OUT: RAX = Number of HPET ticks since kernel start 202 | All other registers preserved 203 | 204 | #### FREE_MEMORY 205 | 206 | Return the amount of free memory. 207 | 208 | IN: Nothing 209 | OUT: RAX = Free memory in Mebibytes (MiB) 210 | All other registers preserved 211 | 212 | #### MOUSE 213 | 214 | Return details of the mouse 215 | 216 | IN: Nothing 217 | OUT: RAX = Mouse details 218 | All other registers preserved 219 | Bits 63:48 - Padding 220 | Bits 47:32 - Y position 221 | Bits 31:16 - X position 222 | Bits 15:0 - Buttons pressed 223 | 224 | #### SMP_ID 225 | 226 | Returns the APIC ID of the CPU that ran this function. 227 | 228 | IN: Nothing 229 | OUT: RAX = CPU's APIC ID number 230 | All other registers preserved 231 | 232 | #### SMP_NUMCORES 233 | 234 | Returns the total number of CPU cores. 235 | 236 | IN: Nothing 237 | OUT: RAX = Number of CPU cores 238 | All other registers preserved 239 | 240 | #### SMP_SET 241 | 242 | Set a specific CPU to run code. 243 | 244 | IN: RAX = Code address 245 | RDX = CPU APIC ID 246 | OUT: RAX = 0 on error 247 | Note: Code address must be 16-byte aligned 248 | 249 | #### SMP_GET 250 | 251 | Returns a CPU code address and flags. 252 | 253 | IN: Nothing 254 | OUT: RAX = Code address (bits 63:4) and flags (bits 3:0) 255 | 256 | #### SMP_LOCK 257 | 258 | Attempt to lock a mutex. 259 | 260 | IN: RAX = Address of lock variable 261 | OUT: Nothing. All registers preserved 262 | 263 | #### SMP_UNLOCK 264 | 265 | Unlock a mutex. 266 | 267 | IN: RAX = Address of lock variable 268 | OUT: Nothing. All registers preserved 269 | 270 | #### SMP_BUSY 271 | 272 | Check if CPU cores are busy. 273 | 274 | IN: Nothing 275 | OUT: RAX = 1 if CPU cores are busy, 0 if not 276 | All other registers preserved 277 | Note: This ignores the core it is running on 278 | 279 | #### SCREEN_LFB_GET 280 | 281 | Return the address of the linear frame buffer. 282 | 283 | IN: Nothing 284 | OUT: RAX = Address of linear frame buffer 285 | All other registers preserved 286 | 287 | #### SCREEN_X_GET 288 | 289 | Return the amount of pixels along the horizontal. 290 | 291 | IN: Nothing 292 | OUT: RAX = Number of pixels 293 | All other registers preserved 294 | 295 | #### SCREEN_Y_GET 296 | 297 | Return the amount of pixels along the vertical. 298 | 299 | IN: Nothing 300 | OUT: RAX = Number of pixels 301 | All other registers preserved 302 | 303 | #### SCREEN_PPSL_GET 304 | 305 | Return the number of pixels per scan line. This may be more than `SCREEN_X` due to memory alignment requirements. 306 | 307 | IN: Nothing 308 | OUT: RAX = Number of pixels per scan line 309 | All other registers preserved 310 | 311 | #### SCREEN_BPP_GET 312 | 313 | Return the number of bits per pixel. This should return 32. 314 | 315 | IN: Nothing 316 | OUT: RAX = Bits per pixel 317 | All other registers preserved 318 | 319 | #### MAC_GET 320 | 321 | Return the MAC address of the network device. 322 | 323 | IN: Nothing 324 | OUT: RAX = MAC address (bits 0-47) 325 | All other registers preserved 326 | 327 | #### BUS_READ 328 | 329 | #### BUS_WRITE 330 | 331 | #### STDOUT_SET 332 | 333 | #### STDOUT_GET 334 | 335 | #### CALLBACK_TIMER 336 | 337 | #### CALLBACK_NETWORK 338 | 339 | #### CALLBACK_KEYBOARD 340 | 341 | #### CALLBACK_MOUSE 342 | 343 | Set a callback for execution on mouse activity. 344 | 345 | IN: Nothing 346 | OUT: RAX = Address of callback 347 | All other registers preserved 348 | 349 | Set a callback of 0x0 to disable it. 350 | 351 | #### DUMP_MEM 352 | 353 | Dump contents of memory 354 | 355 | IN: RAX: The start of the memory to dump 356 | RDX: Number of bytes to dump 357 | OUT: All registers preserved 358 | 359 | #### DUMP_RAX 360 | 361 | Dump RAX register in Hex 362 | 363 | IN: RAX: The content that gets dump 364 | OUT: All registers preserved 365 | 366 | #### DELAY 367 | 368 | Delay by X microseconds 369 | 370 | IN: RAX = Time microseconds 371 | OUT: All registers preserved 372 | 373 | #### RESET 374 | 375 | Reset all other CPU cores 376 | 377 | IN: Nothing 378 | OUT: All registers preserved (on the caller CPU) 379 | 380 | #### REBOOT 381 | 382 | Reboot the system 383 | 384 | IN: Nothing 385 | OUT: All registers lost 386 | 387 | #### SHUTDOWN 388 | 389 | Shut down the system 390 | 391 | IN: Nothing 392 | OUT: All registers lost 393 | 394 | 395 | // EOF 396 | -------------------------------------------------------------------------------- /doc/Supported Hardware.md: -------------------------------------------------------------------------------- 1 | # Supported Hardware 2 | With links to documentation 3 | 4 | 5 | ## CPU 6 | 7 | 8 | ### Intel 9 | 10 | * [Software Developer Manuals](http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html) 11 | 12 | 13 | ### AMD 14 | 15 | * [Developer Guides, Manuals & ISA Documents](http://developer.amd.com/resources/developer-guides-manuals/) 16 | 17 | 18 | ## Bus 19 | 20 | 21 | ### PCI Express (PCIe) 22 | 23 | * [PCI Express](https://wiki.osdev.org/PCI_Express) - [PCI Express Base Specification Revision 4.0 Version 0.3](https://astralvx.com/storage/2020/11/PCI_Express_Base_4.0_Rev0.3_February19-2014.pdf) 24 | 25 | 26 | ### PCI 27 | 28 | * [PCI](https://wiki.osdev.org/PCI) 29 | 30 | 31 | ### XHCI (USB 3) 32 | 33 | * [OSDev.org XHCI Article](https://wiki.osdev.org/EXtensible_Host_Controller_Interface) 34 | * [Requirements Specification](https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/extensible-host-controler-interface-usb-xhci.pdf) - Revision 1.2 - May 2019 35 | * [USB: The Universal Serial Bus](https://www.fysnet.net/the_universal_serial_bus.htm) - Book by Benjamin David Lunt 36 | 37 | 38 | ## Network 39 | 40 | 41 | ### Virtio-Net 42 | 43 | * [Specs](https://docs.oasis-open.org/virtio/virtio/v1.2/virtio-v1.2.pdf) 44 | * [Legacy specs](http://ozlabs.org/~rusty/virtio-spec/virtio-0.9.5.pdf) 45 | 46 | ### Intel 8254x PCI (e1000) 47 | 48 | * Supports the Intel 8254x Gigabit network interfaces. 49 | * [PCI Software Developer's Manual](https://www.intel.com/content/dam/doc/manual/pci-pci-x-family-gbe-controllers-software-dev-manual.pdf) 50 | 51 | ### Intel 8257x PCIe (e1000e) 52 | 53 | * Supports the Intel 8257x Gigabit network interfaces. 54 | * [PCIe Software Developer's Manual](https://www.intel.com/content/dam/www/public/us/en/documents/manuals/pcie-gbe-controllers-open-source-manual.pdf) 55 | 56 | ### Intel 8259x (ixbge) 57 | 58 | * Supports the Intel 8259x/X540/X550 10 Gigabit network interfaces. 59 | * [Datasheet](https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/82599-10-gbe-controller-datasheet.pdf) 60 | 61 | ### Realtek 8169 (r8169) 62 | 63 | * Supports the Realtek 8168, 8169, 8110, and 8111 Gigabit network interfaces. 64 | * [Datasheet](http://realtek.info/pdf/rtl8169s.pdf) 65 | 66 | 67 | ## Non-volatile Storage 68 | 69 | 70 | ### Virtio-Block 71 | 72 | * [Specs](https://docs.oasis-open.org/virtio/virtio/v1.2/virtio-v1.2.pdf) 73 | * [Legacy specs](http://ozlabs.org/~rusty/virtio-spec/virtio-0.9.5.pdf) 74 | 75 | 76 | ### NVMe 77 | 78 | * [Base Specification](https://nvmexpress.org/wp-content/uploads/NVM-Express-Base-Specification-Revision-2.1-2024.08.05-Ratified.pdf) - Revision 2.1 - August 5th, 2024 79 | 80 | 81 | ### AHCI (Serial ATA) 82 | 83 | * [ATA/ATAPI Command Set](http://www.t13.org/documents/uploadeddocuments/docs2006/d1699r3f-ata8-acs.pdf) - from 2006 but still valid 84 | * [ATA Command Set](http://www.t13.org/documents/UploadedDocuments/docs2016/di529r14-ATAATAPI_Command_Set_-_4.pdf) - from 2016 85 | * [Official Intel Specs](http://www.intel.com/content/www/us/en/io/serial-ata/ahci.html) - latest version 1.3.1 86 | * [OSDev.org AHCI article](https://wiki.osdev.org/AHCI) 87 | 88 | 89 | ### ATA 90 | 91 | * [OSDev.org ATA article](https://wiki.osdev.org/ATA_PIO_Mode) 92 | 93 | 94 | ## Video 95 | 96 | 97 | ### Linear Frame Buffer 98 | 99 | * [UEFI GOP](https://wiki.osdev.org/GOP) - UEFI Graphics Output Protocol 100 | * [BIOS VBE](https://wiki.osdev.org/VESA_Video_Modes) - BIOS VESA VBE 101 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # x86-64 2 | 3 | Targeting x86-64 based systems (Intel and AMD) 4 | 5 | 6 | // EOF 7 | -------------------------------------------------------------------------------- /src/drivers.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; Driver Includes 6 | ; ============================================================================= 7 | 8 | 9 | ; Internal 10 | %include "drivers/apic.asm" 11 | %include "drivers/hpet.asm" 12 | %include "drivers/ioapic.asm" 13 | %include "drivers/ps2.asm" 14 | %include "drivers/serial.asm" 15 | %include "drivers/virtio.asm" 16 | 17 | ; Bus 18 | %include "drivers/bus/pcie.asm" 19 | %include "drivers/bus/pci.asm" 20 | %include "drivers/bus/xhci.asm" 21 | 22 | ; Non-volatile Storage 23 | %include "drivers/nvs/nvme.asm" 24 | %include "drivers/nvs/ahci.asm" 25 | %include "drivers/nvs/virtio-blk.asm" 26 | %include "drivers/nvs/ata.asm" 27 | 28 | ; Network 29 | %include "drivers/net/i8254x.asm" 30 | %include "drivers/net/i8257x.asm" 31 | %include "drivers/net/i8259x.asm" 32 | %include "drivers/net/r8169.asm" 33 | %include "drivers/net/virtio-net.asm" 34 | 35 | ; Video 36 | %include "drivers/lfb/lfb.asm" 37 | 38 | NIC_DeviceVendor_ID: ; The supported list of NICs 39 | 40 | ; Virtio 41 | dw 0x1AF4 ; Driver ID 42 | dw 0x1AF4 ; Vendor ID 43 | dw 0x1000 ; Device ID - legacy 44 | dw 0x1041 ; Device ID - v1.0 45 | dw 0x0000 46 | 47 | ; Intel 8254x Gigabit Ethernet 48 | dw 0x8254 ; Driver ID 49 | dw 0x8086 ; Vendor ID 50 | dw 0x1000 ; 82542 (Fiber) 51 | dw 0x1001 ; 82543GC (Fiber) 52 | dw 0x1004 ; 82543GC (Copper) 53 | dw 0x1008 ; 82544EI (Copper) 54 | dw 0x1009 ; 82544EI (Fiber) 55 | dw 0x100A ; 82540EM 56 | dw 0x100C ; 82544GC (Copper) 57 | dw 0x100D ; 82544GC (LOM) 58 | dw 0x100E ; 82540EM - QEMU e1000 59 | dw 0x100F ; 82545EM (Copper) 60 | dw 0x1010 ; 82546EB (Copper) 61 | dw 0x1011 ; 82545EM (Fiber) 62 | dw 0x1012 ; 82546EB (Fiber) 63 | dw 0x1013 ; 82541EI 64 | dw 0x1014 ; 82541ER 65 | dw 0x1015 ; 82540EM (LOM) 66 | dw 0x1016 ; 82540EP (Mobile) 67 | dw 0x1017 ; 82540EP 68 | dw 0x1018 ; 82541EI 69 | dw 0x1019 ; 82547EI 70 | dw 0x101A ; 82547EI (Mobile) 71 | dw 0x101D ; 82546EB 72 | dw 0x101E ; 82540EP (Mobile) 73 | dw 0x1026 ; 82545GM 74 | dw 0x1027 ; 82545GM 75 | dw 0x1028 ; 82545GM 76 | dw 0x1075 ; 82547GI 77 | dw 0x1076 ; 82541GI 78 | dw 0x1077 ; 82541GI 79 | dw 0x1078 ; 82541ER 80 | dw 0x1079 ; 82546GB 81 | dw 0x107A ; 82546GB 82 | dw 0x107B ; 82546GB 83 | dw 0x107C ; 82541PI 84 | dw 0x108A ; 82546GB 85 | dw 0x1099 ; 82546GB (Copper) 86 | dw 0x10B5 ; 82546GB (Copper) 87 | dw 0x0000 88 | 89 | ; Intel 8257x Gigabit Ethernet 90 | dw 0x8257 ; Driver ID 91 | dw 0x8086 ; Vendor ID 92 | dw 0x105E ; 82571EB/82571GB 93 | dw 0x105F ; 82571EB 94 | dw 0x1060 ; 82571EB 95 | dw 0x1075 ; 82547GI 96 | dw 0x107D ; 82572EI (Copper) 97 | dw 0x107E ; 82572EI (Fiber) 98 | dw 0x107F ; 82572EI 99 | dw 0x108B ; 82573V (Copper) 100 | dw 0x108C ; 82573E (Copper) 101 | dw 0x109A ; 82573L 102 | dw 0x10A4 ; 82571EB 103 | dw 0x10A5 ; 82571EB (Fiber) 104 | dw 0x10B9 ; 82572EI (Copper) 105 | dw 0x10BC ; 82571EB/82571GB (Copper) 106 | dw 0x10C9 ; 82576 107 | dw 0x10D3 ; 82574L - QEMU e1000e 108 | dw 0x10D6 ; 82575GB 109 | dw 0x10E2 ; 82575GB 110 | dw 0x10E6 ; 82576 111 | dw 0x10E7 ; 82576 112 | dw 0x10E8 ; 82576 113 | dw 0x10EA ; 82577LM 114 | dw 0x10EB ; 82577LC 115 | dw 0x10EF ; 82578DM 116 | dw 0x10F0 ; 82578DC 117 | dw 0x10F6 ; 82574L 118 | dw 0x153A ; I217-LM 119 | dw 0x153B ; I217-V 120 | dw 0x0000 121 | 122 | ; Intel 8259x/X540/X550 10 Gigabit Ethernet 123 | dw 0x8259 ; Driver ID 124 | dw 0x8086 ; Vendor ID 125 | dw 0x10FB ; 82599ES (SFI/SFP+) 126 | dw 0x1528 ; X540-AT2 127 | dw 0x1560 ; X540 128 | dw 0x1572 ; X710 (SFP+) 129 | dw 0x0000 130 | 131 | ; Realtek 816x/811x Gigabit Ethernet 132 | dw 0x8169 ; Driver ID 133 | dw 0x10EC ; Vendor ID 134 | dw 0x8161 ; 8111/8168/8411 PCI Express 135 | dw 0x8167 ; 8110SC/8169SC 136 | dw 0x8168 ; 8111/8168/8211/8411 PCI Express 137 | dw 0x8169 ; 8169 138 | dw 0x0000 139 | 140 | ; End of list 141 | dw 0x0000 142 | dw 0x0000 143 | 144 | 145 | ; ============================================================================= 146 | ; EOF 147 | -------------------------------------------------------------------------------- /src/drivers/apic.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; APIC Functions 6 | ; ============================================================================= 7 | 8 | 9 | ; ----------------------------------------------------------------------------- 10 | ; os_apic_init -- Initialize the APIC 11 | ; IN: Nothing 12 | ; OUT: Nothing 13 | ; All other registers preserved 14 | os_apic_init: 15 | mov ecx, APIC_VER 16 | call os_apic_read 17 | mov [os_apic_ver], eax 18 | ret 19 | ; ----------------------------------------------------------------------------- 20 | 21 | 22 | ; ----------------------------------------------------------------------------- 23 | ; os_apic_read -- Read from a register in the APIC 24 | ; IN: ECX = Register to read 25 | ; OUT: EAX = Register value 26 | ; All other registers preserved 27 | os_apic_read: 28 | mov rax, [os_LocalAPICAddress] 29 | mov eax, [rax + rcx] 30 | ret 31 | ; ----------------------------------------------------------------------------- 32 | 33 | 34 | ; ----------------------------------------------------------------------------- 35 | ; os_apic_write -- Write to a register in the APIC 36 | ; IN: ECX = Register to write 37 | ; EAX = Value to write 38 | ; OUT: All registers preserved 39 | os_apic_write: 40 | push rcx 41 | add rcx, [os_LocalAPICAddress] 42 | mov [rcx], eax 43 | pop rcx 44 | ret 45 | ; ----------------------------------------------------------------------------- 46 | 47 | 48 | ; Register list 49 | ; 0x000 - 0x010 are Reserved 50 | APIC_ID equ 0x020 ; ID Register 51 | APIC_VER equ 0x030 ; Version Register 52 | ; 0x040 - 0x070 are Reserved 53 | APIC_TPR equ 0x080 ; Task Priority Register 54 | APIC_APR equ 0x090 ; Arbitration Priority Register 55 | APIC_PPR equ 0x0A0 ; Processor Priority Register 56 | APIC_EOI equ 0x0B0 ; End Of Interrupt 57 | APIC_RRD equ 0x0C0 ; Remote Read Register 58 | APIC_LDR equ 0x0D0 ; Logical Destination Register 59 | APIC_DFR equ 0x0E0 ; Destination Format Register 60 | APIC_SPURIOUS equ 0x0F0 ; Spurious Interrupt Vector Register 61 | APIC_ISR equ 0x100 ; In-Service Register (Starting Address) 62 | APIC_TMR equ 0x180 ; Trigger Mode Register (Starting Address) 63 | APIC_IRR equ 0x200 ; Interrupt Request Register (Starting Address) 64 | APIC_ESR equ 0x280 ; Error Status Register 65 | ; 0x290 - 0x2E0 are Reserved 66 | APIC_ICRL equ 0x300 ; Interrupt Command Register (low 32 bits) 67 | APIC_ICRH equ 0x310 ; Interrupt Command Register (high 32 bits) 68 | APIC_LVT_TMR equ 0x320 ; LVT Timer Register 69 | APIC_LVT_TSR equ 0x330 ; LVT Thermal Sensor Register 70 | APIC_LVT_PERF equ 0x340 ; LVT Performance Monitoring Counters Register 71 | APIC_LVT_LINT0 equ 0x350 ; LVT LINT0 Register 72 | APIC_LVT_LINT1 equ 0x360 ; LVT LINT1 Register 73 | APIC_LVT_ERR equ 0x370 ; LVT Error Register 74 | APIC_TMRINITCNT equ 0x380 ; Initial Count Register (for Timer) 75 | APIC_TMRCURRCNT equ 0x390 ; Current Count Register (for Timer) 76 | ; 0x3A0 - 0x3D0 are Reserved 77 | APIC_TMRDIV equ 0x3E0 ; Divide Configuration Register (for Timer) 78 | ; 0x3F0 is Reserved 79 | 80 | 81 | ; ============================================================================= 82 | ; EOF 83 | -------------------------------------------------------------------------------- /src/drivers/bus/pci.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; PCI Functions 6 | ; ============================================================================= 7 | 8 | 9 | ; See syscalls/bus.asm for description on RDX format 10 | 11 | 12 | ; ----------------------------------------------------------------------------- 13 | ; os_pci_read -- Read from a register on a PCI device 14 | ; IN: RDX = Register to read from 15 | ; OUT: EAX = Register value that was read 16 | ; All other registers preserved 17 | os_pci_read: 18 | push rdx 19 | 20 | call os_pci_convert ; Convert RDX to a PCI Address 21 | 22 | mov dx, PCI_CONFIG_ADDRESS 23 | out dx, eax 24 | mov dx, PCI_CONFIG_DATA 25 | in eax, dx 26 | 27 | pop rdx 28 | ret 29 | ; ----------------------------------------------------------------------------- 30 | 31 | 32 | ; ----------------------------------------------------------------------------- 33 | ; os_pci_write -- Write to a register on a PCI device 34 | ; IN: RDX = Register to write to 35 | ; EAX = Register value to be written 36 | ; OUT: Nothing, all registers preserved 37 | os_pci_write: 38 | push rdx 39 | push rax ; Save the value to be written 40 | 41 | call os_pci_convert ; Convert RDX to a PCI Address 42 | 43 | mov dx, PCI_CONFIG_ADDRESS 44 | out dx, eax 45 | pop rax ; Restore the value and write it 46 | mov dx, PCI_CONFIG_DATA 47 | out dx, eax 48 | 49 | pop rdx 50 | ret 51 | ; ----------------------------------------------------------------------------- 52 | 53 | 54 | ; ----------------------------------------------------------------------------- 55 | ; os_pci_convert -- Convert the value in EDX to what is expected for PCI access 56 | ; IN: EDX = Register 57 | ; EAX = PCI Address 58 | ; OUT: Nothing, all registers preserved 59 | os_pci_convert: 60 | mov eax, edx ; Save EDX for the register value later on 61 | shl al, 2 ; Shift PCI register ID left two bits 62 | shr edx, 8 ; Shift Bus/Device/Function 63 | mov dl, al ; Restore register value 64 | and edx, 0x00FFFFFC ; Clear bits 31 - 24, 1 - 0 65 | or edx, 0x80000000 ; Set bit 31 66 | mov eax, edx ; We need dx so save value to EAX for use 67 | ret 68 | ; ----------------------------------------------------------------------------- 69 | 70 | 71 | ; Configuration Mechanism One has two IO port rages associated with it. 72 | ; The address port (0xCF8-0xCFB) and the data port (0xCFC-0xCFF). 73 | ; A configuration cycle consists of writing to the address port to specify which device and register you want to access and then reading or writing the data to the data port. 74 | 75 | PCI_CONFIG_ADDRESS EQU 0x0CF8 76 | PCI_CONFIG_DATA EQU 0x0CFC 77 | 78 | ; Address dd 10000000000000000000000000000000b 79 | ; /\ /\ /\ /\ /\ /\ 80 | ; E Res Bus Dev F Reg 0 81 | ; Bits 82 | ; 31 Enable bit = set to 1 83 | ; 30 - 24 Reserved = set to 0 84 | ; 23 - 16 Bus number = 256 options 85 | ; 15 - 11 Device/Slot number = 32 options 86 | ; 10 - 8 Function number = 8 options 87 | ; 7 - 2 Register number = 64 options, 64 x 4 bytes = 256 bytes worth of accessible registers 88 | ; 1 - 0 Set to 0 89 | 90 | 91 | ; ============================================================================= 92 | ; EOF 93 | -------------------------------------------------------------------------------- /src/drivers/bus/pcie.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; PCI Express Functions 6 | ; ============================================================================= 7 | 8 | 9 | ; See syscalls/bus.asm for description on RDX format 10 | ; 11 | ; os_pcie_convert uses the PCIe Table to find the correct memory address 12 | ; 13 | ; ┌───────────────────────────────────────────────────────────────────┐ 14 | ; │ PCIe Table Format │ 15 | ; ├───┬───────────────────────────────────────────────────────────────┤ 16 | ; │0x0│ PCIe Base Memory │ 17 | ; ├───┼───────────────┬───────┬───────┬───────────────────────────────┤ 18 | ; │0x8│ Group Segment │ Start │ End │ 0 │ 19 | ; └───┴───────────────┴───────┴───────┴───────────────────────────────┘ 20 | ; 21 | ; Bytes 0-7 Base memory address for a PCIe host bridge 22 | ; Bytes 8-9 This PCIe Group Segment Number for this host bridge 23 | ; Byte 10 Start PCI bus number decoded by this host bridge 24 | ; Byte 11 End PCI bus number decoded by this host bridge 25 | ; Bytes 12-15 0 26 | ; 27 | ; The last record will contain all 0xFF's 28 | 29 | 30 | ; ----------------------------------------------------------------------------- 31 | ; os_pcie_read -- Read from a register on a PCIe device 32 | ; IN: RDX = Register to read from 33 | ; OUT: EAX = Register value that was read 34 | ; All other registers preserved 35 | os_pcie_read: 36 | push rdx 37 | 38 | call os_pcie_convert ; Convert RDX to memory address 39 | mov eax, [rdx] ; Load register value 40 | 41 | pop rdx 42 | ret 43 | ; ----------------------------------------------------------------------------- 44 | 45 | 46 | ; ----------------------------------------------------------------------------- 47 | ; os_pcie_write -- Write to a register on a PCIe device 48 | ; IN: RDX = Register to write to 49 | ; EAX = Register value to be written 50 | ; OUT: Nothing, all registers preserved 51 | os_pcie_write: 52 | push rdx 53 | 54 | call os_pcie_convert ; Convert RDX to memory address 55 | mov [rdx], eax ; Store register value 56 | 57 | pop rdx 58 | ret 59 | ; ----------------------------------------------------------------------------- 60 | 61 | 62 | ; ----------------------------------------------------------------------------- 63 | ; os_pcie_convert -- Convert RDX to the memory address of the register 64 | ; IN: RDX = Register to read/write 65 | ; OUT: RDX = Memory address of register 66 | ; All other registers preserved 67 | os_pcie_convert: 68 | push rsi 69 | push rax 70 | 71 | ; Check the submitted Segment Group against known ones 72 | mov rsi, 0x5408 ; Start of the PCIe info, offset to PCIe Segment Group at 0x8 73 | ror rdx, 32 ; Rotate PCIe Segment Group to DX 74 | os_pcie_convert_check_segment: 75 | mov ax, [rsi] ; Load a known PCIe Segment Group 76 | cmp ax, dx ; Compare the known value to what was provided 77 | je os_pcie_convert_valid 78 | cmp ax, 0xFFFF ; Compare to the end of the list value 79 | je os_pcie_convert_invalid 80 | add rsi, 16 ; Increment to the next record 81 | jmp os_pcie_convert_check_segment 82 | 83 | os_pcie_convert_valid: 84 | sub rsi, 8 ; Set RSI to the location of the memory address at 0x0 85 | mov rsi, [rsi] ; Load the memory address to RSI 86 | rol rdx, 32 ; Rotate PCIe Segment Group back to upper bits 87 | ; Add offset to the correct device/function/register 88 | push rdx ; Save RDX for the register 89 | and edx, 0xFFFF0000 ; Isolate the device/function 90 | shr edx, 4 ; Quick divide by 16 91 | add rsi, rdx ; RSI now points to the start of the 4KB register memory 92 | pop rdx ; Low 10 bits of RDX is the register 93 | and edx, 0x000003FF ; Only keep the low 10 bits 94 | shl edx, 2 ; Quick multiply by 4 95 | add rsi, rdx ; Add offset for the register 96 | mov rdx, rsi ; Store final memory address in RDX 97 | 98 | pop rax 99 | pop rsi 100 | ret 101 | 102 | os_pcie_convert_invalid: 103 | xor edx, edx 104 | not rdx ; Set RDX to 0xFFFFFFFFFFFFFFFF 105 | pop rax 106 | pop rsi 107 | ret 108 | ; ----------------------------------------------------------------------------- 109 | 110 | 111 | ; ============================================================================= 112 | ; EOF 113 | -------------------------------------------------------------------------------- /src/drivers/hpet.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; HPET Functions 6 | ; ============================================================================= 7 | 8 | 9 | ; Note: There are 1,000,000 microseconds in a second 10 | ; There are 1,000 milliseconds in a second 11 | 12 | 13 | ; ----------------------------------------------------------------------------- 14 | ; os_hpet_init -- Initialize the High Precision Event Timer 15 | ; IN: Nothing 16 | ; OUT: Nothing 17 | ; All other registers preserved 18 | os_hpet_init: 19 | ; Pure64 has already initialized the HPET (if it existed) 20 | 21 | ; Verify there is a valid HPET address 22 | mov rax, [os_HPET_Address] 23 | jz os_hpet_init_error 24 | 25 | ; Gather clock period 26 | mov ecx, HPET_GEN_CAP 27 | call os_hpet_read ; Get HPET General Capabilities and ID Register 28 | shr rax, 32 ; Shift COUNTER_CLK_PERIOD (femtoseconds per tick) into EAX 29 | mov [os_HPET_Frequency], eax 30 | 31 | ; Set flag that HPET was enabled 32 | or qword [os_SysConfEn], 1 << 4 33 | 34 | os_hpet_init_error: 35 | ret 36 | ; ----------------------------------------------------------------------------- 37 | 38 | 39 | ; ----------------------------------------------------------------------------- 40 | ; os_hpet_us -- Get current microseconds (us) since HPET started 41 | ; IN: Nothing 42 | ; OUT: RAX = Time in microseconds since start 43 | os_hpet_us: 44 | push rdx 45 | push rcx 46 | 47 | xor edx, edx 48 | 49 | ; Read Main Counter 50 | mov ecx, HPET_MAIN_COUNTER 51 | call os_hpet_read ; Read HPET Main Counter to RAX 52 | 53 | ; Multiply by Main Counter Clock Period 54 | mov ecx, [os_HPET_Frequency] 55 | mul rcx ; RDX:RAX *= RCX 56 | 57 | ; Divide by # of femtoseconds in a microsecond 58 | mov rcx, 1000000000 59 | div rcx ; RAX = RDX:RAX / RCX 60 | 61 | pop rcx 62 | pop rdx 63 | ret 64 | ; ----------------------------------------------------------------------------- 65 | 66 | 67 | ; ----------------------------------------------------------------------------- 68 | ; os_hpet_read -- Read from a register in the High Precision Event Timer 69 | ; IN: ECX = Register to read 70 | ; OUT: RAX = Register value 71 | ; All other registers preserved 72 | os_hpet_read: 73 | mov rax, [os_HPET_Address] 74 | mov rax, [rax + rcx] 75 | ret 76 | ; ----------------------------------------------------------------------------- 77 | 78 | 79 | ; ----------------------------------------------------------------------------- 80 | ; os_hpet_write -- Write to a register in the High Precision Event Timer 81 | ; IN: ECX = Register to write 82 | ; RAX = Value to write 83 | ; OUT: All registers preserved 84 | os_hpet_write: 85 | push rcx 86 | add rcx, [os_HPET_Address] 87 | mov [rcx], rax 88 | pop rcx 89 | ret 90 | ; ----------------------------------------------------------------------------- 91 | 92 | 93 | ; Register list (64-bits wide) 94 | HPET_GEN_CAP equ 0x000 ; COUNTER_CLK_PERIOD (63:32), LEG_RT_CAP (15), COUNT_SIZE_CAP (13), NUM_TIM_CAP (12:8) 95 | ; 0x008 - 0x00F are Reserved 96 | HPET_GEN_CONF equ 0x010 ; LEG_RT_CNF (1), ENABLE_CNF (0) 97 | ; 0x018 - 0x01F are Reserved 98 | HPET_GEN_INT_STATUS equ 0x020 99 | ; 0x028 - 0x0EF are Reserved 100 | HPET_MAIN_COUNTER equ 0x0F0 101 | ; 0x0F8 - 0x0FF are Reserved 102 | HPET_TIMER_0_CONF equ 0x100 103 | HPET_TIMER_0_COMP equ 0x108 104 | HPET_TIMER_0_INT equ 0x110 105 | ; 0x118 - 0x11F are Reserved 106 | HPET_TIMER_1_CONF equ 0x120 107 | HPET_TIMER_1_COMP equ 0x128 108 | HPET_TIMER_1_INT equ 0x130 109 | ; 0x138 - 0x13F are Reserved 110 | HPET_TIMER_2_CONF equ 0x140 111 | HPET_TIMER_2_COMP equ 0x148 112 | HPET_TIMER_2_INT equ 0x150 113 | ; 0x158 - 0x15F are Reserved 114 | ; 0x160 - 0x3FF are Reserved for Timers 3-31 115 | 116 | 117 | ; ============================================================================= 118 | ; EOF -------------------------------------------------------------------------------- /src/drivers/ioapic.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; I/O APIC Functions 6 | ; ============================================================================= 7 | 8 | 9 | ; ----------------------------------------------------------------------------- 10 | ; os_ioapic_init -- Initialize the I/O APIC 11 | ; IN: Nothing 12 | ; OUT: Nothing 13 | ; All other registers preserved 14 | os_ioapic_init: 15 | mov ecx, IOAPICVER 16 | call os_ioapic_read 17 | mov [os_ioapic_ver], al ; Store the version 18 | shr eax, 16 19 | mov [os_ioapic_mde], al ; Store the maximum # of redirection entries 20 | ret 21 | ; ----------------------------------------------------------------------------- 22 | 23 | 24 | ; ----------------------------------------------------------------------------- 25 | ; os_ioapic_read -- Read from a register in the I/O APIC 26 | ; IN: ECX = Register to read 27 | ; OUT: EAX = Register value 28 | ; All other registers preserved 29 | os_ioapic_read: 30 | push rdx 31 | mov rdx, [os_IOAPICAddress] 32 | mov [rdx], ecx ; Write the register # 33 | mov eax, [rdx+0x10] ; Read the value 34 | pop rdx 35 | ret 36 | ; ----------------------------------------------------------------------------- 37 | 38 | 39 | ; ----------------------------------------------------------------------------- 40 | ; os_ioapic_write -- Write to a register in the I/O APIC 41 | ; IN: ECX = Register to write 42 | ; EAX = Value to write 43 | ; OUT: All registers preserved 44 | os_ioapic_write: 45 | push rdx 46 | mov rdx, [os_IOAPICAddress] 47 | mov [rdx], ecx ; Write the register # 48 | mov [rdx+0x10], eax ; Write the value 49 | pop rdx 50 | ret 51 | ; ----------------------------------------------------------------------------- 52 | 53 | 54 | ; ----------------------------------------------------------------------------- 55 | ; os_ioapic_mask_clear -- Clear a mask on the I/O APIC 56 | ; IN: ECX = IRQ # 57 | ; EAX = Interrupt Vector 58 | ; OUT: All registers preserved 59 | os_ioapic_mask_clear: 60 | push rcx 61 | push rax 62 | shl ecx, 1 ; Quick multiply by 2 63 | add ecx, IOAPICREDTBL ; Add offset 64 | call os_ioapic_write ; Write the low 32 bits 65 | add ecx, 1 ; Increment for next register 66 | xor eax, eax 67 | call os_ioapic_write ; Write the high 32 bits 68 | pop rax 69 | pop rcx 70 | ret 71 | ; ----------------------------------------------------------------------------- 72 | 73 | 74 | ; ----------------------------------------------------------------------------- 75 | ; os_ioapic_redirection -- Configure a redirection on the I/O APIC 76 | ; IN: AL = Interrupt Vector 77 | ; OUT: All registers preserved 78 | ; Note: This also clears the mask 79 | os_ioapic_redirection: 80 | push rcx 81 | push rax 82 | and eax, 0x000000FF ; Clear the top 24 bits of EAX just in case 83 | mov cl, [os_ioapic_mde] ; Get the Maximum amount of Redirection Entries 84 | cmp al, cl ; Compare the Interrupt Vector to the MDE 85 | ja os_ioapic_redirection_error ; If it is greater then bail out 86 | mov ecx, eax 87 | add eax, 0x20 ; Offset to start of Interrupts 88 | push rcx 89 | push rax 90 | shl ecx, 1 ; Quick multiply by 2 91 | add ecx, IOAPICREDTBL ; Add offset 92 | bts eax, 13 ; Active low 93 | bts eax, 15 ; Level 94 | call os_ioapic_write ; Write the low 32 bits 95 | add ecx, 1 ; Increment for next register 96 | xor eax, eax 97 | call os_ioapic_write ; Write the high 32 bits 98 | pop rax 99 | pop rcx 100 | os_ioapic_redirection_error: 101 | pop rax 102 | pop rcx 103 | ret 104 | ; ----------------------------------------------------------------------------- 105 | 106 | 107 | ; Register list 108 | IOAPICID equ 0x00 ; Bits 27:24 - APIC ID 109 | IOAPICVER equ 0x01 ; Bits 23:16 - Max Redirection Entry, 7:0 - I/O APIC Version 110 | IOAPICARB equ 0x02 ; Bits 27:24 - APIC Arbitration ID 111 | IOAPICREDTBL equ 0x10 ; Starting Register for IRQs (0x10-11 for IRQ 0, 0x12-13 for IRQ 1, etc) 112 | 113 | ; IOAPICREDTBL Info 114 | ; Field Bits Description 115 | ; Vector 7:0 The Interrupt vector that will be raised on the specified CPU(s). 116 | ; Delivery Mode 10:8 How the interrupt will be sent to the CPU(s). It can be 000 (Fixed), 001 (Lowest Priority), 010 (SMI), 100 (NMI), 101 (INIT) and 111 (ExtINT). 117 | ; Destination Mode 11 Specify how the Destination field shall be interpreted. 0: Physical Destination, 1: Logical Destination 118 | ; Delivery Status 12 If 0, the IRQ is just relaxed and waiting for something to happen (or it has fired and already processed by Local APIC(s)). If 1, it means that the IRQ has been sent to the Local APICs but it's still waiting to be delivered. 119 | ; Pin Polarity 13 0: Active high, 1: Active low. For ISA IRQs assume Active High unless otherwise specified in Interrupt Source Override descriptors of the MADT or in the MP Tables. 120 | ; Remote IRR 14 TODO 121 | ; Trigger Mode 15 0: Edge, 1: Level. For ISA IRQs assume Edge unless otherwise specified in Interrupt Source Override descriptors of the MADT or in the MP Tables. 122 | ; Mask 16 Just like in the old PIC, you can temporary disable this IRQ by setting this bit, and reenable it by clearing the bit. 123 | ; Destination 63:56 This field is interpreted according to the Destination Format bit. If Physical destination is chosen, then this field is limited to bits 56 - 59 (only 16 CPUs addressable). Use the APIC ID of the CPU that you want to receive the interrupt. TODO: Logical destination format... 124 | 125 | 126 | ; ============================================================================= 127 | ; EOF 128 | -------------------------------------------------------------------------------- /src/drivers/lfb/fonts/smol.fnt: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal Monitor - 3 | ; Copyright (C) 2008-2024 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; Fonts are defined in HxW (Pixel height x Pixel width) 6 | ; 8x4 padded to 8x8 7 | ; 8 | ; Smol Font 9 | ; ============================================================================= 10 | 11 | 12 | font_height: db 8 13 | font_width: db 4 14 | font_h equ 8 15 | font_w equ 4 16 | 17 | font_data: 18 | 19 | ; Space 20 | db 00000000b 21 | db 00000000b 22 | db 00000000b 23 | db 00000000b 24 | db 00000000b 25 | db 00000000b 26 | db 00000000b 27 | db 00000000b 28 | 29 | ; ! 30 | db 01000000b 31 | db 01000000b 32 | db 01000000b 33 | db 01000000b 34 | db 01000000b 35 | db 00000000b 36 | db 01000000b 37 | db 00000000b 38 | 39 | ; " 40 | db 00000000b 41 | db 00000000b 42 | db 00000000b 43 | db 00000000b 44 | db 00000000b 45 | db 00000000b 46 | db 00000000b 47 | db 00000000b 48 | 49 | ; # 50 | db 00000000b 51 | db 00000000b 52 | db 00000000b 53 | db 00000000b 54 | db 00000000b 55 | db 00000000b 56 | db 00000000b 57 | db 00000000b 58 | 59 | ; $ 60 | db 00000000b 61 | db 00000000b 62 | db 00000000b 63 | db 00000000b 64 | db 00000000b 65 | db 00000000b 66 | db 00000000b 67 | db 00000000b 68 | 69 | ; % 70 | db 00000000b 71 | db 00000000b 72 | db 00000000b 73 | db 00000000b 74 | db 00000000b 75 | db 00000000b 76 | db 00000000b 77 | db 00000000b 78 | 79 | ; & 80 | db 00000000b 81 | db 00000000b 82 | db 00000000b 83 | db 00000000b 84 | db 00000000b 85 | db 00000000b 86 | db 00000000b 87 | db 00000000b 88 | 89 | ; ' 90 | db 00000000b 91 | db 01000000b 92 | db 01000000b 93 | db 00000000b 94 | db 00000000b 95 | db 00000000b 96 | db 00000000b 97 | db 00000000b 98 | 99 | ; ( 100 | db 01100000b 101 | db 10000000b 102 | db 10000000b 103 | db 10000000b 104 | db 10000000b 105 | db 10000000b 106 | db 01100000b 107 | db 00000000b 108 | 109 | ; ) 110 | db 11000000b 111 | db 00100000b 112 | db 00100000b 113 | db 00100000b 114 | db 00100000b 115 | db 00100000b 116 | db 11000000b 117 | db 00000000b 118 | 119 | ; * 120 | db 00000000b 121 | db 00000000b 122 | db 00000000b 123 | db 00000000b 124 | db 00000000b 125 | db 00000000b 126 | db 00000000b 127 | db 00000000b 128 | 129 | ; + 130 | db 00000000b 131 | db 00000000b 132 | db 00000000b 133 | db 01000000b 134 | db 11100000b 135 | db 01000000b 136 | db 00000000b 137 | db 00000000b 138 | 139 | ; , 140 | db 00000000b 141 | db 00000000b 142 | db 00000000b 143 | db 00000000b 144 | db 00000000b 145 | db 00000000b 146 | db 01000000b 147 | db 10000000b 148 | 149 | ; - 150 | db 00000000b 151 | db 00000000b 152 | db 00000000b 153 | db 00000000b 154 | db 00000000b 155 | db 00000000b 156 | db 11100000b 157 | db 00000000b 158 | 159 | ; . 160 | db 00000000b 161 | db 00000000b 162 | db 00000000b 163 | db 00000000b 164 | db 00000000b 165 | db 00000000b 166 | db 01000000b 167 | db 00000000b 168 | 169 | ; / 170 | db 00000000b 171 | db 00000000b 172 | db 00000000b 173 | db 00000000b 174 | db 00000000b 175 | db 00000000b 176 | db 00000000b 177 | db 00000000b 178 | 179 | ; 0 180 | db 11100000b 181 | db 10100000b 182 | db 10100000b 183 | db 10100000b 184 | db 10100000b 185 | db 10100000b 186 | db 11100000b 187 | db 00000000b 188 | 189 | ; 1 190 | db 01000000b 191 | db 11000000b 192 | db 01000000b 193 | db 01000000b 194 | db 01000000b 195 | db 01000000b 196 | db 11100000b 197 | db 00000000b 198 | 199 | ; 2 200 | db 11000000b 201 | db 00100000b 202 | db 00100000b 203 | db 01000000b 204 | db 10000000b 205 | db 10000000b 206 | db 11100000b 207 | db 00000000b 208 | 209 | ; 3 210 | db 11100000b 211 | db 00100000b 212 | db 00100000b 213 | db 11100000b 214 | db 00100000b 215 | db 00100000b 216 | db 11100000b 217 | db 00000000b 218 | 219 | ; 4 220 | db 10100000b 221 | db 10100000b 222 | db 10100000b 223 | db 11100000b 224 | db 00100000b 225 | db 00100000b 226 | db 00100000b 227 | db 00000000b 228 | 229 | ; 5 230 | db 11100000b 231 | db 10000000b 232 | db 10000000b 233 | db 11000000b 234 | db 00100000b 235 | db 00100000b 236 | db 11000000b 237 | db 00000000b 238 | 239 | ; 6 240 | db 01100000b 241 | db 10000000b 242 | db 10000000b 243 | db 11100000b 244 | db 10100000b 245 | db 10100000b 246 | db 11100000b 247 | db 00000000b 248 | 249 | ; 7 250 | db 11100000b 251 | db 00100000b 252 | db 00100000b 253 | db 01000000b 254 | db 10000000b 255 | db 10000000b 256 | db 10000000b 257 | db 00000000b 258 | 259 | ; 8 260 | db 11100000b 261 | db 10100000b 262 | db 10100000b 263 | db 01000000b 264 | db 10100000b 265 | db 10100000b 266 | db 11100000b 267 | db 00000000b 268 | 269 | ; 9 270 | db 11100000b 271 | db 10100000b 272 | db 10100000b 273 | db 11100000b 274 | db 00100000b 275 | db 00100000b 276 | db 00100000b 277 | db 00000000b 278 | 279 | ; : 280 | db 00000000b 281 | db 00000000b 282 | db 01000000b 283 | db 00000000b 284 | db 00000000b 285 | db 01000000b 286 | db 00000000b 287 | db 00000000b 288 | 289 | ; ; 290 | db 00000000b 291 | db 00000000b 292 | db 01000000b 293 | db 00000000b 294 | db 00000000b 295 | db 01000000b 296 | db 10000000b 297 | db 00000000b 298 | 299 | ; < 300 | db 00000000b 301 | db 00000000b 302 | db 00000000b 303 | db 00000000b 304 | db 00000000b 305 | db 00000000b 306 | db 00000000b 307 | db 00000000b 308 | 309 | ; = 310 | db 00000000b 311 | db 00000000b 312 | db 00000000b 313 | db 00000000b 314 | db 00000000b 315 | db 00000000b 316 | db 00000000b 317 | db 00000000b 318 | 319 | ; > 320 | db 00000000b 321 | db 00000000b 322 | db 10000000b 323 | db 01000000b 324 | db 00100000b 325 | db 01000000b 326 | db 10000000b 327 | db 00000000b 328 | 329 | ; ? 330 | db 00000000b 331 | db 01000000b 332 | db 10100000b 333 | db 00100000b 334 | db 01000000b 335 | db 00000000b 336 | db 01000000b 337 | db 00000000b 338 | 339 | ; @ 340 | db 00000000b 341 | db 00000000b 342 | db 00000000b 343 | db 00000000b 344 | db 00000000b 345 | db 00000000b 346 | db 00000000b 347 | db 00000000b 348 | 349 | ; A 350 | db 11100000b 351 | db 10100000b 352 | db 10100000b 353 | db 11100000b 354 | db 10100000b 355 | db 10100000b 356 | db 10100000b 357 | db 00000000b 358 | 359 | ; B 360 | db 11000000b 361 | db 10100000b 362 | db 10100000b 363 | db 11000000b 364 | db 10100000b 365 | db 10100000b 366 | db 11000000b 367 | db 00000000b 368 | 369 | ; C 370 | db 11100000b 371 | db 10000000b 372 | db 10000000b 373 | db 10000000b 374 | db 10000000b 375 | db 10000000b 376 | db 11100000b 377 | db 00000000b 378 | 379 | ; D 380 | db 11000000b 381 | db 10100000b 382 | db 10100000b 383 | db 10100000b 384 | db 10100000b 385 | db 10100000b 386 | db 11000000b 387 | db 00000000b 388 | 389 | ; E 390 | db 11100000b 391 | db 10000000b 392 | db 10000000b 393 | db 11000000b 394 | db 10000000b 395 | db 10000000b 396 | db 11100000b 397 | db 00000000b 398 | 399 | ; F 400 | db 11100000b 401 | db 10000000b 402 | db 10000000b 403 | db 11000000b 404 | db 10000000b 405 | db 10000000b 406 | db 10000000b 407 | db 00000000b 408 | 409 | ; G 410 | db 11100000b 411 | db 10000000b 412 | db 10000000b 413 | db 10100000b 414 | db 10100000b 415 | db 10100000b 416 | db 11100000b 417 | db 00000000b 418 | 419 | ; H 420 | db 10100000b 421 | db 10100000b 422 | db 10100000b 423 | db 11100000b 424 | db 10100000b 425 | db 10100000b 426 | db 10100000b 427 | db 00000000b 428 | 429 | ; I 430 | db 11100000b 431 | db 01000000b 432 | db 01000000b 433 | db 01000000b 434 | db 01000000b 435 | db 01000000b 436 | db 11100000b 437 | db 00000000b 438 | 439 | ; J 440 | db 11100000b 441 | db 00100000b 442 | db 00100000b 443 | db 00100000b 444 | db 00100000b 445 | db 10100000b 446 | db 11100000b 447 | db 00000000b 448 | 449 | ; K 450 | db 10100000b 451 | db 10100000b 452 | db 10100000b 453 | db 11000000b 454 | db 10100000b 455 | db 10100000b 456 | db 10100000b 457 | db 00000000b 458 | 459 | ; L 460 | db 10000000b 461 | db 10000000b 462 | db 10000000b 463 | db 10000000b 464 | db 10000000b 465 | db 10000000b 466 | db 11100000b 467 | db 00000000b 468 | 469 | ; M 470 | db 10100000b 471 | db 11100000b 472 | db 10100000b 473 | db 10100000b 474 | db 10100000b 475 | db 10100000b 476 | db 10100000b 477 | db 00000000b 478 | 479 | ; N 480 | db 10100000b 481 | db 10100000b 482 | db 11100000b 483 | db 10100000b 484 | db 10100000b 485 | db 10100000b 486 | db 10100000b 487 | db 00000000b 488 | 489 | ; O 490 | db 11100000b 491 | db 10100000b 492 | db 10100000b 493 | db 10100000b 494 | db 10100000b 495 | db 10100000b 496 | db 11100000b 497 | db 00000000b 498 | 499 | ; P 500 | db 11100000b 501 | db 10100000b 502 | db 10100000b 503 | db 11100000b 504 | db 10000000b 505 | db 10000000b 506 | db 10000000b 507 | db 00000000b 508 | 509 | ; Q 510 | db 11100000b 511 | db 10100000b 512 | db 10100000b 513 | db 10100000b 514 | db 10100000b 515 | db 10100000b 516 | db 11100000b 517 | db 00100000b 518 | 519 | ; R 520 | db 11100000b 521 | db 10100000b 522 | db 10100000b 523 | db 11100000b 524 | db 11000000b 525 | db 10100000b 526 | db 10100000b 527 | db 00000000b 528 | 529 | ; S 530 | db 11100000b 531 | db 10000000b 532 | db 10000000b 533 | db 11100000b 534 | db 00100000b 535 | db 00100000b 536 | db 11100000b 537 | db 00000000b 538 | 539 | ; T 540 | db 11100000b 541 | db 01000000b 542 | db 01000000b 543 | db 01000000b 544 | db 01000000b 545 | db 01000000b 546 | db 01000000b 547 | db 00000000b 548 | 549 | ; U 550 | db 10100000b 551 | db 10100000b 552 | db 10100000b 553 | db 10100000b 554 | db 10100000b 555 | db 10100000b 556 | db 11100000b 557 | db 00000000b 558 | 559 | ; V 560 | db 10100000b 561 | db 10100000b 562 | db 10100000b 563 | db 10100000b 564 | db 10100000b 565 | db 01000000b 566 | db 01000000b 567 | db 00000000b 568 | 569 | ; W 570 | db 10100000b 571 | db 10100000b 572 | db 10100000b 573 | db 10100000b 574 | db 10100000b 575 | db 11100000b 576 | db 10100000b 577 | db 00000000b 578 | 579 | ; X 580 | db 10100000b 581 | db 10100000b 582 | db 01000000b 583 | db 01000000b 584 | db 10100000b 585 | db 10100000b 586 | db 10100000b 587 | db 00000000b 588 | 589 | ; Y 590 | db 10100000b 591 | db 10100000b 592 | db 10100000b 593 | db 01000000b 594 | db 01000000b 595 | db 01000000b 596 | db 01000000b 597 | db 00000000b 598 | 599 | ; Z 600 | db 11100000b 601 | db 00100000b 602 | db 00100000b 603 | db 01000000b 604 | db 10000000b 605 | db 10000000b 606 | db 11100000b 607 | db 00000000b 608 | 609 | ; [ 610 | db 11100000b 611 | db 10000000b 612 | db 10000000b 613 | db 10000000b 614 | db 10000000b 615 | db 10000000b 616 | db 11100000b 617 | db 00000000b 618 | 619 | ; backslash 620 | db 00000000b 621 | db 00000000b 622 | db 00000000b 623 | db 00000000b 624 | db 10000000b 625 | db 01000000b 626 | db 00100000b 627 | db 00010000b 628 | 629 | ; ] 630 | db 11100000b 631 | db 00100000b 632 | db 00100000b 633 | db 00100000b 634 | db 00100000b 635 | db 00100000b 636 | db 11100000b 637 | db 00000000b 638 | 639 | ; ^ 640 | db 00000000b 641 | db 00000000b 642 | db 00000000b 643 | db 00100000b 644 | db 01010000b 645 | db 00000000b 646 | db 00000000b 647 | db 00000000b 648 | 649 | ; _ 650 | db 00000000b 651 | db 00000000b 652 | db 00000000b 653 | db 00000000b 654 | db 00000000b 655 | db 00000000b 656 | db 11100000b 657 | db 00000000b 658 | 659 | ; ` 660 | db 00000000b 661 | db 00000000b 662 | db 00000000b 663 | db 01000000b 664 | db 01000000b 665 | db 00100000b 666 | db 00000000b 667 | db 00000000b 668 | 669 | ; a 670 | db 00000000b 671 | db 00000000b 672 | db 11100000b 673 | db 00100000b 674 | db 11100000b 675 | db 10100000b 676 | db 11100000b 677 | db 00000000b 678 | 679 | ; b 680 | db 10000000b 681 | db 10000000b 682 | db 11100000b 683 | db 10100000b 684 | db 10100000b 685 | db 10100000b 686 | db 11100000b 687 | db 00000000b 688 | 689 | ; c 690 | db 00000000b 691 | db 00000000b 692 | db 11100000b 693 | db 10000000b 694 | db 10000000b 695 | db 10000000b 696 | db 11100000b 697 | db 00000000b 698 | 699 | ; d 700 | db 00100000b 701 | db 00100000b 702 | db 11100000b 703 | db 10100000b 704 | db 10100000b 705 | db 10100000b 706 | db 11100000b 707 | db 00000000b 708 | 709 | ; e 710 | db 00000000b 711 | db 00000000b 712 | db 11100000b 713 | db 10100000b 714 | db 11100000b 715 | db 10000000b 716 | db 11100000b 717 | db 00000000b 718 | 719 | ; f 720 | db 01100000b 721 | db 10000000b 722 | db 11100000b 723 | db 10000000b 724 | db 10000000b 725 | db 10000000b 726 | db 10000000b 727 | db 00000000b 728 | 729 | ; g 730 | db 00000000b 731 | db 00000000b 732 | db 11100000b 733 | db 10100000b 734 | db 10100000b 735 | db 11100000b 736 | db 00100000b 737 | db 01000000b 738 | 739 | ; h 740 | db 10000000b 741 | db 10000000b 742 | db 11100000b 743 | db 10100000b 744 | db 10100000b 745 | db 10100000b 746 | db 10100000b 747 | db 00000000b 748 | 749 | ; i 750 | db 00000000b 751 | db 01000000b 752 | db 00000000b 753 | db 01000000b 754 | db 01000000b 755 | db 01000000b 756 | db 11100000b 757 | db 00000000b 758 | 759 | ; j 760 | db 00000000b 761 | db 01000000b 762 | db 00000000b 763 | db 01000000b 764 | db 01000000b 765 | db 01000000b 766 | db 01000000b 767 | db 11000000b 768 | 769 | ; k 770 | db 10000000b 771 | db 10000000b 772 | db 10100000b 773 | db 10100000b 774 | db 11000000b 775 | db 10100000b 776 | db 10100000b 777 | db 00000000b 778 | 779 | ; l 780 | db 11000000b 781 | db 01000000b 782 | db 01000000b 783 | db 01000000b 784 | db 01000000b 785 | db 01000000b 786 | db 11100000b 787 | db 00000000b 788 | 789 | ; m 790 | db 00000000b 791 | db 00000000b 792 | db 11100000b 793 | db 11100000b 794 | db 11100000b 795 | db 10100000b 796 | db 10100000b 797 | db 00000000b 798 | 799 | ; n 800 | db 00000000b 801 | db 00000000b 802 | db 11100000b 803 | db 10100000b 804 | db 10100000b 805 | db 10100000b 806 | db 10100000b 807 | db 00000000b 808 | 809 | ; o 810 | db 00000000b 811 | db 00000000b 812 | db 11100000b 813 | db 10100000b 814 | db 10100000b 815 | db 10100000b 816 | db 11100000b 817 | db 00000000b 818 | 819 | ; p 820 | db 00000000b 821 | db 00000000b 822 | db 11100000b 823 | db 10100000b 824 | db 10100000b 825 | db 10100000b 826 | db 11100000b 827 | db 10000000b 828 | 829 | ; q 830 | db 00000000b 831 | db 00000000b 832 | db 11100000b 833 | db 10100000b 834 | db 10100000b 835 | db 10100000b 836 | db 11100000b 837 | db 00100000b 838 | 839 | ; r 840 | db 00000000b 841 | db 00000000b 842 | db 11100000b 843 | db 10000000b 844 | db 10000000b 845 | db 10000000b 846 | db 10000000b 847 | db 00000000b 848 | 849 | ; s 850 | db 00000000b 851 | db 00000000b 852 | db 11100000b 853 | db 10000000b 854 | db 11100000b 855 | db 00100000b 856 | db 11100000b 857 | db 00000000b 858 | 859 | ; t 860 | db 10000000b 861 | db 10000000b 862 | db 11000000b 863 | db 10000000b 864 | db 10000000b 865 | db 10000000b 866 | db 11100000b 867 | db 00000000b 868 | 869 | ; u 870 | db 00000000b 871 | db 00000000b 872 | db 10100000b 873 | db 10100000b 874 | db 10100000b 875 | db 10100000b 876 | db 11100000b 877 | db 00000000b 878 | 879 | ; v 880 | db 00000000b 881 | db 00000000b 882 | db 10100000b 883 | db 10100000b 884 | db 10100000b 885 | db 01000000b 886 | db 01000000b 887 | db 00000000b 888 | 889 | ; w 890 | db 00000000b 891 | db 00000000b 892 | db 10100000b 893 | db 10100000b 894 | db 10100000b 895 | db 11100000b 896 | db 10100000b 897 | db 00000000b 898 | 899 | ; x 900 | db 00000000b 901 | db 00000000b 902 | db 10100000b 903 | db 10100000b 904 | db 01000000b 905 | db 10100000b 906 | db 10100000b 907 | db 00000000b 908 | 909 | ; y 910 | db 00000000b 911 | db 00000000b 912 | db 10100000b 913 | db 10100000b 914 | db 10100000b 915 | db 11100000b 916 | db 00100000b 917 | db 11000000b 918 | 919 | ; z 920 | db 00000000b 921 | db 00000000b 922 | db 11100000b 923 | db 00100000b 924 | db 01000000b 925 | db 10000000b 926 | db 11100000b 927 | db 00000000b 928 | 929 | ; { 930 | db 00000000b 931 | db 00000000b 932 | db 00000000b 933 | db 00000000b 934 | db 00000000b 935 | db 00000000b 936 | db 00000000b 937 | db 00000000b 938 | 939 | ; | 940 | db 01000000b 941 | db 01000000b 942 | db 01000000b 943 | db 01000000b 944 | db 01000000b 945 | db 01000000b 946 | db 01000000b 947 | db 01000000b 948 | 949 | ; } 950 | db 00000000b 951 | db 00000000b 952 | db 00000000b 953 | db 00000000b 954 | db 00000000b 955 | db 00000000b 956 | db 00000000b 957 | db 00000000b 958 | 959 | ; ~ 960 | db 00000000b 961 | db 00000000b 962 | db 00000000b 963 | db 00000000b 964 | db 00000000b 965 | db 00000000b 966 | db 00000000b 967 | db 00000000b 968 | 969 | ; block 970 | db 11110000b 971 | db 11110000b 972 | db 11110000b 973 | db 11110000b 974 | db 11110000b 975 | db 11110000b 976 | db 11110000b 977 | db 11110000b 978 | 979 | 980 | 981 | ; ============================================================================= 982 | ; EOF 983 | -------------------------------------------------------------------------------- /src/drivers/lfb/lfb.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; Linear Frame Buffer Output 6 | ; ============================================================================= 7 | 8 | 9 | ; ----------------------------------------------------------------------------- 10 | ; lfb_init -- Initialize the linear frame buffer display 11 | ; IN: Nothing 12 | ; OUT: Nothing 13 | lfb_init: 14 | push rdx 15 | push rcx 16 | push rbx 17 | push rax 18 | 19 | ; Convert font data to pixel data. The default 12x6 font is 72 pixels per glyph. 288 bytes per. 20 | mov rdi, os_font 21 | mov rsi, font_data 22 | xor ebx, ebx 23 | next_char: 24 | cmp ebx, 128 25 | je render_done 26 | inc ebx 27 | xor edx, edx ; font_h 28 | next_line: 29 | cmp edx, font_h 30 | je next_char 31 | inc edx 32 | xor ecx, ecx 33 | lodsb ; Load a line of font data 34 | next_pixel: 35 | cmp ecx, font_w ; Font width 36 | je next_line 37 | rol al, 1 38 | bt ax, 0 39 | jc lit 40 | push rax 41 | mov eax, [BG_Color] 42 | jmp store_pixel 43 | lit: 44 | push rax 45 | mov eax, [FG_Color] 46 | store_pixel: 47 | stosd 48 | pop rax 49 | inc ecx 50 | jmp next_pixel 51 | render_done: 52 | 53 | ; Calculate screen parameters 54 | xor eax, eax 55 | xor ecx, ecx 56 | mov ax, [os_screen_x] 57 | mov cx, [os_screen_y] 58 | mul ecx 59 | mov [Screen_Pixels], eax 60 | mov ecx, 4 61 | mul ecx 62 | mov [Screen_Bytes], eax 63 | 64 | ; call lfb_clear 65 | 66 | ; Calculate display parameters based on font dimensions 67 | xor eax, eax 68 | xor edx, edx 69 | xor ecx, ecx 70 | mov ax, [os_screen_x] 71 | mov cl, [font_width] 72 | div cx ; Divide VideoX by font_width 73 | mov [Screen_Cols], ax 74 | xor eax, eax 75 | xor edx, edx 76 | xor ecx, ecx 77 | mov ax, [os_screen_y] 78 | mov cl, [font_height] 79 | div cx ; Divide VideoY by font_height 80 | mov [Screen_Rows], ax 81 | 82 | ; Calculate lfb_glyph_bytes 83 | xor eax, eax 84 | xor ecx, ecx 85 | mov al, [font_height] 86 | mov cl, [font_width] 87 | mul ecx ; EDX:EAX := EAX * ECX 88 | shl eax, 2 ; Quick multiply by 4 89 | mov [lfb_glyph_bytes], eax 90 | 91 | ; Pixels per row = font_h * [os_screen_x] * 4 * [Screen_Cursor_Row] 92 | ; Todo - [Screen_Cursor_Row] * (font_h * [os_screen_x] * 4) 93 | ; Calculate lfb_bytes_per_row 94 | xor eax, eax 95 | xor ecx, ecx 96 | mov ax, [os_screen_x] 97 | mov cx, font_h ; Font height 98 | mul ecx ; EDX:EAX := EAX * ECX 99 | shl eax, 2 ; Quick multiply by 4 100 | mov [lfb_glyph_bytes_per_row], eax 101 | 102 | xor eax, eax 103 | mov ax, font_w 104 | shl eax, 2 ; Quick multiply by 4 105 | mov [lfb_glyph_bytes_per_col], eax 106 | 107 | xor eax, eax 108 | mov ax, [os_screen_ppsl] ; Use Pixels Per Scan Line in case system aligns video memory 109 | sub ax, font_w ; Subtract the width of a single glyph 110 | shl eax, 2 ; Quick multiply by 4 111 | mov [lfb_glyph_next_line], rax 112 | 113 | mov rax, [os_screen_lfb] 114 | mov [LastLine], rax 115 | 116 | ; Overwrite the kernel b_output function so output goes to the screen instead of the serial port 117 | mov rax, lfb_output_chars 118 | mov [0x100018], rax 119 | 120 | pop rax 121 | pop rbx 122 | pop rcx 123 | pop rdx 124 | ret 125 | ; ----------------------------------------------------------------------------- 126 | 127 | 128 | ; ----------------------------------------------------------------------------- 129 | ; lfb_inc_cursor -- Increment the cursor by one 130 | ; IN: Nothing 131 | ; OUT: All registers preserved 132 | lfb_inc_cursor: 133 | push rax 134 | 135 | inc word [Screen_Cursor_Col] ; Increment the current cursor column 136 | mov ax, [Screen_Cursor_Col] 137 | cmp ax, [Screen_Cols] ; Compare it to the # of columns for the screen 138 | jne lfb_inc_cursor_done ; If not equal we are done 139 | mov word [Screen_Cursor_Col], 0 ; Reset column to 0 140 | inc word [Screen_Cursor_Row] ; Increment the current cursor row 141 | call lfb_draw_line 142 | mov ax, [Screen_Cursor_Row] 143 | cmp ax, [Screen_Rows] ; Compare it to the # of rows for the screen 144 | jne lfb_inc_cursor_done ; If not equal we are done 145 | mov word [Screen_Cursor_Row], 0 ; Wrap around 146 | lfb_inc_cursor_done: 147 | pop rax 148 | ret 149 | ; ----------------------------------------------------------------------------- 150 | 151 | 152 | ; ----------------------------------------------------------------------------- 153 | ; lfb_dec_cursor -- Decrement the cursor by one 154 | ; IN: Nothing 155 | ; OUT: All registers preserved 156 | lfb_dec_cursor: 157 | push rax 158 | 159 | cmp word [Screen_Cursor_Col], 0 ; Compare the current cursor column to 0 160 | jne lfb_dec_cursor_done ; If not equal we are done 161 | dec word [Screen_Cursor_Row] ; Otherwise decrement the row 162 | mov ax, [Screen_Cols] ; Get the total columns and save it as the current 163 | mov word [Screen_Cursor_Col], ax 164 | 165 | lfb_dec_cursor_done: 166 | dec word [Screen_Cursor_Col] ; Decrement the cursor as usual 167 | 168 | pop rax 169 | ret 170 | ; ----------------------------------------------------------------------------- 171 | 172 | 173 | ; ----------------------------------------------------------------------------- 174 | ; lfb_output_chars -- Output text to LFB 175 | ; IN: RSI = message location (an ASCII string, not zero-terminated) 176 | ; RCX = number of chars to print 177 | ; OUT: All registers preserved 178 | lfb_output_chars: 179 | push rsi 180 | push rcx 181 | push rax 182 | 183 | lfb_output_chars_nextchar: 184 | cmp rcx, 0 185 | jz lfb_output_chars_done 186 | dec rcx 187 | lodsb ; Get char from string and store in AL 188 | cmp al, 0x20 189 | jae lfb_output_chars_nextchar_output 190 | cmp al, 0x0A ; LF - Check if there was a newline (aka line feed) character in the string 191 | je lfb_output_chars_newline ; If so then we print a new line 192 | cmp al, 0x0D ; CR - Check if there was a carriage return character in the string 193 | je lfb_output_chars_cr ; If so reset to column 0 194 | cmp al, 0x0E ; Backspace 195 | je lfb_output_backspace 196 | cmp al, 0x09 197 | je lfb_output_chars_tab 198 | ; Check for special characters 199 | cmp al, 0x01 ; Clear Screen 200 | je lfb_output_cls 201 | cmp al, 0x02 ; Increment Cursor 202 | je lfb_output_inc_cursor 203 | cmp al, 0x03 ; Decrement Cursor 204 | je lfb_output_dec_cursor 205 | lfb_output_chars_nextchar_output: 206 | call lfb_output_char 207 | jmp lfb_output_chars_nextchar 208 | 209 | lfb_output_chars_newline: 210 | mov al, [rsi] 211 | cmp al, 0x0A 212 | je lfb_output_chars_newline_skip_LF 213 | call lfb_output_newline 214 | jmp lfb_output_chars_nextchar 215 | 216 | lfb_output_chars_cr: 217 | mov al, [rsi] ; Check the next character 218 | cmp al, 0x0A ; Is it a newline? 219 | je lfb_output_chars_newline ; If so, display a newline and ignore the carriage return 220 | push rcx 221 | xor eax, eax 222 | xor ecx, ecx 223 | mov [Screen_Cursor_Col], ax 224 | mov cx, [Screen_Cols] 225 | mov al, ' ' 226 | lfb_output_chars_cr_clearline: 227 | call lfb_output_char 228 | dec cx 229 | jnz lfb_output_chars_cr_clearline 230 | dec word [Screen_Cursor_Row] 231 | xor eax, eax 232 | mov [Screen_Cursor_Col], ax 233 | pop rcx 234 | jmp lfb_output_chars_nextchar 235 | 236 | lfb_output_backspace: 237 | call lfb_dec_cursor ; Decrement the cursor 238 | mov al, ' ' ; 0x20 is the character for a space 239 | call lfb_output_char ; Write over the last typed character with the space 240 | call lfb_dec_cursor ; Decrement the cursor again 241 | jmp lfb_output_chars_nextchar 242 | 243 | lfb_output_chars_newline_skip_LF: 244 | test rcx, rcx 245 | jz lfb_output_chars_newline_skip_LF_nosub 246 | dec rcx 247 | 248 | lfb_output_chars_newline_skip_LF_nosub: 249 | inc rsi 250 | call lfb_output_newline 251 | jmp lfb_output_chars_nextchar 252 | 253 | lfb_output_chars_tab: 254 | push rcx 255 | mov ax, [Screen_Cursor_Col] ; Grab the current cursor X value (ex 7) 256 | mov cx, ax 257 | add ax, 8 ; Add 8 (ex 15) 258 | shr ax, 3 ; Clear lowest 3 bits (ex 8) 259 | shl ax, 3 ; Bug? 'xor al, 7' doesn't work... 260 | sub ax, cx ; (ex 8 - 7 = 1) 261 | mov cx, ax 262 | mov al, ' ' 263 | 264 | lfb_output_chars_tab_next: 265 | call lfb_output_char 266 | dec cx 267 | jnz lfb_output_chars_tab_next 268 | pop rcx 269 | jmp lfb_output_chars_nextchar 270 | 271 | lfb_output_cls: 272 | call lfb_clear 273 | call lfb_draw_line 274 | jmp lfb_output_chars_nextchar 275 | 276 | lfb_output_inc_cursor: 277 | call lfb_inc_cursor 278 | jmp lfb_output_chars_nextchar 279 | 280 | lfb_output_dec_cursor: 281 | call lfb_dec_cursor 282 | jmp lfb_output_chars_nextchar 283 | 284 | lfb_output_chars_done: 285 | pop rax 286 | pop rcx 287 | pop rsi 288 | ret 289 | ; ----------------------------------------------------------------------------- 290 | 291 | 292 | ; ----------------------------------------------------------------------------- 293 | ; lfb_output_char -- Displays a char 294 | ; IN: AL = char to display 295 | ; OUT: All registers preserved 296 | lfb_output_char: 297 | call lfb_glyph 298 | call lfb_inc_cursor 299 | ret 300 | ; ----------------------------------------------------------------------------- 301 | 302 | 303 | ; ----------------------------------------------------------------------------- 304 | ; lfb_output_newline -- Reset cursor to start of next line and wrap if needed 305 | ; IN: Nothing 306 | ; OUT: All registers preserved 307 | lfb_output_newline: 308 | push rax 309 | 310 | mov word [Screen_Cursor_Col], 0 ; Reset column to 0 311 | mov ax, [Screen_Rows] ; Grab max rows on screen 312 | dec ax ; and subtract 1 313 | cmp ax, [Screen_Cursor_Row] ; Is the cursor already on the bottom row? 314 | je lfb_output_newline_wrap ; If so, then wrap 315 | inc word [Screen_Cursor_Row] ; If not, increment the cursor to next row 316 | jmp lfb_output_newline_done 317 | 318 | lfb_output_newline_wrap: 319 | mov word [Screen_Cursor_Row], 0 320 | 321 | lfb_output_newline_done: 322 | call lfb_draw_line 323 | pop rax 324 | ret 325 | ; ----------------------------------------------------------------------------- 326 | 327 | 328 | ; ----------------------------------------------------------------------------- 329 | ; lfb_glyph -- Put a glyph on the screen at the cursor location 330 | ; IN: AL = char to display 331 | ; OUT: All registers preserved 332 | lfb_glyph: 333 | push rdi 334 | push rsi 335 | push rdx 336 | push rcx 337 | push rax 338 | 339 | ; Filter out characters that can't be displayed 340 | and eax, 0x000000FF ; Only keep AL 341 | cmp al, 0x20 342 | jb hidden 343 | cmp al, 127 344 | ja hidden 345 | sub rax, 0x20 346 | jmp load_char 347 | hidden: 348 | mov al, 0 349 | load_char: 350 | 351 | push rax ; Save the character to display 352 | 353 | ; Calculate where to put glyph in the Linear Frame Buffer 354 | mov rdi, [os_screen_lfb] 355 | 356 | ; Calculate offset for row into Linear Frame Buffer 357 | xor ecx, ecx 358 | mov eax, [lfb_glyph_bytes_per_row] 359 | mov cx, [Screen_Cursor_Row] 360 | mul ecx ; EDX:EAX := EAX * ECX 361 | add rdi, rax 362 | 363 | ; Calculate offset for column into Linear Frame Buffer 364 | xor ecx, ecx 365 | mov eax, [lfb_glyph_bytes_per_col] 366 | mov cx, [Screen_Cursor_Col] 367 | mul ecx ; EDX:EAX := EAX * ECX 368 | add rdi, rax 369 | 370 | pop rax ; Restore the character to display 371 | 372 | ; Copy glyph data to Linear Frame Buffer 373 | mov rsi, os_font ; Font pixel data 374 | mov ecx, [lfb_glyph_bytes] ; Bytes per glyph 375 | mul ecx ; EDX:EAX := EAX * ECX 376 | xor edx, edx ; Counter for font height 377 | add rsi, rax ; RSI points to start of glyph 378 | mov rax, [lfb_glyph_next_line] 379 | lfb_glyph_next: 380 | mov ecx, font_w 381 | rep movsd 382 | add rdi, rax ; Skip to next line in Linear Frame Buffer 383 | inc edx 384 | cmp edx, font_h 385 | jne lfb_glyph_next 386 | 387 | lfb_glyph_done: 388 | pop rax 389 | pop rcx 390 | pop rdx 391 | pop rsi 392 | pop rdi 393 | ret 394 | ; ----------------------------------------------------------------------------- 395 | 396 | 397 | ; ----------------------------------------------------------------------------- 398 | ; lfb_pixel -- Put a pixel on the screen 399 | ; IN: EBX = Packed X & Y coordinates (YYYYXXXX) 400 | ; EAX = Pixel Details (AARRGGBB) 401 | ; OUT: All registers preserved 402 | lfb_pixel: 403 | push rdi 404 | push rdx 405 | push rcx 406 | push rbx 407 | push rax 408 | 409 | ; Calculate offset in video memory and store pixel 410 | push rax ; Save the pixel details 411 | mov rax, rbx 412 | shr eax, 16 ; Isolate Y co-ordinate 413 | xor ecx, ecx 414 | mov cx, [os_screen_ppsl] 415 | mul ecx ; Multiply Y by VideoPPSL 416 | and ebx, 0x0000FFFF ; Isolate X co-ordinate 417 | add eax, ebx ; Add X 418 | mov rbx, rax ; Save the offset to RBX 419 | mov rdi, [os_screen_lfb] ; Store the pixel to video memory 420 | pop rax ; Restore pixel details 421 | shl ebx, 2 ; Quickly multiply by 4 422 | add rdi, rbx ; Add offset in video memory 423 | stosd ; Output pixel to video memory 424 | 425 | pop rax 426 | pop rbx 427 | pop rcx 428 | pop rdx 429 | pop rdi 430 | ret 431 | ; ----------------------------------------------------------------------------- 432 | 433 | 434 | ; ----------------------------------------------------------------------------- 435 | ; lfb_clear -- Clear the Linear Frame Buffer 436 | ; IN: Nothing 437 | ; OUT: All registers preserved 438 | lfb_clear: 439 | push rdi 440 | push rcx 441 | push rax 442 | 443 | ; Set cursor to top left corner 444 | mov word [Screen_Cursor_Col], 0 445 | mov word [Screen_Cursor_Row], 0 446 | 447 | ; Fill the Linear Frame Buffer with the background colour 448 | mov rdi, [os_screen_lfb] 449 | mov eax, [BG_Color] 450 | mov ecx, [Screen_Bytes] 451 | shr ecx, 2 ; Quick divide by 4 452 | rep stosd 453 | 454 | pop rax 455 | pop rcx 456 | pop rdi 457 | ret 458 | ; ----------------------------------------------------------------------------- 459 | 460 | 461 | ; ----------------------------------------------------------------------------- 462 | ; lfb_draw_line 463 | lfb_draw_line: 464 | push rdi 465 | push rdx 466 | push rcx 467 | push rax 468 | 469 | ; Clear the previously drawn line 470 | mov rdi, [LastLine] 471 | mov cx, [os_screen_ppsl] 472 | mov eax, [BG_Color] 473 | rep stosd 474 | 475 | ; Display a line under the current cursor row 476 | mov rdi, [os_screen_lfb] 477 | xor ecx, ecx 478 | xor eax, eax 479 | mov ax, [Screen_Cursor_Row] 480 | add ax, 1 481 | mov cx, font_h * 4 ; Font height 482 | mul cx 483 | mov cx, [os_screen_ppsl] 484 | mul ecx ; Multiply Y by os_screen_ppsl 485 | add rdi, rax 486 | mov [LastLine], rdi 487 | xor ecx, ecx 488 | mov cx, [os_screen_ppsl] 489 | mov eax, [Line_Color] 490 | rep stosd 491 | 492 | ; Clear the next row of text 493 | mov ax, [Screen_Cursor_Row] ; Get the current cursor row 494 | inc ax ; Inc by 1 as it is 0-based 495 | cmp ax, [Screen_Rows] ; Compare it to the # of rows for the screen 496 | jne lfb_draw_line_skip 497 | mov rdi, [os_screen_lfb] ; Roll RDI back to the start of video memory 498 | lfb_draw_line_skip: 499 | xor eax, eax 500 | mov ax, [os_screen_ppsl] 501 | mov ecx, font_h 502 | mul ecx 503 | mov ecx, eax 504 | mov eax, [BG_Color] 505 | rep stosd 506 | 507 | pop rax 508 | pop rcx 509 | pop rdx 510 | pop rdi 511 | ret 512 | ; ----------------------------------------------------------------------------- 513 | 514 | 515 | ; Font data - Only 1 font may be used 516 | align 16 517 | ;%include 'drivers/lfb/fonts/smol.fnt' ; 8x4 518 | %include 'drivers/lfb/fonts/baremetal.fnt' ; 12x6 519 | ;%include 'drivers/lfb/fonts/departuremono.fnt' ; 14x7 520 | ;%include 'drivers/lfb/fonts/ibm.fnt' ; 16x8 521 | 522 | 523 | ; Variables 524 | align 16 525 | 526 | LastLine: dq 0 527 | lfb_glyph_next_line: dq 0 528 | FG_Color: dd 0x00FFFFFF ; White 529 | BG_Color: dd 0x00404040 ; Dark grey 530 | Line_Color: dd 0x00F7CA54 ; Return Infinity Yellow/Orange 531 | Screen_Pixels: dd 0 532 | Screen_Bytes: dd 0 533 | lfb_glyph_bytes: dd 0 534 | lfb_glyph_bytes_per_row: dd 0 535 | lfb_glyph_bytes_per_col: dd 0 536 | Screen_Rows: dw 0 537 | Screen_Cols: dw 0 538 | Screen_Cursor_Row: dw 0 539 | Screen_Cursor_Col: dw 0 540 | 541 | 542 | ; ============================================================================= 543 | ; EOF 544 | -------------------------------------------------------------------------------- /src/drivers/net/i8257x.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; Intel 8257x Gigabit Ethernet Driver 6 | ; 7 | ; This driver has been tested on physical hardware with a 82574L PCIe card 8 | ; (device ID 0x10D3) as well as QEMU (-device e1000e) 9 | ; ============================================================================= 10 | 11 | 12 | ; ----------------------------------------------------------------------------- 13 | ; Initialize an Intel 8257x NIC 14 | ; IN: RDX = Packed Bus address (as per syscalls/bus.asm) 15 | net_i8257x_init: 16 | push rsi 17 | push rdx 18 | push rcx 19 | push rax 20 | 21 | ; Get the Base Memory Address of the device 22 | mov al, 0 ; Read BAR0 23 | call os_bus_read_bar 24 | mov [os_NetIOBaseMem], rax ; Save it as the base 25 | mov [os_NetIOLength], rcx ; Save the length 26 | 27 | ; Set PCI Status/Command values 28 | mov dl, 0x01 ; Read Status/Command 29 | call os_bus_read 30 | bts eax, 10 ; Set Interrupt Disable 31 | bts eax, 2 ; Enable Bus Master 32 | bts eax, 1 ; Enable Memory Space 33 | call os_bus_write ; Write updated Status/Command 34 | 35 | ; Get the MAC address 36 | mov rsi, [os_NetIOBaseMem] 37 | mov eax, [rsi+i8257x_RAL] ; RAL 38 | mov [os_NetMAC], al 39 | shr eax, 8 40 | mov [os_NetMAC+1], al 41 | shr eax, 8 42 | mov [os_NetMAC+2], al 43 | shr eax, 8 44 | mov [os_NetMAC+3], al 45 | mov eax, [rsi+i8257x_RAH] ; RAH 46 | mov [os_NetMAC+4], al 47 | shr eax, 8 48 | mov [os_NetMAC+5], al 49 | 50 | ; Reset the device 51 | call net_i8257x_reset 52 | 53 | net_i8257x_init_error: 54 | 55 | pop rax 56 | pop rcx 57 | pop rdx 58 | pop rsi 59 | ret 60 | ; ----------------------------------------------------------------------------- 61 | 62 | 63 | ; ----------------------------------------------------------------------------- 64 | ; net_i8257x_reset - Reset an Intel 8257x NIC 65 | ; IN: Nothing 66 | ; OUT: Nothing, all registers preserved 67 | net_i8257x_reset: 68 | push rdi 69 | push rsi 70 | push rax 71 | 72 | mov rsi, [os_NetIOBaseMem] 73 | mov rdi, rsi 74 | 75 | ; Disable Interrupts (14.4) 76 | mov eax, i8257x_IRQ_CLEAR_MASK 77 | mov [rsi+i8257x_IMC], eax ; Disable all interrupt causes 78 | xor eax, eax 79 | mov [rsi+i8257x_ITR], eax ; Disable interrupt throttling logic 80 | mov [rsi+i8257x_IMS], eax ; Mask all interrupts 81 | mov eax, [rsi+i8257x_ICR] ; Clear any pending interrupts 82 | 83 | ; Issue a global reset (14.5) 84 | mov eax, i8257x_CTRL_RST_MASK ; Load the mask for a software reset and link reset 85 | mov [rsi+i8257x_CTRL], eax ; Write the reset value 86 | net_i8257x_init_reset_wait: 87 | mov eax, [rsi+i8257x_CTRL] ; Read CTRL 88 | jnz net_i8257x_init_reset_wait ; Wait for it to read back as 0x0 89 | 90 | ; Disable Interrupts again (14.4) 91 | mov eax, i8257x_IRQ_CLEAR_MASK 92 | mov [rsi+i8257x_IMC], eax ; Disable all interrupt causes 93 | xor eax, eax 94 | mov [rsi+i8257x_ITR], eax ; Disable interrupt throttling logic 95 | mov [rsi+i8257x_IMS], eax ; Mask all interrupts 96 | mov eax, [rsi+i8257x_ICR] ; Clear any pending interrupts 97 | 98 | ; Set up the PHY and the link (14.8.1) 99 | mov eax, [rsi+i8257x_CTRL] 100 | ; Clear the bits we don't want 101 | and eax, 0xFFFFFFFF - (1 << i8257x_CTRL_LRST | 1 << i8257x_CTRL_VME) 102 | ; Set the bits we do want 103 | or eax, 1 << i8257x_CTRL_FD | 1 << i8257x_CTRL_SLU 104 | mov [rsi+i8257x_CTRL], eax 105 | 106 | ; Initialize all statistical counters () 107 | mov eax, [rsi+i8257x_GPRC] ; RX packets 108 | mov eax, [rsi+i8257x_GPTC] ; TX packets 109 | mov eax, [rsi+i8257x_GORCL] 110 | mov eax, [rsi+i8257x_GORCH] ; RX bytes = GORCL + (GORCH << 32) 111 | mov eax, [rsi+i8257x_GOTCL] 112 | mov eax, [rsi+i8257x_GOTCH] ; TX bytes = GOTCL + (GOTCH << 32) 113 | 114 | ; Create RX descriptors 115 | push rdi 116 | mov ecx, i8257x_MAX_DESC 117 | mov rdi, os_rx_desc 118 | net_i8257x_reset_nextdesc: 119 | mov rax, os_PacketBuffers ; Default packet will go here 120 | stosq 121 | xor eax, eax 122 | stosq 123 | dec ecx 124 | jnz net_i8257x_reset_nextdesc 125 | pop rdi 126 | 127 | ; Initialize receive (14.6) 128 | mov rax, os_rx_desc 129 | mov [rsi+i8257x_RDBAL], eax ; Receive Descriptor Base Address Low 130 | shr rax, 32 131 | mov [rsi+i8257x_RDBAH], eax ; Receive Descriptor Base Address High 132 | mov eax, i8257x_MAX_DESC * 16 133 | mov [rsi+i8257x_RDLEN], eax ; Receive Descriptor Length 134 | xor eax, eax 135 | mov [rsi+i8257x_RDH], eax ; Receive Descriptor Head 136 | mov eax, i8257x_MAX_DESC / 2 137 | mov [rsi+i8257x_RDT], eax ; Receive Descriptor Tail 138 | mov eax, 1 << i8257x_RCTL_EN | 1 << i8257x_RCTL_UPE | 1 << i8257x_RCTL_MPE | 1 << i8257x_RCTL_LPE | 1 << i8257x_RCTL_BAM | 1 << i8257x_RCTL_SECRC 139 | mov [rsi+i8257x_RCTL], eax ; Receive Control Register 140 | 141 | ; Initialize transmit (14.7) 142 | mov rax, os_tx_desc 143 | mov [rsi+i8257x_TDBAL], eax ; Transmit Descriptor Base Address Low 144 | shr rax, 32 145 | mov [rsi+i8257x_TDBAH], eax ; Transmit Descriptor Base Address High 146 | mov eax, i8257x_MAX_DESC * 16 147 | mov [rsi+i8257x_TDLEN], eax ; Transmit Descriptor Length 148 | xor eax, eax 149 | mov [rsi+i8257x_TDH], eax ; Transmit Descriptor Head 150 | mov [rsi+i8257x_TDT], eax ; Transmit Descriptor Tail 151 | mov eax, 1 << i8257x_TCTL_EN | 1 << i8257x_TCTL_PSP | 15 << i8257x_TCTL_CT | 0x3F << i8257x_TCTL_COLD 152 | mov [rsi+i8257x_TCTL], eax ; Transmit Control Register 153 | 154 | ; Set Driver Loaded bit 155 | mov eax, [rsi+i8257x_CTRL_EXT] 156 | or eax, 1 << i8257x_CTRL_EXT_DRV_LOAD 157 | mov [rsi+i8257x_CTRL_EXT], eax 158 | 159 | pop rax 160 | pop rsi 161 | pop rdi 162 | ret 163 | ; ----------------------------------------------------------------------------- 164 | 165 | 166 | ; ----------------------------------------------------------------------------- 167 | ; net_i8257x_transmit - Transmit a packet via an Intel 8257x NIC 168 | ; IN: RSI = Location of packet 169 | ; RCX = Length of packet 170 | ; OUT: Nothing 171 | ; Note: This driver uses the "legacy format" so TDESC.CMD.DEXT (5) is cleared to 0 172 | ; TDESC Descriptor Format: 173 | ; First Qword: 174 | ; Bits 63:0 - Buffer Address 175 | ; Second Qword: 176 | ; Bits 15:0 - Length 177 | ; Bits 23:16 - CSO - Checksum Offset 178 | ; Bits 31:24 - CMD - Command Byte 179 | ; Bits 35:32 - STA - Status 180 | ; Bits 39:36 - ExtCMD - Extended Command 181 | ; Bits 47:40 - CSS - Checksum Start 182 | ; Bits 63:48 - VLAN 183 | net_i8257x_transmit: 184 | push rdi 185 | push rax 186 | 187 | mov rdi, os_tx_desc ; Transmit Descriptor Base Address 188 | 189 | ; Calculate the descriptor to write to 190 | mov eax, [i8257x_tx_lasttail] 191 | push rax ; Save lasttail 192 | shl eax, 4 ; Quick multiply by 16 193 | add rdi, rax ; Add offset to RDI 194 | 195 | ; Write to the descriptor 196 | mov rax, rsi 197 | stosq ; Store the data location 198 | mov rax, rcx ; The packet size is in CX 199 | bts rax, 24 ; TDESC.CMD.EOP (0) - End Of Packet 200 | bts rax, 25 ; TDESC.CMD.IFCS (1) - Insert FCS (CRC) 201 | bts rax, 27 ; TDESC.CMD.RS (3) - Report Status 202 | stosq 203 | 204 | ; Increment i8257x_tx_lasttail and the Transmit Descriptor Tail 205 | pop rax ; Restore lasttail 206 | add eax, 1 207 | and eax, i8257x_MAX_DESC - 1 208 | mov [i8257x_tx_lasttail], eax 209 | mov rdi, [os_NetIOBaseMem] 210 | mov [rdi+i8257x_TDT], eax ; TDL - Transmit Descriptor Tail 211 | 212 | pop rax 213 | pop rdi 214 | ret 215 | ; ----------------------------------------------------------------------------- 216 | 217 | 218 | ; ----------------------------------------------------------------------------- 219 | ; net_i8257x_poll - Polls the Intel 8257x NIC for a received packet 220 | ; IN: RDI = Location to store packet 221 | ; OUT: RCX = Length of packet 222 | ; Note: RDESC Descriptor Format: 223 | ; First Qword: 224 | ; Bits 63:0 - Buffer Address 225 | ; Second Qword: 226 | ; Bits 15:0 - Length 227 | ; Bits 31:16 - Fragment Checksum 228 | ; Bits 39:32 - STA - Status 229 | ; Bits 47:40 - Errors 230 | ; Bits 63:48 - VLAN 231 | net_i8257x_poll: 232 | push rdi 233 | push rsi ; Used for the base MMIO of the NIC 234 | push rax 235 | 236 | mov rdi, os_rx_desc 237 | mov rsi, [os_NetIOBaseMem] ; Load the base MMIO of the NIC 238 | 239 | ; Calculate the descriptor to read from 240 | mov eax, [i8257x_rx_lasthead] 241 | shl eax, 4 ; Quick multiply by 16 242 | add eax, 8 ; Offset to bytes received 243 | add rdi, rax ; Add offset to RDI 244 | ; Todo: read all 64 bits. check status bit for DD 245 | xor ecx, ecx ; Clear RCX 246 | mov cx, [rdi] ; Get the packet length 247 | cmp cx, 0 248 | je net_i8257x_poll_end ; No data? Bail out 249 | 250 | xor eax, eax 251 | stosq ; Clear the descriptor length and status 252 | 253 | ; Increment i8257x_rx_lasthead and the Receive Descriptor Tail 254 | mov eax, [i8257x_rx_lasthead] 255 | add eax, 1 256 | and eax, i8257x_MAX_DESC - 1 257 | mov [i8257x_rx_lasthead], eax 258 | mov eax, [rsi+i8257x_RDT] ; Read the current Receive Descriptor Tail 259 | add eax, 1 ; Add 1 to the Receive Descriptor Tail 260 | and eax, i8257x_MAX_DESC - 1 261 | mov [rsi+i8257x_RDT], eax ; Write the updated Receive Descriptor Tail 262 | 263 | pop rax 264 | pop rsi 265 | pop rdi 266 | ret 267 | 268 | net_i8257x_poll_end: 269 | xor ecx, ecx 270 | pop rax 271 | pop rsi 272 | pop rdi 273 | ret 274 | ; ----------------------------------------------------------------------------- 275 | 276 | 277 | ; Variables 278 | i8257x_tx_lasttail: dd 0 279 | i8257x_rx_lasthead: dd 0 280 | 281 | ; Constants 282 | i8257x_MAX_PKT_SIZE equ 16384 283 | i8257x_MAX_DESC equ 16 ; Must be 16, 32, 64, 128, etc. 284 | 285 | ; Register list (13.3) (All registers should be accessed as 32-bit values) 286 | 287 | ; General Control Registers 288 | i8257x_CTRL equ 0x00000 ; Device Control Register 289 | i8257x_CTRL_Legacy equ 0x00004 ; Copy of Device Control Register 290 | i8257x_STATUS equ 0x00008 ; Device Status Register 291 | i8257x_CTRL_EXT equ 0x00018 ; Extended Device Control Register 292 | i8257x_MDIC equ 0x00020 ; MDI Control Register 293 | i8257x_LEDCTL equ 0x00E00 ; LED Control 294 | 295 | ; EEPROM / Flash Registers 296 | 297 | ; Interrupts Registers 298 | i8257x_ICR equ 0x000C0 ; Interrupt Cause Read 299 | i8257x_ITR equ 0x000C4 ; Interrupt Throttling Rate 300 | i8257x_ICS equ 0x000C8 ; Interrupt Cause Set 301 | i8257x_IMS equ 0x000D0 ; Interrupt Mask Set/Read 302 | i8257x_IMC equ 0x000D8 ; Interrupt Mask Clear 303 | i8257x_IAM equ 0x000E0 ; Interrupt Acknowledge Auto Mask 304 | 305 | ; Receive Registers 306 | i8257x_RCTL equ 0x00100 ; Receive Control 307 | i8257x_RDBAL equ 0x02800 ; Receive Descriptor Base Address Low Queue 0 308 | i8257x_RDBAH equ 0x02804 ; Receive Descriptor Base Address High Queue 0 309 | i8257x_RDLEN equ 0x02808 ; Receive Descriptor Ring Length Queue 0 310 | i8257x_RDH equ 0x02810 ; Receive Descriptor Head Queue 0 311 | i8257x_RDT equ 0x02818 ; Receive Descriptor Tail Queue 0 312 | i8257x_RDTR equ 0x02820 ; Receive Interrupt Packet Delay Timer 313 | i8257x_RXDCTL equ 0x02828 ; Receive Descriptor Control Queue 0 314 | i8257x_RADV equ 0x0282C ; Receive Interrupt Absolute Delay Timer 315 | i8257x_RSRPD equ 0x02C00 ; Receive Small Packet Detect 316 | i8257x_RXCSUM equ 0x05000 ; Receive Checksum Control 317 | i8257x_RLPML equ 0x05004 ; Receive Long packet maximal length 318 | i8257x_RFCTL equ 0x05008 ; Receive Filter Control Register 319 | i8257x_MTA equ 0x05200 ; Multicast Table Array (n) 320 | i8257x_RAL equ 0x05400 ; Receive Address Low (Lower 32-bits of 48-bit address) 321 | i8257x_RAH equ 0x05404 ; Receive Address High (Upper 16-bits of 48-bit address). Bit 31 should be set for Address Valid 322 | 323 | ; Transmit Registers 324 | i8257x_TCTL equ 0x00400 ; Transmit Control 325 | i8257x_TIPG equ 0x00410 ; Transmit IPG (Inter Packet Gap) 326 | i8257x_TDBAL equ 0x03800 ; Transmit Descriptor Base Address Low 327 | i8257x_TDBAH equ 0x03804 ; Transmit Descriptor Base Address High 328 | i8257x_TDLEN equ 0x03808 ; Transmit Descriptor Length (Bits 19:0 in bytes, 128-byte aligned) 329 | i8257x_TDH equ 0x03810 ; Transmit Descriptor Head (Bits 15:0) 330 | i8257x_TDT equ 0x03818 ; Transmit Descriptor Tail (Bits 15:0) 331 | i8257x_TIDV equ 0x03820 ; Transmit Interrupt Delay Value 332 | i8257x_TXDCTL equ 0x03828 ; Transmit Descriptor Control (Bit 25 - Enable) 333 | i8257x_TADV equ 0x0382C ; Transmit Absolute Interrupt Delay Value 334 | i8257x_TARC0 equ 0x03840 ; Transmit Arbitration Counter Queue 0 335 | 336 | ; Statistic Registers 337 | i8257x_GPRC equ 0x04074 ; Good Packets Received Count 338 | i8257x_BPRC equ 0x04078 ; Broadcast Packets Received Count 339 | i8257x_MPRC equ 0x0407C ; Multicast Packets Received Count 340 | i8257x_GPTC equ 0x04080 ; Good Packets Transmitted Count 341 | i8257x_GORCL equ 0x04088 ; Good Octets Received Count Low 342 | i8257x_GORCH equ 0x0408C ; Good Octets Received Count High 343 | i8257x_GOTCL equ 0x04090 ; Good Octets Transmitted Count Low 344 | i8257x_GOTCH equ 0x04094 ; Good Octets Transmitted Count High 345 | 346 | ; Register bits 347 | 348 | ; CTRL (Device Control Register, 0x00000 / 0x00004, RW) Bit Masks 349 | i8257x_CTRL_FD equ 0 ; Full-Duplex 350 | i8257x_CTRL_GIO equ 2 ; GIO Master Disable 351 | i8257x_CTRL_LRST equ 3 ; Link Reset 352 | i8257x_CTRL_SLU equ 6 ; Set Link Up 353 | i8257x_CTRL_SPEED equ 8 ; 2 bits - Speed selection 354 | i8257x_CTRL_FRCSPD equ 11 ; Force Speed 355 | i8257x_CTRL_FRCDPLX equ 12 ; Force Duplex 356 | i8257x_CTRL_RST equ 26 ; Device Reset 357 | i8257x_CTRL_RFCE equ 27 ; Receive Flow Control Enable 358 | i8257x_CTRL_TFCE equ 28 ; Transmit Flow Control Enable 359 | i8257x_CTRL_VME equ 30 ; VLAN Mode Enable 360 | i8257x_CTRL_PHY_RST equ 31 ; PHY Reset 361 | ; All other bits are reserved and should be written as 0 362 | i8257x_CTRL_RST_MASK equ 1 << i8257x_CTRL_LRST | 1 << i8257x_CTRL_RST 363 | 364 | ; STATUS (Device Status Register, 0x00008, R) 365 | i8257x_STATUS_FD equ 0 ; Link Full Duplex configuration Indication 366 | i8257x_STATUS_LU equ 1 ; Link Up Indication 367 | i8257x_STATUS_LANID equ 2 ; 2 bits - LAN ID 368 | i8257x_STATUS_TXOFF equ 4 ; Transmission Paused 369 | i8257x_STATUS_TBIMODE equ 5 ; TBI Mode 370 | i8257x_STATUS_SPEED equ 6 ; 2 bits - Link speed setting 371 | i8257x_STATUS_ASDV equ 8 ; 2 bits - Auto Speed Detection Value 372 | i8257x_STATUS_PHYRA equ 10 ; PHY Reset Asserted 373 | i8257x_STATUS_GIO equ 19 ; GIO Master Enable Status 374 | 375 | ; CTRL_EXT (Extended Device Control Register, 0x00018, RW) Bit Masks 376 | i8257x_CTRL_EXT_ASDCHK equ 12 ; ASD Check 377 | i8257x_CTRL_EXT_DRV_LOAD equ 28 ; Driver loaded and the corresponding network interface is enabled 378 | 379 | ; RCTL (Receive Control Register, 0x00100, RW) Bit Masks 380 | i8257x_RCTL_EN equ 1 ; Receive Enable 381 | i8257x_RCTL_SBP equ 2 ; Store Bad Packets 382 | i8257x_RCTL_UPE equ 3 ; Unicast Promiscuous Enabled 383 | i8257x_RCTL_MPE equ 4 ; Multicast Promiscuous Enabled 384 | i8257x_RCTL_LPE equ 5 ; Long Packet Reception Enable 385 | i8257x_RCTL_BAM equ 15 ; Broadcast Accept Mode 386 | i8257x_RCTL_SECRC equ 26 ; Strip Ethernet CRC from incoming packet 387 | 388 | ; TCTL (Transmit Control Register, 0x00400, RW) Bit Masks 389 | i8257x_TCTL_EN equ 1 ; Transmit Enable 390 | i8257x_TCTL_PSP equ 3 ; Pad Short Packets 391 | i8257x_TCTL_CT equ 4 ; Collision Threshold (11:4) 392 | i8257x_TCTL_COLD equ 12 ; Collision Distance (21:12) 393 | i8257x_TCTL_RRTHRESH equ 29 ; Read Request Threshold (30:29) 394 | 395 | ; All other bits are reserved and should be written as 0 396 | 397 | i8257x_IRQ_CLEAR_MASK equ 0xFFFFFFFF 398 | 399 | ; ============================================================================= 400 | ; EOF 401 | -------------------------------------------------------------------------------- /src/drivers/net/r8169.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; Realtek 816x/811x Gigabit Ethernet Driver 6 | ; ============================================================================= 7 | 8 | 9 | ; ----------------------------------------------------------------------------- 10 | ; Initialize a Realtek 816x NIC 11 | ; IN: RDX = Packed Bus address (as per syscalls/bus.asm) 12 | net_r8169_init: 13 | push rsi 14 | push rdx 15 | push rcx 16 | push rax 17 | 18 | ; Get the Base I/O Address of the device 19 | mov dl, 0x04 ; BAR0 20 | call os_bus_read 21 | and eax, 0xFFFFFFFC ; EAX now holds the Base IO Address (clear the low 2 bits) 22 | mov word [os_NetIOAddress], ax 23 | 24 | ; Set PCI Status/Command values 25 | mov dl, 0x01 ; Read Status/Command 26 | call os_bus_read 27 | bts eax, 10 ; Set Interrupt Disable 28 | bts eax, 2 ; Enable Bus Master 29 | bts eax, 0 ; Enable I/O port Space 30 | call os_bus_write ; Write updated Status/Command 31 | 32 | ; Get the MAC address 33 | mov dx, word [os_NetIOAddress] 34 | in al, dx 35 | mov [os_NetMAC], al 36 | inc dx 37 | in al, dx 38 | mov [os_NetMAC+1], al 39 | inc dx 40 | in al, dx 41 | mov [os_NetMAC+2], al 42 | inc dx 43 | in al, dx 44 | mov [os_NetMAC+3], al 45 | inc dx 46 | in al, dx 47 | mov [os_NetMAC+4], al 48 | inc dx 49 | in al, dx 50 | mov [os_NetMAC+5], al 51 | 52 | ; Reset the device 53 | call net_r8169_reset 54 | 55 | pop rax 56 | pop rcx 57 | pop rdx 58 | pop rsi 59 | ret 60 | ; ----------------------------------------------------------------------------- 61 | 62 | 63 | ; ----------------------------------------------------------------------------- 64 | ; os_net_rtl8136_reset - Reset a Realtek 8169 NIC 65 | ; IN: Nothing 66 | ; OUT: Nothing, all registers preserved 67 | net_r8169_reset: 68 | push rdx 69 | push rcx 70 | push rax 71 | 72 | mov dx, word [os_NetIOAddress] 73 | add dx, R8169_REG_COMMAND 74 | mov al, 0x10 ; Bit 4 set for Reset 75 | out dx, al 76 | mov cx, 1000 ; Wait no longer for the reset to complete 77 | wait_for_8169_reset: 78 | in al, dx 79 | test al, 0x10 80 | jz reset_8169_completed ; RST remains 1 during reset, Reset complete when 0 81 | dec cx 82 | jns wait_for_8169_reset 83 | reset_8169_completed: 84 | 85 | ; Unlock config registers 86 | mov dx, word [os_NetIOAddress] 87 | add dx, R8169_REG_9346CR 88 | mov al, 0xC0 ; Unlock 89 | out dx, al 90 | 91 | ; Set the C+ Command 92 | mov dx, word [os_NetIOAddress] 93 | add dx, R8169_REG_CCR 94 | in ax, dx 95 | bts ax, 3 ; Enable PCI Multiple Read/Write 96 | btc ax, 9 ; Little-endian mode 97 | out dx, ax 98 | 99 | ; Power management? 100 | 101 | ; Receive configuration 102 | mov dx, word [os_NetIOAddress] 103 | add edx, R8169_REG_RCR 104 | mov eax, 0x0000E70A ; Set bits 1 (APM), 3 (AB), 8-10 (Unlimited), 13-15 (No limit) 105 | out dx, eax 106 | 107 | ; Set up TCR 108 | mov dx, word [os_NetIOAddress] 109 | add dx, R8169_REG_TCR 110 | mov eax, 0x03000700 111 | out dx, eax 112 | 113 | ; Setup max RX size 114 | mov dx, word [os_NetIOAddress] 115 | add dx, R8169_REG_MAXRX 116 | mov ax, 0x3FFF ; 16384 - 1 117 | out dx, ax 118 | 119 | ; Setup max TX size 120 | mov dx, word [os_NetIOAddress] 121 | add dx, R8169_REG_MAXTX 122 | mov al, 0x3B 123 | out dx, al 124 | 125 | ; Set the Transmit Normal Priority Descriptor Start Address 126 | mov dx, word [os_NetIOAddress] 127 | add dx, R8169_REG_TNPDS 128 | mov rax, os_tx_desc 129 | out dx, eax ; Write the low bits 130 | shr rax, 32 131 | add dx, 4 132 | out dx, eax ; Write the high bits 133 | mov eax, 0x70000000 ; Set bit 30 (End of Descriptor Ring), 29 (FS), and 28 (LS) 134 | mov [os_tx_desc], eax 135 | 136 | ; Set the Receive Descriptor Start Address 137 | mov dx, word [os_NetIOAddress] 138 | add dx, R8169_REG_RDSAR 139 | mov rax, os_rx_desc 140 | out dx, eax ; Write the low bits 141 | shr rax, 32 142 | add dx, 4 143 | out dx, eax ; Write the high bits 144 | mov eax, 0x80001FF8 ; Set bits 31 (Ownership), also buffer size (Max 0x1FF8) 145 | mov [os_rx_desc], eax 146 | mov rax, os_PacketBuffers 147 | mov [os_rx_desc+8], rax 148 | mov eax, 0xC0001FF8 ; Set bits 31 (Ownership) and 30 (End of Descriptor Ring), also buffer size (Max 0x1FF8) 149 | mov [os_rx_desc+16], eax 150 | mov rax, os_PacketBuffers 151 | mov [os_rx_desc+24], rax 152 | 153 | ; Initialize multicast registers (no filtering) 154 | mov eax, 0xFFFFFFFF 155 | mov dx, word [os_NetIOAddress] 156 | add dx, R8169_REG_MAR0 157 | out dx, eax 158 | add dx, 4 ; MAR4 159 | out dx, eax 160 | 161 | ; Enable Rx/Tx in the Command register 162 | mov dx, word [os_NetIOAddress] 163 | add dx, R8169_REG_COMMAND 164 | mov al, (1 << R8169_BIT_RE) | (1 << R8169_BIT_TE) ; Set bits 2 (TE) and 3 (RE) 165 | out dx, al 166 | 167 | ; Enable Receive and Transmit interrupts 168 | mov dx, word [os_NetIOAddress] 169 | add dx, R8169_REG_IMR 170 | mov ax, 0x0005 ; Set bits 0 (RX OK) and 2 (TX OK) 171 | out dx, ax 172 | 173 | ; Lock config register 174 | mov dx, word [os_NetIOAddress] 175 | add dx, R8169_REG_9346CR 176 | xor al, al ; Lock 177 | out dx, al 178 | 179 | pop rax 180 | pop rcx 181 | pop rdx 182 | ret 183 | ; ----------------------------------------------------------------------------- 184 | 185 | 186 | ; ----------------------------------------------------------------------------- 187 | ; net_r8169_transmit - Transmit a packet via a Realtek 8169 NIC 188 | ; IN: RSI = Location of packet 189 | ; RCX = Length of packet 190 | ; OUT: Nothing 191 | ; ToDo: Check for proper timeout 192 | net_r8169_transmit: 193 | push rdi 194 | push rsi 195 | push rdx 196 | push rcx 197 | push rax 198 | 199 | mov rdi, os_tx_desc 200 | mov rax, rcx 201 | stosw ; Store the frame length 202 | add rdi, 6 ; Should the other data be cleared here? 203 | mov rax, rsi 204 | stosq ; Store the packet location 205 | or dword [os_tx_desc], 0xF0000000 ; Set bit 31 (OWN), 30 (EOR), 29 (FS), and 28 (LS) 206 | mov dx, word [os_NetIOAddress] 207 | add dx, R8169_REG_TPPOLL 208 | mov al, 0x40 209 | out dx, al ; Set up TX Polling 210 | net_r8169_transmit_sendloop: 211 | mov eax, [os_tx_desc] 212 | and eax, 0x80000000 ; Check the ownership bit (BT command instead?) 213 | cmp eax, 0x80000000 ; If the ownership bit is clear then the NIC sent the packet 214 | je net_r8169_transmit_sendloop 215 | 216 | pop rax 217 | pop rcx 218 | pop rdx 219 | pop rsi 220 | pop rdi 221 | ret 222 | ; ----------------------------------------------------------------------------- 223 | 224 | 225 | ; ----------------------------------------------------------------------------- 226 | ; net_r8169_poll - Polls the Realtek 8169 NIC for a received packet 227 | ; IN: RDI = Location to store packet 228 | ; OUT: RCX = Length of packet 229 | net_r8169_poll: 230 | push rdi 231 | push rsi 232 | push rdx 233 | push rax 234 | 235 | xor ecx, ecx 236 | mov cx, [os_rx_desc] 237 | and cx, 0x3FFF ; Clear the two high bits as length is bits 13-0 238 | cmp cx, 0x1FF8 239 | jne net_r8169_poll_first_descriptor 240 | mov cx, [os_rx_desc+16] 241 | and cx, 0x3FFF ; Clear the two high bits as length is bits 13-0 242 | net_r8169_poll_first_descriptor: 243 | mov rsi, os_PacketBuffers 244 | push rcx 245 | rep movsb ; Copy the packet to the location stored in RDI 246 | pop rcx 247 | mov eax, 0x80001FF8 ; Set bits 31 (Ownership), also buffer size (Max 0x1FF8) 248 | mov [os_rx_desc], eax 249 | mov rax, os_PacketBuffers 250 | mov [os_rx_desc+8], rax 251 | mov eax, 0xC0001FF8 ; Set bits 31 (Ownership) and 30 (End of Descriptor Ring), also buffer size (Max 0x1FF8) 252 | mov [os_rx_desc+16], eax 253 | mov rax, os_PacketBuffers 254 | mov [os_rx_desc+24], rax 255 | 256 | pop rax 257 | pop rdx 258 | pop rsi 259 | pop rdi 260 | ret 261 | ; ----------------------------------------------------------------------------- 262 | 263 | 264 | ; ----------------------------------------------------------------------------- 265 | ; net_r8169_ack_int - Acknowledge an internal interrupt of the Realtek 8169 NIC 266 | ; IN: Nothing 267 | ; OUT: RAX = Ethernet status 268 | ; Uses RDI 269 | net_r8169_ack_int: 270 | push rdx 271 | mov dx, word [os_NetIOAddress] ; Clear active interrupt sources 272 | add dx, R8169_REG_ISR 273 | in ax, dx 274 | out dx, ax 275 | shr eax, 2 276 | pop rdx 277 | ret 278 | ; ----------------------------------------------------------------------------- 279 | 280 | 281 | ; Register Descriptors 282 | R8169_REG_IDR0 equ 0x00 ; ID Register 0 283 | R8169_REG_IDR1 equ 0x01 ; ID Register 1 284 | R8169_REG_IDR2 equ 0x02 ; ID Register 2 285 | R8169_REG_IDR3 equ 0x03 ; ID Register 3 286 | R8169_REG_IDR4 equ 0x04 ; ID Register 4 287 | R8169_REG_IDR5 equ 0x05 ; ID Register 5 288 | R8169_REG_MAR0 equ 0x08 ; Multicast Register 0 289 | R8169_REG_MAR1 equ 0x09 ; Multicast Register 1 290 | R8169_REG_MAR2 equ 0x0A ; Multicast Register 2 291 | R8169_REG_MAR3 equ 0x0B ; Multicast Register 3 292 | R8169_REG_MAR4 equ 0x0C ; Multicast Register 4 293 | R8169_REG_MAR5 equ 0x0D ; Multicast Register 5 294 | R8169_REG_MAR6 equ 0x0E ; Multicast Register 6 295 | R8169_REG_MAR7 equ 0x0F ; Multicast Register 7 296 | R8169_REG_TNPDS equ 0x20 ; Transmit Normal Priority Descriptors: Start address (64-bit). (256-byte alignment) 297 | R8169_REG_COMMAND equ 0x37 ; Command Register 298 | R8169_REG_TPPOLL equ 0x38 ; Transmit Priority Polling Register 299 | R8169_REG_IMR equ 0x3C ; Interrupt Mask Register 300 | R8169_REG_ISR equ 0x3E ; Interrupt Status Register 301 | R8169_REG_TCR equ 0x40 ; Transmit (Tx) Configuration Register 302 | R8169_REG_RCR equ 0x44 ; Receive (Rx) Configuration Register 303 | R8169_REG_9346CR equ 0x50 ; 93C46 (93C56) Command Register 304 | R8169_REG_CONFIG0 equ 0x51 ; Configuration Register 0 305 | R8169_REG_CONFIG1 equ 0x52 ; Configuration Register 1 306 | R8169_REG_CONFIG2 equ 0x53 ; Configuration Register 2 307 | R8169_REG_CONFIG3 equ 0x54 ; Configuration Register 3 308 | R8169_REG_CONFIG4 equ 0x55 ; Configuration Register 4 309 | R8169_REG_CONFIG5 equ 0x56 ; Configuration Register 5 310 | R8169_REG_PHYAR equ 0x60 ; PHY Access Register 311 | R8169_REG_PHYStatus equ 0x6C ; PHY(GMII, MII, or TBI) Status Register 312 | R8169_REG_MAXRX equ 0xDA ; Mac Receive Packet Size Register 313 | R8169_REG_CCR equ 0xE0 ; C+ Command Register 314 | R8169_REG_RDSAR equ 0xE4 ; Receive Descriptor Start Address Register (256-byte alignment) 315 | R8169_REG_MAXTX equ 0xEC ; Max Transmit Packet Size Register 316 | 317 | ; Command Register (Offset 0037h, R/W) 318 | R8169_BIT_RST equ 4 ; Reset 319 | R8169_BIT_RE equ 3 ; Receiver Enable 320 | R8169_BIT_TE equ 2 ; Transmitter Enable 321 | 322 | ; Receive Configuration (Offset 0044h-0047h, R/W) 323 | R8169_BIT_AER equ 5 ; Accept Error 324 | R8169_BIT_AR equ 4 ; Accept Runt 325 | R8169_BIT_AB equ 3 ; Accept Broadcast Packets 326 | R8169_BIT_AM equ 2 ; Accept Multicast Packets 327 | R8169_BIT_APM equ 1 ; Accept Physical Match Packets 328 | R8169_BIT_AAP equ 0 ; Accept All Packets with Destination Address 329 | 330 | ; PHY Register Table 331 | ; BMCR (address 0x00) 332 | R8169_BIT_ANE equ 12 ; Auto-Negotiation Enable 333 | 334 | ; ============================================================================= 335 | ; EOF -------------------------------------------------------------------------------- /src/drivers/nvs/ata.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; ATA Driver 6 | ; ============================================================================= 7 | 8 | 9 | ; NOTES: 10 | ; 11 | ; ATA is a legacy technology and, ideally, should not be used - ever 12 | ; It is here strictly to support disk access under Bochs 13 | ; 14 | ; These functions use LBA28. Maximum visible drive size is 128GiB 15 | ; LBA48 would be needed to access sectors over 128GiB (up to 128PiB) 16 | ; 17 | ; These functions are hard coded to access the Primary Master HDD only 18 | 19 | 20 | ; ----------------------------------------------------------------------------- 21 | ata_init: 22 | bts word [os_nvsVar], 2 ; Set the bit flag that ATA has been initialized 23 | mov rdi, os_nvs_io 24 | mov rax, ata_io 25 | stosq 26 | mov rax, ata_id 27 | stosq 28 | add rsi, 15 29 | mov byte [rsi], 1 ; Mark driver as installed in Bus Table 30 | sub rsi, 15 31 | ret 32 | ; ----------------------------------------------------------------------------- 33 | 34 | 35 | ; ----------------------------------------------------------------------------- 36 | ; ata_io -- Perform an I/O operation on an IDE device 37 | ; IN: RAX = starting sector # (28-bit LBA address) 38 | ; RBX = I/O Opcode 39 | ; RCX = number of sectors 40 | ; RDX = drive # 41 | ; RDI = memory location used for reading/writing data from/to device 42 | ; OUT: Nothing 43 | ; All other registers preserved 44 | ata_io: 45 | push rdi 46 | push rdx 47 | push rcx 48 | push rbx 49 | push rax 50 | 51 | shl rcx, 3 ; Quick multiply by 8 as BareMetal deals in 4K sectors 52 | shl rax, 3 ; Same for the starting sector number 53 | push rcx ; Save RCX for use in the read loop 54 | mov rbx, rcx ; Store number of sectors to read 55 | 56 | ; A single request can read 128KiB 57 | cmp rcx, 256 58 | ja ata_io_fail ; Over 256? Fail! 59 | ; TODO - Don't fail. Break the read into multiple requests 60 | jne ata_io_skip ; Not 256? No need to modify CL 61 | xor rcx, rcx ; 0 translates to 256 62 | ata_io_skip: 63 | 64 | push rax ; Save sector number 65 | mov dx, ATA_PSC ; 0x01F2 - Sector count Port 7:0 66 | mov al, cl ; Read CL sectors 67 | out dx, al 68 | pop rax ; Restore number number 69 | inc dx ; 0x01F3 - LBA Low Port 7:0 70 | out dx, al 71 | inc dx ; 0x01F4 - LBA Mid Port 15:8 72 | shr rax, 8 73 | out dx, al 74 | inc dx ; 0x01F5 - LBA High Port 23:16 75 | shr rax, 8 76 | out dx, al 77 | inc dx ; 0x01F6 - Device Port. Bit 6 set for LBA mode, Bit 4 for device (0 = master, 1 = slave), Bits 3-0 for LBA "Extra High" (27:24) 78 | shr rax, 8 79 | and al, 00001111b ; Clear bits 4-7 just to be safe 80 | or al, 01000000b ; Turn bit 6 on since we want to use LBA addressing, leave device at 0 (master) 81 | out dx, al 82 | inc dx ; 0x01F7 - Command Port 83 | mov al, 0x20 ; Read sector(s). 0x24 if LBA48 84 | out dx, al 85 | 86 | mov rcx, 4 87 | ata_io_wait: 88 | in al, dx ; Read status from 0x01F7 89 | test al, 0x80 ; BSY flag set? 90 | jne ata_io_retry 91 | test al, 0x08 ; DRQ set? 92 | jne ata_io_dataready 93 | ata_io_retry: 94 | dec rcx 95 | ja ata_io_wait 96 | ata_io_nextsector: 97 | in al, dx ; Read status from 0x01F7 98 | test al, 0x80 ; BSY flag set? 99 | jne ata_io_nextsector 100 | test al, 0x21 ; ERR or DF set? 101 | jne ata_io_fail 102 | 103 | ata_io_dataready: 104 | sub dx, 7 ; Data port (0x1F0) 105 | mov rcx, 256 ; Read 106 | rep insw ; Copy a 512 byte sector to RDI 107 | add dx, 7 ; Set DX back to status register (0x01F7) 108 | in al, dx ; Delay ~400ns to allow drive to set new values of BSY and DRQ 109 | in al, dx 110 | in al, dx 111 | in al, dx 112 | 113 | dec rbx ; RBX is the "sectors to read" counter 114 | cmp rbx, 0 115 | jne ata_io_nextsector 116 | 117 | pop rcx 118 | pop rax 119 | inc rax 120 | pop rbx 121 | pop rcx 122 | pop rdx 123 | pop rdi 124 | ret 125 | 126 | ata_io_fail: 127 | pop rcx 128 | pop rax 129 | pop rbx 130 | pop rcx 131 | pop rdx 132 | pop rdi 133 | xor rcx, rcx ; Set RCX to 0 since nothing was read 134 | ret 135 | ; ----------------------------------------------------------------------------- 136 | 137 | 138 | ; ----------------------------------------------------------------------------- 139 | ; ata_id -- Perform an ID operation on an IDE device 140 | ; IN: RAX = starting sector # (28-bit LBA address) 141 | ; RBX = I/O Opcode 142 | ; RCX = number of sectors 143 | ; RDX = drive # 144 | ; RDI = memory location used for reading/writing data from/to device 145 | ; OUT: Nothing 146 | ; All other registers preserved 147 | ata_id: 148 | ret 149 | ; ----------------------------------------------------------------------------- 150 | 151 | 152 | ; Port Registers 153 | ATA_PDATA equ 0x1F0 154 | ATA_PERR equ 0x1F0 155 | ATA_PFEAT equ 0x1F1 156 | ATA_PSC equ 0x1F2 157 | ATA_LBALO equ 0x1F3 158 | ATA_LBAMID equ 0x1F4 159 | ATA_LBAHI equ 0x1F5 160 | ATA_PHEAD equ 0x1F6 161 | ATA_PSTATUS equ 0x1F7 162 | ATA_PCMD equ 0x1F7 163 | 164 | ; Opcodes for IDE Commands 165 | ATA_Write equ 0x30 166 | ATA_Read equ 0x20 167 | ATA_Identify equ 0xEC 168 | 169 | 170 | ; ============================================================================= 171 | ; EOF -------------------------------------------------------------------------------- /src/drivers/nvs/virtio-blk.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; Virtio Block Driver 6 | ; ============================================================================= 7 | 8 | 9 | ; ----------------------------------------------------------------------------- 10 | virtio_blk_init: 11 | push rsi 12 | push rdx ; RDX should already point to a supported device for os_bus_read/write 13 | push rbx 14 | push rax 15 | 16 | ; Verify this driver supports the Vendor 17 | mov eax, [rsi+4] ; Offset to Vendor/Device ID in the Bus Table 18 | mov rsi, virtio_blk_driverid 19 | mov bx, [rsi] ; Load the Vendor (0x1AF4) 20 | cmp ax, bx 21 | jne virtio_blk_init_error ; Bail out if it wasn't a match 22 | 23 | ; Verify this driver support the Device 24 | shr eax, 16 ; Move Device ID into AX 25 | virtio_blk_init_next_dev: 26 | add rsi, 2 27 | mov bx, [rsi] ; Load the Device 28 | cmp bx, 0 ; End of list? 29 | je virtio_blk_init_error ; If so, bail out 30 | cmp ax, bx ; Check against the list 31 | jne virtio_blk_init_next_dev ; No match? Try next entry 32 | 33 | ; Grab the Base I/O Address of the device 34 | mov al, 4 ; Read BAR4 35 | call os_bus_read_bar 36 | mov [os_virtioblk_base], rax ; Save it as the base 37 | mov rsi, rax ; RSI holds the base for MMIO 38 | 39 | ; Set PCI Status/Command values 40 | mov dl, 0x01 ; Read Status/Command 41 | call os_bus_read 42 | bts eax, 10 ; Set Interrupt Disable 43 | bts eax, 2 ; Enable Bus Master 44 | bts eax, 1 ; Enable Memory Space 45 | call os_bus_write ; Write updated Status/Command 46 | 47 | ; Gather required values from PCI Capabilities 48 | mov dl, 1 49 | call os_bus_read ; Read register 1 for Status/Command 50 | bt eax, 20 ; Check bit 4 of the Status word (31:16) 51 | jnc virtio_blk_init_error ; If if doesn't exist then bail out 52 | mov dl, 13 53 | call os_bus_read ; Read register 13 for the Capabilities Pointer (7:0) 54 | and al, 0xFC ; Clear the bottom two bits as they are reserved 55 | 56 | virtio_blk_init_cap_next: 57 | shr al, 2 ; Quick divide by 4 58 | mov dl, al 59 | call os_bus_read 60 | cmp al, VIRTIO_PCI_CAP_VENDOR_CFG 61 | je virtio_blk_init_cap 62 | shr eax, 8 63 | jmp virtio_blk_init_cap_next_offset 64 | 65 | virtio_blk_init_cap: 66 | rol eax, 8 ; Move Virtio cfg_type to AL 67 | cmp al, VIRTIO_PCI_CAP_COMMON_CFG 68 | je virtio_blk_init_cap_common 69 | cmp al, VIRTIO_PCI_CAP_NOTIFY_CFG 70 | je virtio_blk_init_cap_notify 71 | ror eax, 16 ; Move next entry offset to AL 72 | jmp virtio_blk_init_cap_next_offset 73 | 74 | virtio_blk_init_cap_common: 75 | push rdx 76 | ; TODO Check for BAR4 and offset of 0x0 77 | pop rdx 78 | jmp virtio_blk_init_cap_next_offset 79 | 80 | virtio_blk_init_cap_notify: 81 | push rdx 82 | inc dl 83 | call os_bus_read 84 | pop rdx 85 | cmp al, 0x04 ; Needs to be BAR4 86 | jne virtio_blk_init_error 87 | push rdx 88 | add dl, 2 89 | call os_bus_read 90 | mov [notify_offset], eax 91 | add dl, 2 ; Skip Length 92 | call os_bus_read 93 | mov [notify_offset_multiplier], eax 94 | pop rdx 95 | jmp virtio_blk_init_cap_next_offset 96 | 97 | virtio_blk_init_cap_next_offset: 98 | call os_bus_read 99 | shr eax, 8 ; Shift pointer to AL 100 | cmp al, 0x00 ; End of linked list? 101 | jne virtio_blk_init_cap_next ; If not, continue reading 102 | 103 | virtio_blk_init_cap_end: 104 | 105 | ; Device Initialization (section 3.1) 106 | 107 | ; 3.1.1 - Step 1 - Reset the device (section 2.4) 108 | mov al, 0x00 109 | mov [rsi+VIRTIO_DEVICE_STATUS], al 110 | virtio_blk_init_reset_wait: 111 | mov al, [rsi+VIRTIO_DEVICE_STATUS] 112 | cmp al, 0x00 113 | jne virtio_blk_init_reset_wait 114 | 115 | ; 3.1.1 - Step 2 - Tell the device we see it 116 | mov al, VIRTIO_STATUS_ACKNOWLEDGE 117 | mov [rsi+VIRTIO_DEVICE_STATUS], al 118 | 119 | ; 3.1.1 - Step 3 - Tell the device we support it 120 | mov al, VIRTIO_STATUS_ACKNOWLEDGE | VIRTIO_STATUS_DRIVER 121 | mov [rsi+VIRTIO_DEVICE_STATUS], al 122 | 123 | ; 3.1.1 - Step 4 124 | ; Process the first 32-bits of Feature bits 125 | xor eax, eax 126 | mov [rsi+VIRTIO_DEVICE_FEATURE_SELECT], eax 127 | mov eax, [rsi+VIRTIO_DEVICE_FEATURE] 128 | ; btc eax, VIRTIO_BLK_F_MQ ; Disable Multiqueue support for this driver 129 | ; btc eax, VIRTIO_F_INDIRECT_DESC 130 | mov eax, 0x44 ; Only support BLK_SIZE (6) & SEG_MAX (2) 131 | push rax 132 | xor eax, eax 133 | mov [rsi+VIRTIO_DRIVER_FEATURE_SELECT], eax 134 | pop rax 135 | mov [rsi+VIRTIO_DRIVER_FEATURE], eax 136 | ; Process the next 32-bits of Feature bits 137 | mov eax, 1 138 | mov [rsi+VIRTIO_DEVICE_FEATURE_SELECT], eax 139 | mov eax, [rsi+VIRTIO_DEVICE_FEATURE] 140 | and eax, 1 141 | push rax 142 | mov eax, 1 143 | mov [rsi+VIRTIO_DRIVER_FEATURE_SELECT], eax 144 | pop rax 145 | mov [rsi+VIRTIO_DRIVER_FEATURE], eax 146 | 147 | ; 3.1.1 - Step 5 148 | mov al, VIRTIO_STATUS_ACKNOWLEDGE | VIRTIO_STATUS_DRIVER | VIRTIO_STATUS_FEATURES_OK 149 | mov [rsi+VIRTIO_DEVICE_STATUS], al 150 | 151 | ; 3.1.1 - Step 6 - Re-read device status to make sure FEATURES_OK is still set 152 | mov al, [rsi+VIRTIO_DEVICE_STATUS] 153 | bt ax, 3 ; VIRTIO_STATUS_FEATURES_OK 154 | jnc virtio_blk_init_error 155 | 156 | ; 3.1.1 - Step 7 157 | ; Set up the device and the queues 158 | ; discovery of virtqueues for the device 159 | ; optional per-bus setup 160 | ; reading and possibly writing the device’s virtio configuration space 161 | ; population of virtqueues 162 | 163 | ; Set up Queue 0 164 | xor eax, eax 165 | mov [rsi+VIRTIO_QUEUE_SELECT], ax 166 | mov ax, [rsi+VIRTIO_QUEUE_SIZE] ; Return the size of the queue 167 | mov ecx, eax ; Store queue size in ECX 168 | mov eax, os_nvs_mem 169 | mov [rsi+VIRTIO_QUEUE_DESC], eax 170 | rol rax, 32 171 | mov [rsi+VIRTIO_QUEUE_DESC+8], eax 172 | rol rax, 32 173 | add rax, 4096 174 | mov [rsi+VIRTIO_QUEUE_DRIVER], eax 175 | rol rax, 32 176 | mov [rsi+VIRTIO_QUEUE_DRIVER+8], eax 177 | rol rax, 32 178 | add rax, 4096 179 | mov [rsi+VIRTIO_QUEUE_DEVICE], eax 180 | rol rax, 32 181 | mov [rsi+VIRTIO_QUEUE_DEVICE+8], eax 182 | rol rax, 32 183 | mov ax, 1 184 | mov [rsi+VIRTIO_QUEUE_ENABLE], ax 185 | 186 | ; Populate the Next entries in the description ring 187 | ; FIXME - Don't expect exactly 256 entries 188 | mov eax, 1 189 | mov rdi, os_nvs_mem 190 | add rdi, 14 191 | virtio_blk_init_pop: 192 | mov [rdi], al 193 | add rdi, 16 194 | add al, 1 195 | cmp al, 0 196 | jne virtio_blk_init_pop 197 | 198 | ; 3.1.1 - Step 8 - At this point the device is “live” 199 | mov al, VIRTIO_STATUS_ACKNOWLEDGE | VIRTIO_STATUS_DRIVER | VIRTIO_STATUS_DRIVER_OK | VIRTIO_STATUS_FEATURES_OK 200 | mov [rsi+VIRTIO_DEVICE_STATUS], al 201 | 202 | virtio_blk_init_done: 203 | bts word [os_nvsVar], 3 ; Set the bit flag that Virtio Block has been initialized 204 | mov rdi, os_nvs_io ; Write over the storage function addresses 205 | mov rax, virtio_blk_io 206 | stosq 207 | mov rax, virtio_blk_id 208 | stosq 209 | pop rax 210 | pop rbx 211 | pop rdx 212 | pop rsi 213 | add rsi, 15 214 | mov byte [rsi], 1 ; Mark driver as installed in Bus Table 215 | sub rsi, 15 216 | ret 217 | 218 | virtio_blk_init_error: 219 | pop rax 220 | pop rbx 221 | pop rdx 222 | pop rsi 223 | ret 224 | ; ----------------------------------------------------------------------------- 225 | 226 | 227 | ; ----------------------------------------------------------------------------- 228 | ; virtio_blk_io -- Perform an I/O operation on a VIRTIO Block device 229 | ; IN: RAX = starting sector # 230 | ; RBX = I/O Opcode 231 | ; RCX = number of sectors 232 | ; RDX = drive # 233 | ; RDI = memory location used for reading/writing data from/to device 234 | ; OUT: Nothing 235 | ; All other registers preserved 236 | virtio_blk_io: 237 | push r9 238 | push rdi 239 | push rdx 240 | push rcx 241 | push rbx 242 | push rax 243 | 244 | push rax ; Save the starting sector 245 | mov r9, rdi ; Save the memory address 246 | 247 | mov rdi, os_nvs_mem ; This driver always starts at beginning of the Descriptor Table 248 | ; FIXME: Add desc_index offset 249 | 250 | ; Add header to Descriptor Entry 0 251 | mov rax, header ; Address of the header 252 | stosq ; 64-bit address 253 | mov eax, 16 254 | stosd ; 32-bit length 255 | mov ax, VIRTQ_DESC_F_NEXT 256 | stosw ; 16-bit Flags 257 | add rdi, 2 ; Skip Next as it is pre-populated 258 | 259 | ; Add data to Descriptor Entry 1 260 | mov rax, r9 ; Address to store the data 261 | stosq 262 | shl rcx, 12 ; Covert count to 4096B sectors 263 | mov eax, ecx ; Number of bytes 264 | stosd 265 | mov ax, VIRTQ_DESC_F_NEXT | VIRTQ_DESC_F_WRITE 266 | stosw ; 16-bit Flags 267 | add rdi, 2 ; Skip Next as it is pre-populated 268 | 269 | ; Add footer to Descriptor Entry 3 270 | mov rax, footer ; Address of the footer 271 | stosq ; 64-bit address 272 | mov eax, 1 273 | stosd ; 32-bit length 274 | mov eax, VIRTQ_DESC_F_WRITE 275 | stosw ; 16-bit Flags 276 | add rdi, 2 ; Skip Next as it is pre-populated 277 | 278 | ; Build the header 279 | mov rdi, header 280 | ; BareMetal I/O opcode for Read is 2, Write is 1 281 | ; Virtio-blk I/O opcode for Read is 0, Write is 1 282 | ; FIXME: Currently we just clear bit 1. 283 | btc bx, 1 284 | mov eax, ebx 285 | stosd ; type 286 | xor eax, eax 287 | stosd ; reserved 288 | pop rax ; Restore the starting sector 289 | shl rax, 3 ; Multiply by 8 as we use 4096-byte sectors internally 290 | stosq ; starting sector 291 | 292 | ; Build the footer 293 | mov rdi, footer 294 | xor eax, eax 295 | stosb 296 | 297 | ; Add entry to Avail 298 | mov rdi, os_nvs_mem+0x1000 ; Offset to start of Availability Ring 299 | mov ax, 1 ; 1 for no interrupts 300 | stosw ; 16-bit flags 301 | mov ax, [availindex] 302 | stosw ; 16-bit index 303 | mov ax, 0 304 | stosw ; 16-bit ring 305 | 306 | ; Notify the queue 307 | mov rdi, [os_virtioblk_base] 308 | add rdi, [notify_offset] ; This driver only uses Queue 0 so no multiplier needed 309 | xor eax, eax 310 | stosw 311 | 312 | ; Inspect the used ring 313 | mov rdi, os_nvs_mem+0x2002 ; Offset to start of Used Ring 314 | mov bx, [availindex] 315 | virtio_blk_io_wait: 316 | mov ax, [rdi] ; Load the index 317 | cmp ax, bx 318 | jne virtio_blk_io_wait 319 | 320 | add word [descindex], 3 ; 3 entries were required 321 | add word [availindex], 1 322 | 323 | pop rax 324 | pop rbx 325 | pop rcx 326 | pop rdx 327 | pop rdi 328 | pop r9 329 | ret 330 | ; ----------------------------------------------------------------------------- 331 | 332 | 333 | ; ----------------------------------------------------------------------------- 334 | ; virtio_blk_id -- 335 | ; IN: EAX = CDW0 336 | ; EBX = CDW1 337 | ; ECX = CDW10 338 | ; EDX = CDW11 339 | ; RDI = CDW6-7 340 | ; OUT: Nothing 341 | ; All other registers preserved 342 | virtio_blk_id: 343 | ret 344 | ; ----------------------------------------------------------------------------- 345 | 346 | ; Variables 347 | notify_offset: dq 0 348 | notify_offset_multiplier: dq 0 349 | descindex: dw 0 350 | availindex: dw 1 351 | 352 | ; Driver 353 | virtio_blk_driverid: 354 | dw 0x1AF4 ; Vendor ID 355 | dw 0x1001 ; Device ID - legacy 356 | dw 0x1042 ; Device ID - v1.0 357 | dw 0x0000 ; End of list 358 | 359 | align 16 360 | footer: 361 | db 0x00 362 | 363 | align 16 364 | header: 365 | dd 0x00 ; 32-bit type 366 | dd 0x00 ; 32-bit reserved 367 | dq 0 ; 64-bit sector 368 | 369 | ; VIRTIO BLK Registers 370 | VIRTIO_BLK_CAPACITY equ 0x14 ; 64-bit Capacity (in 512-byte sectors) 371 | VIRTIO_BLK_SIZE_MAX equ 0x1C ; 32-bit Maximum Segment Size 372 | VIRTIO_BLK_SEG_MAX equ 0x20 ; 32-bit Maximum Segment Count 373 | VIRTIO_BLK_CYLINDERS equ 0x24 ; 16-bit Cylinder Count 374 | VIRTIO_BLK_HEADS equ 0x26 ; 8-bit Head Count 375 | VIRTIO_BLK_SECTORS equ 0x27 ; 8-bit Sector Count 376 | VIRTIO_BLK_BLK_SIZE equ 0x28 ; 32-bit Block Length 377 | VIRTIO_BLK_PHYSICAL_BLOCK_EXP equ 0x2C ; 8-bit # OF LOGICAL BLOCKS PER PHYSICAL BLOCK (LOG2) 378 | VIRTIO_BLK_ALIGNMENT_OFFSET equ 0x2D ; 8-bit OFFSET OF FIRST ALIGNED LOGICAL BLOCK 379 | VIRTIO_BLK_MIN_IO_SIZE equ 0x2E ; 16-bit SUGGESTED MINIMUM I/O SIZE IN BLOCKS 380 | VIRTIO_BLK_OPT_IO_SIZE equ 0x30 ; 32-bit OPTIMAL (SUGGESTED MAXIMUM) I/O SIZE IN BLOCKS 381 | VIRTIO_BLK_WRITEBACK equ 0x34 ; 8-bit 382 | VIRTIO_BLK_NUM_QUEUES equ 0x36 ; 16-bit 383 | VIRTIO_BLK_MAX_DISCARD_SECTORS equ 0x38 ; 32-bit 384 | VIRTIO_BLK_MAX_DISCARD_SEG equ 0x3C ; 32-bit 385 | VIRTIO_BLK_DISCARD_SECTOR_ALIGNMENT equ 0x40 ; 32-bit 386 | VIRTIO_BLK_MAX_WRITE_ZEROES_SECTORS equ 0x44 ; 32-bit 387 | VIRTIO_BLK_MAX_WRITE_ZEROES_SEG equ 0x48 ; 32-bit 388 | VIRTIO_BLK_WRITE_ZEROES_MAY_UNMAP equ 0x4C ; 8-bit 389 | VIRTIO_BLK_MAX_SECURE_ERASE_SECTORS equ 0x50 ; 32-bit 390 | VIRTIO_BLK_MAX_SECURE_ERASE_SEG equ 0x54 ; 32-bit 391 | VIRTIO_BLK_SECURE_ERASE_SECTOR_ALIGNMENT equ 0x58 ; 32-bit 392 | 393 | ; VIRTIO_DEVICEFEATURES bits 394 | VIRTIO_BLK_F_BARRIER equ 0 ; Legacy - Device supports request barriers 395 | VIRTIO_BLK_F_SIZE_MAX equ 1 ; Maximum size of any single segment is in size_max 396 | VIRTIO_BLK_F_SEG_MAX equ 2 ; Maximum number of segments in a request is in seg_max 397 | VIRTIO_BLK_F_GEOMETRY equ 4 ; Disk-style geometry specified in geometry 398 | VIRTIO_BLK_F_RO equ 5 ; Device is read-only 399 | VIRTIO_BLK_F_BLK_SIZE equ 6 ; Block size of disk is in blk_size 400 | VIRTIO_BLK_F_SCSI equ 7 ; Legacy - Device supports scsi packet commands 401 | VIRTIO_BLK_F_FLUSH equ 9 ; Cache flush command support 402 | VIRTIO_BLK_F_TOPOLOGY equ 10 ; Device exports information on optimal I/O alignment 403 | VIRTIO_BLK_F_CONFIG_WCE equ 11 ; Device can toggle its cache between writeback and writethrough modes 404 | VIRTIO_BLK_F_MQ equ 12 ; Device supports multiqueue 405 | VIRTIO_BLK_F_DISCARD equ 13 ; Device can support discard command 406 | VIRTIO_BLK_F_WRITE_ZEROES equ 14 ; Device can support write zeroes command 407 | VIRTIO_BLK_F_LIFETIME equ 15 ; Device supports providing storage lifetime information 408 | VIRTIO_BLK_F_SECURE_ERASE equ 16 ; Device supports secure erase command 409 | 410 | ; VIRTIO Block Types 411 | VIRTIO_BLK_T_IN equ 0 ; Read from device 412 | VIRTIO_BLK_T_OUT equ 1 ; Write to device 413 | VIRTIO_BLK_T_FLUSH equ 4 ; Flush 414 | VIRTIO_BLK_T_GET_ID equ 8 ; Get device ID string 415 | VIRTIO_BLK_T_GET_LIFETIME equ 10 ; Get device lifetime 416 | VIRTIO_BLK_T_DISCARD equ 11 ; Discard 417 | VIRTIO_BLK_T_WRITE_ZEROES equ 13 ; Write zeros 418 | VIRTIO_BLK_T_SECURE_ERASE equ 14 ; Secure erase 419 | 420 | 421 | ; ============================================================================= 422 | ; EOF -------------------------------------------------------------------------------- /src/drivers/serial.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; Serial Functions 6 | ; ============================================================================= 7 | 8 | 9 | ; ----------------------------------------------------------------------------- 10 | serial_init: 11 | ; Pure64 has already initialized the serial port 12 | 13 | ; Check if PS/2 is present via ACPI IAPC_BOOT_ARCH 14 | mov ax, [os_boot_arch] 15 | bt ax, 0 ; LEGACY_DEVICES 16 | jnc serial_init_error 17 | 18 | ; TODO - Enable interrupts if needed 19 | 20 | ; Set flag that Serial was enabled 21 | or qword [os_SysConfEn], 1 << 2 22 | 23 | serial_init_error: 24 | ret 25 | ; ----------------------------------------------------------------------------- 26 | 27 | 28 | ; ----------------------------------------------------------------------------- 29 | ; serial_send -- Send a character via the configured serial port 30 | ; IN: AL = Character to send 31 | ; OUT: All registers preserved 32 | serial_send: 33 | push rdx 34 | push rax 35 | 36 | serial_send_wait: 37 | mov dx, COM_PORT_LINE_STATUS 38 | in al, dx 39 | and al, 0x20 ; Bit 5 40 | cmp al, 0 41 | je serial_send_wait 42 | 43 | ; Restore the byte and write to the serial port 44 | pop rax 45 | mov dx, COM_PORT_DATA 46 | out dx, al 47 | 48 | pop rdx 49 | ret 50 | ; ----------------------------------------------------------------------------- 51 | 52 | 53 | ; ----------------------------------------------------------------------------- 54 | ; serial_recv -- Receives a character via the configured serial port 55 | ; IN: Nothing 56 | ; OUT: AL = Character received, 0 if no character 57 | serial_recv: 58 | push rdx 59 | 60 | ; Check if serial port has pending data 61 | mov dx, COM_PORT_LINE_STATUS 62 | in al, dx 63 | and al, 0x01 ; Bit 0 64 | cmp al, 0 65 | je serial_recv_nochar 66 | 67 | ; Read from the serial port 68 | mov dx, COM_PORT_DATA 69 | in al, dx 70 | cmp al, 0x0D ; Enter via serial? 71 | je serial_recv_enter 72 | cmp al, 0x7F ; Backspace via serial? 73 | je serial_recv_backspace 74 | 75 | serial_recv_done: 76 | pop rdx 77 | ret 78 | 79 | serial_recv_nochar: 80 | xor al, al 81 | pop rdx 82 | ret 83 | 84 | serial_recv_enter: 85 | mov al, 0x1C ; Adjust it to the same value as a keyboard 86 | jmp serial_recv_done 87 | serial_recv_backspace: 88 | mov al, 0x0E ; Adjust it to the same value as a keyboard 89 | jmp serial_recv_done 90 | ; ----------------------------------------------------------------------------- 91 | 92 | 93 | ; Port Registers 94 | COM_BASE equ 0x3F8 95 | COM_PORT_DATA equ COM_BASE + 0 96 | COM_PORT_INTERRUPT_ENABLE equ COM_BASE + 1 97 | COM_PORT_FIFO_CONTROL equ COM_BASE + 2 98 | COM_PORT_LINE_CONTROL equ COM_BASE + 3 99 | COM_PORT_MODEM_CONTROL equ COM_BASE + 4 100 | COM_PORT_LINE_STATUS equ COM_BASE + 5 101 | COM_PORT_MODEM_STATUS equ COM_BASE + 6 102 | COM_PORT_SCRATCH_REGISTER equ COM_BASE + 7 103 | 104 | ; Baud Rates 105 | BAUD_115200 equ 1 106 | BAUD_57600 equ 2 107 | BAUD_9600 equ 12 108 | BAUD_300 equ 384 109 | 110 | 111 | ; ============================================================================= 112 | ; EOF -------------------------------------------------------------------------------- /src/drivers/virtio.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; Virtio Definitions and Virtqueue functions 6 | ; ============================================================================= 7 | 8 | 9 | ; ----------------------------------------------------------------------------- 10 | ; stub -- description 11 | ; IN: Nothing 12 | ; OUT: Nothing 13 | ; All other registers preserved 14 | ;stub: 15 | ; ret 16 | ; ----------------------------------------------------------------------------- 17 | 18 | 19 | ; VIRTIO PCI CAP 'cfg_type' Values 20 | VIRTIO_PCI_CAP_COMMON_CFG equ 1 ; Common configuration 21 | VIRTIO_PCI_CAP_NOTIFY_CFG equ 2 ; Notifications 22 | VIRTIO_PCI_CAP_ISR_CFG equ 3 ; ISR Status 23 | VIRTIO_PCI_CAP_DEVICE_CFG equ 4 ; Device specific configuration 24 | VIRTIO_PCI_CAP_PCI_CFG equ 5 ; PCI configuration access 25 | VIRTIO_PCI_CAP_SHARED_MEMORY_CFG equ 8 ; Shared memory region 26 | VIRTIO_PCI_CAP_VENDOR_CFG equ 9 ; Vendor-specific data 27 | 28 | ; VIRTIO MMIO Common Registers 29 | VIRTIO_DEVICE_FEATURE_SELECT equ 0x00 ; 32-bit 30 | VIRTIO_DEVICE_FEATURE equ 0x04 ; 32-bit Read-only 31 | VIRTIO_DRIVER_FEATURE_SELECT equ 0x08 ; 32-bit 32 | VIRTIO_DRIVER_FEATURE equ 0x0C ; 32-bit 33 | VIRTIO_CONFIG_MSIX_VECTOR equ 0x10 ; 16-bit 34 | VIRTIO_NUM_QUEUES equ 0x12 ; 16-bit Read-only 35 | VIRTIO_DEVICE_STATUS equ 0x14 ; 8-bit 36 | VIRTIO_CONFIG_GENERATION equ 0x15 ; 8-bit Read-only 37 | VIRTIO_QUEUE_SELECT equ 0x16 ; 16-bit 38 | VIRTIO_QUEUE_SIZE equ 0x18 ; 16-bit 39 | VIRTIO_QUEUE_MSIX_VECTOR equ 0x1A ; 16-bit 40 | VIRTIO_QUEUE_ENABLE equ 0x1C ; 16-bit 41 | VIRTIO_QUEUE_NOTIFY_OFF equ 0x1E ; 16-bit Read-only 42 | VIRTIO_QUEUE_DESC equ 0x20 ; 64-bit 43 | VIRTIO_QUEUE_DRIVER equ 0x28 ; 64-bit 44 | VIRTIO_QUEUE_DEVICE equ 0x30 ; 64-bit 45 | VIRTIO_QUEUE_NOTIFY_DATA equ 0x38 ; 16-bit Read-only 46 | VIRTIO_QUEUE_RESET equ 0x3A ; 16-bit 47 | 48 | ; VIRTIO CFG Types 49 | VIRTIO_PCI_CAP_COMMON_CFG equ 1 50 | VIRTIO_PCI_CAP_NOTIFY_CFG equ 2 51 | VIRTIO_PCI_CAP_ISR_CFG equ 3 52 | VIRTIO_PCI_CAP_DEVICE_CFG equ 4 53 | VIRTIO_PCI_CAP_PCI_CFG equ 5 54 | VIRTIO_PCI_CAP_SHARED_MEMORY_CFG equ 8 55 | VIRTIO_PCI_CAP_VENDOR_CFG equ 9 56 | 57 | ; VIRTIO_STATUS Values 58 | VIRTIO_STATUS_FAILED equ 0x80 ; Indicates that something went wrong in the guest, and it has given up on the device 59 | VIRTIO_STATUS_DEVICE_NEEDS_RESET equ 0x40 ; Indicates that the device has experienced an error from which it can’t recover 60 | VIRTIO_STATUS_FEATURES_OK equ 0x08 ; Indicates that the driver has acknowledged all the features it understands, and feature negotiation is complete 61 | VIRTIO_STATUS_DRIVER_OK equ 0x04 ; Indicates that the driver is set up and ready to drive the device 62 | VIRTIO_STATUS_DRIVER equ 0x02 ; Indicates that the guest OS knows how to drive the device 63 | VIRTIO_STATUS_ACKNOWLEDGE equ 0x01 ; Indicates that the guest OS has found the device and recognized it as a valid virtio device. 64 | 65 | ; VIRTQUEUE Flags 66 | VIRTQ_DESC_F_NEXT equ 1 67 | VIRTQ_DESC_F_WRITE equ 2 68 | VIRTQ_DESC_F_INDIRECT equ 4 69 | 70 | 71 | VIRTIO_F_INDIRECT_DESC equ 28 72 | VIRTIO_F_EVENT_IDX equ 29 73 | VIRTIO_F_VERSION_1 equ 32 74 | VIRTIO_F_ACCESS_PLATFORM equ 33 75 | VIRTIO_F_RING_PACKED equ 34 76 | VIRTIO_F_IN_ORDER equ 35 77 | VIRTIO_F_ORDER_PLATFORM equ 36 78 | VIRTIO_F_SR_IOV equ 37 79 | VIRTIO_F_NOTIFICATION_DATA equ 38 80 | VIRTIO_F_NOTIF_CONFIG_DATA equ 39 81 | VIRTIO_F_RING_RESET equ 40 82 | 83 | VIRTIO_MSI_NO_VECTOR equ 0xFFFF 84 | 85 | ; ============================================================================= 86 | ; EOF -------------------------------------------------------------------------------- /src/init.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; Initialization Includes 6 | ; ============================================================================= 7 | 8 | 9 | %include "init/64.asm" 10 | %include "init/bus.asm" 11 | %include "init/nvs.asm" 12 | %include "init/net.asm" 13 | %include "init/hid.asm" 14 | 15 | 16 | ; ============================================================================= 17 | ; EOF 18 | -------------------------------------------------------------------------------- /src/init/64.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; 64-bit initialization 6 | ; ============================================================================= 7 | 8 | 9 | ; ----------------------------------------------------------------------------- 10 | init_64: 11 | ; Clear all memory after the kernel up to 2MiB 12 | mov edi, os_SystemVariables 13 | mov ecx, 122880 ; Clear 960 KiB 14 | xor eax, eax 15 | rep stosq 16 | 17 | ; Gather data from Pure64's InfoMap 18 | mov esi, 0x00005060 ; LAPIC 19 | lodsq 20 | mov [os_LocalAPICAddress], rax 21 | mov esi, 0x00005010 ; CPUSPEED 22 | lodsw 23 | mov [os_CoreSpeed], ax 24 | mov esi, 0x00005012 ; CORES_ACTIVE 25 | lodsw 26 | mov [os_NumCores], ax 27 | mov esi, 0x00005020 ; RAMAMOUNT 28 | lodsd 29 | sub eax, 2 ; Save 2 MiB for the CPU stacks 30 | mov [os_MemAmount], eax ; In MiB's 31 | mov esi, 0x00005040 ; HPET 32 | lodsq 33 | mov [os_HPET_Address], rax 34 | lodsd 35 | mov [os_HPET_Frequency], eax 36 | lodsw 37 | mov [os_HPET_CounterMin], ax 38 | mov esi, 0x00005080 ; VIDEO_* 39 | xor eax, eax 40 | lodsq 41 | mov [os_screen_lfb], rax 42 | lodsw 43 | mov [os_screen_x], ax 44 | lodsw 45 | mov [os_screen_y], ax 46 | lodsw 47 | mov [os_screen_ppsl], ax 48 | lodsw 49 | mov [os_screen_bpp], ax 50 | mov esi, 0x00005090 ; PCIe bus count 51 | lodsw 52 | mov [os_pcie_count], ax 53 | lodsw 54 | mov [os_boot_arch], ax 55 | xor eax, eax 56 | mov esi, 0x00005604 ; IOAPIC 57 | lodsd 58 | mov [os_IOAPICAddress], rax 59 | 60 | ; Create exception gate stubs (Pure64 has already set the correct gate markers) 61 | xor edi, edi ; 64-bit IDT at linear address 0x0000000000000000 62 | mov ecx, 32 63 | mov rax, exception_gate ; A generic exception handler 64 | make_exception_gate_stubs: 65 | call create_gate 66 | inc edi 67 | dec ecx 68 | jnz make_exception_gate_stubs 69 | 70 | ; Set up the exception gates for all of the CPU exceptions 71 | xor edi, edi 72 | mov ecx, 21 73 | mov rax, exception_gate_00 74 | make_exception_gates: 75 | call create_gate 76 | inc edi 77 | add rax, 24 ; Each exception gate is 24 bytes 78 | dec rcx 79 | jnz make_exception_gates 80 | 81 | ; Create interrupt gate stubs (Pure64 has already set the correct gate markers) 82 | mov ecx, 256-32 83 | mov rax, interrupt_gate 84 | make_interrupt_gate_stubs: 85 | call create_gate 86 | inc edi 87 | dec ecx 88 | jnz make_interrupt_gate_stubs 89 | 90 | ; Set up IRQ handlers for CPUs 91 | mov edi, 0x80 92 | mov rax, ap_wakeup 93 | call create_gate 94 | mov edi, 0x81 95 | mov rax, ap_reset 96 | call create_gate 97 | 98 | ; Set device syscalls to stub 99 | mov rax, os_stub 100 | mov rdi, os_nvs_io 101 | stosq 102 | stosq 103 | mov rdi, os_net_transmit 104 | stosq 105 | stosq 106 | stosq 107 | 108 | ; Configure the Stack base 109 | mov rax, 0x200000 ; Stacks start at 2MiB 110 | mov [os_StackBase], rax 111 | 112 | ; Initialize the linear frame buffer output 113 | call lfb_init 114 | 115 | ; Initialize the APIC 116 | call os_apic_init 117 | 118 | ; Initialize the I/O APIC 119 | call os_ioapic_init 120 | 121 | ; Initialize the HPET 122 | call os_hpet_init 123 | 124 | ; Initialize all AP's to run our reset code. Skip the BSP 125 | call b_smp_get_id 126 | mov ebx, eax 127 | xor eax, eax 128 | mov cx, 255 129 | mov esi, 0x00005100 ; Location in memory of the Pure64 CPU data 130 | next_ap: 131 | test cx, cx 132 | jz no_more_aps 133 | lodsb ; Load the CPU APIC ID 134 | cmp al, bl 135 | je skip_ap 136 | call b_smp_reset ; Reset the CPU 137 | skip_ap: 138 | dec cx 139 | jmp next_ap 140 | no_more_aps: 141 | 142 | ; Configure the serial port 143 | call serial_init 144 | 145 | ; Output block to screen (1/4) 146 | mov ebx, 0 147 | call os_debug_block 148 | 149 | ret 150 | ; ----------------------------------------------------------------------------- 151 | 152 | 153 | ; ----------------------------------------------------------------------------- 154 | ; create_gate 155 | ; rax = address of handler 156 | ; rdi = gate # to configure 157 | create_gate: 158 | push rdi 159 | push rax 160 | 161 | shl rdi, 4 ; Quickly multiply rdi by 16 162 | stosw ; Store the low word (15..0) 163 | shr rax, 16 164 | add rdi, 4 ; Skip the gate marker (selector, ist, type) 165 | stosw ; Store the high word (31..16) 166 | shr rax, 16 167 | stosd ; Store the high dword (63..32) 168 | xor eax, eax 169 | stosd ; Reserved bits 170 | 171 | pop rax 172 | pop rdi 173 | ret 174 | ; ----------------------------------------------------------------------------- 175 | 176 | 177 | ; ============================================================================= 178 | ; EOF 179 | -------------------------------------------------------------------------------- /src/init/bus.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; Initialize Bus 6 | ; ============================================================================= 7 | 8 | 9 | ; Build a table of known devices on the system bus 10 | ; 11 | ; ┌───────────────────────────────────────────────────────────────────┐ 12 | ; │ Bus Table Format │ 13 | ; ├───┬───────────────────────────────┬───────────────┬───────────────┤ 14 | ; │0x0│ Base Value for bus_* │ Vendor ID │ Device ID │ 15 | ; ├───┼───────┬───────┬───────────────┴───────────────┴───────────────┤ 16 | ; │0x8│ Class │ SubCl │ Flags │ 17 | ; └───┴───────┴───────┴───────────────────────────────────────────────┘ 18 | ; 19 | ; Bytes 0-3 Base value used for os_bus_read/write (SG SG BS DF) 20 | ; Bytes 4-5 Vendor ID 21 | ; Bytes 6-7 Device ID 22 | ; Byte 8 Class code 23 | ; Byte 9 Subclass code 24 | ; Bytes 10-15 Flags 25 | ; Byte 14 is the bus type (1 for PCI, 2 for PCIe) 26 | ; Byte 15 will be set to 0x01 later if a driver enabled it 27 | 28 | 29 | ; ----------------------------------------------------------------------------- 30 | init_bus: 31 | mov rdi, bus_table ; Address of Bus Table in memory 32 | xor edx, edx ; Register 0 for Device ID/Vendor ID 33 | 34 | ; Check for PCIe first 35 | mov cx, [os_pcie_count] ; Check for PCIe 36 | cmp cx, 0 37 | jz init_bus_pci ; Fall back to PCI if no PCIe was detected 38 | mov byte [os_BusEnabled], 2 ; Bit 1 set for PCIe 39 | 40 | ; TODO 41 | ; Check which PCIe segments are valid and process only those 42 | ; For now we will only check against PCIe segment 0 43 | 44 | init_bus_pcie_probe: 45 | call os_pcie_read ; Read a Device ID/Vendor ID 46 | cmp eax, 0xFFFFFFFF ; 0xFFFFFFFF is returned for an non-existent device 47 | je init_bus_pcie_probe_next ; Skip to next device 48 | 49 | init_bus_pcie_probe_found: 50 | push rax ; Save the result 51 | mov rax, rdx ; Move the value used for os_pcie_read to RAX 52 | stosd ; Store it to the Bus Table 53 | pop rax ; Restore the Device ID/Vendor ID 54 | stosd ; Store it to the Bus Table 55 | add edx, 2 ; Register 2 for Class code/Subclass/Prog IF/Revision ID 56 | call os_pcie_read 57 | shr eax, 16 ; Move the Class/Subclass code to AX 58 | stosd ; Store it to the Bus Table 59 | sub edx, 2 60 | xor eax, eax ; Pad the Bus Table to 16 bytes 61 | stosw 62 | bts ax, 1 ; Set bit for PCIe 63 | stosw 64 | 65 | init_bus_pcie_probe_next: 66 | add rdx, 0x00010000 ; Skip to next PCIe device/function 67 | cmp edx, 0 ; Overflow EDX for a maximum of 65536 devices per segment 68 | je init_bus_end 69 | jmp init_bus_pcie_probe 70 | 71 | init_bus_pci: 72 | mov eax, 0x80000000 73 | mov dx, PCI_CONFIG_ADDRESS 74 | out dx, eax 75 | in eax, dx 76 | cmp eax, 0x80000000 77 | jne init_bus_pci_not_found ; Exit if PCI wasn't found 78 | mov byte [os_BusEnabled], 1 ; Bit 0 set for PCI 79 | xor edx, edx 80 | 81 | init_bus_pci_probe: 82 | call os_pci_read ; Read a Device ID/Vendor ID 83 | cmp eax, 0xFFFFFFFF ; 0xFFFFFFFF is returned for an non-existent device 84 | jne init_bus_pci_probe_found ; Found a device 85 | init_bus_pci_probe_next: 86 | add edx, 0x00010000 ; Skip to next PCI device 87 | cmp edx, 0 ; Overflow EDX for a maximum of 65536 devices 88 | je init_bus_end 89 | jmp init_bus_pci_probe 90 | 91 | init_bus_pci_probe_found: 92 | push rax ; Save the result 93 | mov rax, rdx ; Move the value used for os_pci_read to RAX 94 | stosd ; Store it to the Bus Table 95 | pop rax ; Restore the Device ID/Vendor ID 96 | stosd ; Store it to the Bus Table 97 | add edx, 2 ; Register 2 for Class code/Subclass/Prog IF/Revision ID 98 | call os_pci_read 99 | shr eax, 16 ; Move the Class/Subclass code to AX 100 | stosd ; Store it to the Bus Table 101 | sub edx, 2 102 | xor eax, eax ; Pad the Bus Table to 16 bytes 103 | stosw 104 | bts ax, 0 ; Set bit for PCI 105 | stosw 106 | jmp init_bus_pci_probe_next 107 | 108 | init_bus_pci_not_found: 109 | ret 110 | 111 | init_bus_end: 112 | mov eax, 0xFFFFFFFF 113 | mov ecx, 4 114 | rep stosd 115 | 116 | init_bus_usb_search: 117 | ; Check Bus Table for a USB Controller 118 | mov rsi, bus_table ; Load Bus Table address to RSI 119 | sub rsi, 16 120 | add rsi, 8 ; Add offset to Class Code 121 | init_bus_usb_check: 122 | add rsi, 16 ; Increment to next record in memory 123 | mov ax, [rsi] ; Load Class Code / Subclass Code 124 | cmp ax, 0xFFFF ; Check if at end of list 125 | je init_bus_usb_not_found 126 | cmp ax, 0x0C03 ; Serial Bus Controller (0C) / USB Controller (03) 127 | je init_bus_usb_find_driver 128 | jmp init_bus_usb_check ; Check Bus Table again 129 | 130 | ; Check the USB Controller to see if it is supported 131 | init_bus_usb_find_driver: 132 | sub rsi, 8 ; Move RSI back to start of Bus record 133 | mov edx, [rsi] ; Load value for os_bus_read/write 134 | mov dl, 0x02 135 | call os_bus_read 136 | shr eax, 8 ; Shift Program Interface to AL 137 | cmp al, 0x30 ; PI for XHCI 138 | je init_bus_usb_xhci_start 139 | add rsi, 8 140 | jmp init_bus_usb_check 141 | 142 | init_bus_usb_xhci_start: 143 | call xhci_init 144 | 145 | init_bus_usb_not_found: 146 | 147 | ; Output block to screen (2/4) 148 | mov ebx, 2 149 | call os_debug_block 150 | 151 | ret 152 | ; ----------------------------------------------------------------------------- 153 | 154 | 155 | ; ============================================================================= 156 | ; EOF 157 | -------------------------------------------------------------------------------- /src/init/hid.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; Initialize HID 6 | ; ============================================================================= 7 | 8 | 9 | ; ----------------------------------------------------------------------------- 10 | init_hid: 11 | ; Configure the PS/2 keyboard and mouse (if they exist) 12 | call ps2_init 13 | 14 | ; Enumerate USB devices 15 | bt qword [os_SysConfEn], 5 16 | jnc init_hid_done 17 | sti 18 | call xhci_enumerate_devices 19 | cli 20 | 21 | init_hid_done: 22 | ret 23 | ; ----------------------------------------------------------------------------- 24 | 25 | 26 | ; ============================================================================= 27 | ; EOF 28 | -------------------------------------------------------------------------------- /src/init/net.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; Initialize network 6 | ; ============================================================================= 7 | 8 | 9 | ; ----------------------------------------------------------------------------- 10 | ; init_net -- Configure the first network device it finds 11 | init_net: 12 | ; Check Bus Table for a Ethernet device 13 | mov rsi, bus_table ; Load Bus Table address to RSI 14 | sub rsi, 16 15 | add rsi, 8 ; Add offset to Class Code 16 | init_net_check_bus: 17 | add rsi, 16 ; Increment to next record in memory 18 | mov ax, [rsi] ; Load Class Code / Subclass Code 19 | cmp ax, 0xFFFF ; Check if at end of list 20 | je init_net_probe_not_found 21 | cmp ax, 0x0200 ; Network Controller (02) / Ethernet (00) 22 | je init_net_probe_find_driver 23 | jmp init_net_check_bus ; Check Bus Table again 24 | 25 | ; Check the Ethernet device to see if it has a driver 26 | init_net_probe_find_driver: 27 | sub rsi, 8 ; Move RSI back to start of Bus record 28 | mov r9, rsi ; Save start of Bus record 29 | mov edx, [rsi] ; Load value for os_bus_read/write 30 | mov r8d, [rsi+4] ; Save the Device ID / Vendor ID in R8D 31 | rol r8d, 16 ; Swap the Device ID / Vendor ID 32 | mov rsi, NIC_DeviceVendor_ID 33 | init_net_probe_find_next_driver: 34 | lodsw ; Load a driver ID 35 | mov bx, ax ; Save the driver ID 36 | lodsw ; Load the vendor ID 37 | cmp eax, 0 ; Check for a 0x0000 driver and vendor ID 38 | je init_net_probe_not_found 39 | rol eax, 16 ; Shift the vendor to the upper 16 bits 40 | init_net_probe_find_next_device: 41 | lodsw ; Load a device and vendor ID from our list of supported NICs 42 | cmp ax, 0x0000 ; Check for end of device list 43 | je init_net_probe_find_next_driver ; We found the next driver type 44 | cmp eax, r8d 45 | je init_net_probe_found ; If Carry is clear then we found a supported NIC 46 | jmp init_net_probe_find_next_device ; Check the next device 47 | 48 | init_net_probe_found: 49 | cmp bx, 0x1AF4 50 | je init_net_probe_found_virtio 51 | cmp bx, 0x8254 52 | je init_net_probe_found_i8254x 53 | cmp bx, 0x8257 54 | je init_net_probe_found_i8257x 55 | cmp bx, 0x8259 56 | je init_net_probe_found_i8259x 57 | cmp bx, 0x8169 58 | je init_net_probe_found_r8169 59 | jmp init_net_probe_not_found 60 | 61 | init_net_probe_found_virtio: 62 | call net_virtio_init 63 | mov rdi, os_net_transmit 64 | mov rax, net_virtio_transmit 65 | stosq 66 | mov rax, net_virtio_poll 67 | stosq 68 | jmp init_net_probe_found_finish 69 | 70 | init_net_probe_found_i8254x: 71 | call net_i8254x_init 72 | mov rdi, os_net_transmit 73 | mov rax, net_i8254x_transmit 74 | stosq 75 | mov rax, net_i8254x_poll 76 | stosq 77 | jmp init_net_probe_found_finish 78 | 79 | init_net_probe_found_i8257x: 80 | call net_i8257x_init 81 | mov rdi, os_net_transmit 82 | mov rax, net_i8257x_transmit 83 | stosq 84 | mov rax, net_i8257x_poll 85 | stosq 86 | jmp init_net_probe_found_finish 87 | 88 | init_net_probe_found_i8259x: 89 | call net_i8259x_init 90 | mov rdi, os_net_transmit 91 | mov rax, net_i8259x_transmit 92 | stosq 93 | mov rax, net_i8259x_poll 94 | stosq 95 | jmp init_net_probe_found_finish 96 | 97 | init_net_probe_found_r8169: 98 | call net_r8169_init 99 | mov rdi, os_net_transmit 100 | mov rax, net_r8169_transmit 101 | stosq 102 | mov rax, net_r8169_poll 103 | stosq 104 | jmp init_net_probe_found_finish 105 | 106 | init_net_probe_found_finish: 107 | add byte [os_net_icount], 1 108 | mov byte [os_NetEnabled], 1 ; A supported NIC was found. Signal to the OS that networking is enabled 109 | add r9, 15 ; Add offset to driver enabled byte 110 | mov byte [r9], 1 ; Mark device as having a driver 111 | 112 | init_net_probe_not_found: 113 | ; Output block to screen (4/4) 114 | mov ebx, 6 115 | call os_debug_block 116 | 117 | ret 118 | ; ----------------------------------------------------------------------------- 119 | 120 | 121 | ; ============================================================================= 122 | ; EOF 123 | -------------------------------------------------------------------------------- /src/init/nvs.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; Initialize non-volatile storage 6 | ; ============================================================================= 7 | 8 | 9 | ; ----------------------------------------------------------------------------- 10 | ; init_nvs -- Configure the first non-volatile storage device it finds 11 | init_nvs: 12 | ; Check Bus Table for NVMe 13 | mov rsi, bus_table ; Load Bus Table address to RSI 14 | sub rsi, 16 15 | add rsi, 8 ; Add offset to Class Code 16 | init_nvs_nvme_check: 17 | add rsi, 16 ; Increment to next record in memory 18 | mov ax, [rsi] ; Load Class Code / Subclass Code 19 | cmp ax, 0xFFFF ; Check if at end of list 20 | je init_nvs_nvme_skip ; If no NVMe the bail out 21 | cmp ax, 0x0108 ; Mass Storage Controller (01) / NVMe Controller (08) 22 | je init_nvs_nvme 23 | jmp init_nvs_nvme_check ; Check Bus Table again 24 | init_nvs_nvme_skip: 25 | 26 | ; Check Bus Table for any other supported controllers 27 | mov rsi, bus_table ; Load Bus Table address to RSI 28 | sub rsi, 16 29 | add rsi, 8 ; Add offset to Class Code 30 | init_nvs_check_bus: 31 | add rsi, 16 ; Increment to next record in memory 32 | mov ax, [rsi] ; Load Class Code / Subclass Code 33 | cmp ax, 0xFFFF ; Check if at end of list 34 | je init_nvs_done ; No storage controller found 35 | cmp ax, 0x0101 ; Mass Storage Controller (01) / ATA Controller (01) 36 | je init_nvs_ata 37 | cmp ax, 0x0106 ; Mass Storage Controller (01) / SATA Controller (06) 38 | je init_nvs_ahci 39 | cmp ax, 0x0100 ; Mass Storage Controller (01) / SCSI storage controller (00) 40 | je init_nvs_virtio_blk 41 | jmp init_nvs_check_bus ; Check Bus Table again 42 | 43 | init_nvs_nvme: 44 | sub rsi, 8 ; Move RSI back to start of Bus record 45 | mov edx, [rsi] ; Load value for os_bus_read/write 46 | call nvme_init 47 | jmp init_nvs_done 48 | 49 | init_nvs_ahci: 50 | sub rsi, 8 ; Move RSI back to start of Bus record 51 | mov edx, [rsi] ; Load value for os_bus_read/write 52 | call ahci_init 53 | jmp init_nvs_done 54 | 55 | init_nvs_virtio_blk: 56 | sub rsi, 8 ; Move RSI back to start of Bus record 57 | mov edx, [rsi] ; Load value for os_bus_read/write 58 | call virtio_blk_init 59 | jmp init_nvs_done 60 | 61 | init_nvs_ata: 62 | sub rsi, 8 ; Move RSI back to start of Bus record 63 | mov edx, [rsi] ; Load value for os_bus_read/write 64 | call ata_init 65 | jmp init_nvs_done 66 | 67 | init_nvs_done: 68 | ; Output block to screen (3/4) 69 | mov ebx, 4 70 | call os_debug_block 71 | 72 | ret 73 | ; ----------------------------------------------------------------------------- 74 | 75 | 76 | ; ============================================================================= 77 | ; EOF 78 | -------------------------------------------------------------------------------- /src/interrupt.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS 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 | align 8 12 | exception_gate: 13 | mov rsi, int_string00 14 | call b_output 15 | mov rsi, exc_string 16 | call b_output 17 | jmp $ ; Hang 18 | ; ----------------------------------------------------------------------------- 19 | 20 | 21 | ; ----------------------------------------------------------------------------- 22 | ; Default interrupt handler 23 | align 8 24 | interrupt_gate: ; handler for all other interrupts 25 | iretq ; It was an undefined interrupt so return to caller 26 | ; ----------------------------------------------------------------------------- 27 | 28 | 29 | ; ----------------------------------------------------------------------------- 30 | ; Keyboard interrupt. IRQ 0x01, INT 0x21 31 | ; This IRQ runs whenever there is input on the keyboard 32 | align 8 33 | int_keyboard: 34 | push rcx 35 | push rax 36 | 37 | call ps2_keyboard_interrupt ; Call keyboard interrupt code in PS/2 driver 38 | 39 | ; Acknowledge the IRQ 40 | mov ecx, APIC_EOI 41 | xor eax, eax 42 | call os_apic_write 43 | 44 | call b_smp_wakeup_all ; A terrible hack 45 | 46 | pop rax 47 | pop rcx 48 | iretq 49 | ; ----------------------------------------------------------------------------- 50 | 51 | 52 | ; ----------------------------------------------------------------------------- 53 | ; Mouse interrupt. IRQ 0x0C, INT 0x2C 54 | ; This IRQ runs whenever there is input on the mouse 55 | ; Note: A PS/2 mouse sends one byte per interrupt. So this is triggered 3 or 4 times per mouse action. 56 | align 8 57 | int_mouse: 58 | push rcx 59 | push rax 60 | 61 | call ps2_mouse_interrupt ; Call mouse interrupt code in PS/2 driver 62 | 63 | ; Check if the mouse driver has received a full packet 64 | cmp word [os_ps2_mouse_count], 0 65 | jne int_mouse_end ; Bail out if count isn't 0 66 | 67 | ; Check if the mouse interrupt has a callback to execute 68 | cmp qword [os_MouseCallback], 0 ; Is it valid? 69 | je int_mouse_end ; If not then bail out 70 | 71 | ; We could do a 'call [os_MouseCallback]' here but that would not be ideal. 72 | ; A defective callback would hang the system if it never returned back to the 73 | ; interrupt handler. Instead, we modify the stack so that the callback is 74 | ; executed after the interrupt handler has finished. Once the callback has 75 | ; finished, the execution flow will pick up back in the program. 76 | push rdi 77 | push rsi 78 | push rcx 79 | mov rcx, [os_MouseCallback] ; RCX stores the callback function address 80 | mov rsi, rsp ; Copy the current stack pointer to RSI 81 | sub rsp, 8 ; Subtract 8 since we add a 64-bit value to the stack 82 | mov rdi, rsp ; Copy the 'new' stack pointer to RDI 83 | movsq ; RCX 84 | movsq ; RSI 85 | movsq ; RDI 86 | movsq ; Flags 87 | movsq ; RAX 88 | lodsq ; RIP 89 | xchg rax, rcx 90 | stosq ; Callback address 91 | movsq ; CS 92 | movsq ; Flags 93 | lodsq ; RSP 94 | sub rax, 8 95 | stosq 96 | movsq ; SS 97 | mov [rax], rcx ; Original RIP 98 | pop rcx 99 | pop rsi 100 | pop rdi 101 | 102 | int_mouse_end: 103 | ; Acknowledge the interrupt 104 | mov ecx, APIC_EOI 105 | xor eax, eax 106 | call os_apic_write 107 | 108 | pop rax 109 | pop rcx 110 | iretq 111 | ; ----------------------------------------------------------------------------- 112 | 113 | 114 | ; ----------------------------------------------------------------------------- 115 | ; HPET Timer 0 interrupt 116 | ; This IRQ runs whenever HPET Timer 0 expires 117 | align 8 118 | hpet: 119 | push rcx 120 | push rax 121 | 122 | mov ecx, APIC_EOI 123 | xor eax, eax 124 | call os_apic_write 125 | 126 | pop rax 127 | pop rcx 128 | iretq 129 | ; ----------------------------------------------------------------------------- 130 | 131 | 132 | ; ----------------------------------------------------------------------------- 133 | ; A simple interrupt that just acknowledges an IPI. Useful for getting an AP past a 'hlt' in the code. 134 | align 8 135 | ap_wakeup: 136 | push rcx 137 | push rax 138 | 139 | ; Acknowledge the IPI 140 | mov ecx, APIC_EOI 141 | xor eax, eax 142 | call os_apic_write 143 | 144 | pop rax 145 | pop rcx 146 | iretq ; Return from the IPI 147 | ; ----------------------------------------------------------------------------- 148 | 149 | 150 | ; ----------------------------------------------------------------------------- 151 | ; Resets a CPU to execute ap_clear 152 | align 8 153 | ap_reset: 154 | ; Don't use 'os_apic_write' as we can't guarantee the state of the stack 155 | mov rax, ap_clear ; Set RAX to the address of ap_clear 156 | mov [rsp], rax ; Overwrite the return address on the CPU's stack 157 | mov rdi, [os_LocalAPICAddress] ; Acknowledge the IPI 158 | add rdi, 0xB0 159 | xor eax, eax 160 | stosd 161 | iretq ; Return from the IPI. CPU will execute code at ap_clear 162 | ; ----------------------------------------------------------------------------- 163 | 164 | 165 | ; ----------------------------------------------------------------------------- 166 | ; CPU Exception Gates 167 | align 8 168 | exception_gate_00: ; DE (Division Error) 169 | mov [rsp-16], rax 170 | xor eax, eax 171 | mov [rsp-8], rax 172 | sub rsp, 16 173 | mov al, 0x00 174 | jmp exception_gate_main 175 | 176 | align 8 177 | exception_gate_01: ; DB 178 | mov [rsp-16], rax 179 | xor eax, eax 180 | mov [rsp-8], rax 181 | sub rsp, 16 182 | mov al, 0x01 183 | jmp exception_gate_main 184 | 185 | align 8 186 | exception_gate_02: 187 | mov [rsp-16], rax 188 | xor eax, eax 189 | mov [rsp-8], rax 190 | sub rsp, 16 191 | mov al, 0x02 192 | jmp exception_gate_main 193 | 194 | align 8 195 | exception_gate_03: ; BP 196 | mov [rsp-16], rax 197 | xor eax, eax 198 | mov [rsp-8], rax 199 | sub rsp, 16 200 | mov al, 0x03 201 | jmp exception_gate_main 202 | 203 | align 8 204 | exception_gate_04: ; OF 205 | mov [rsp-16], rax 206 | xor eax, eax 207 | mov [rsp-8], rax 208 | sub rsp, 16 209 | mov al, 0x04 210 | jmp exception_gate_main 211 | 212 | align 8 213 | exception_gate_05: ; BR 214 | mov [rsp-16], rax 215 | xor eax, eax 216 | mov [rsp-8], rax 217 | sub rsp, 16 218 | mov al, 0x05 219 | jmp exception_gate_main 220 | 221 | align 8 222 | exception_gate_06: ; UD (Invalid Opcode) 223 | mov [rsp-16], rax 224 | xor eax, eax 225 | mov [rsp-8], rax 226 | sub rsp, 16 227 | mov al, 0x06 228 | jmp exception_gate_main 229 | 230 | align 8 231 | exception_gate_07: ; NM 232 | mov [rsp-16], rax 233 | xor eax, eax 234 | mov [rsp-8], rax 235 | sub rsp, 16 236 | mov al, 0x07 237 | jmp exception_gate_main 238 | 239 | align 8 240 | exception_gate_08: ; DF 241 | push rax 242 | mov al, 0x08 243 | jmp exception_gate_main 244 | times 16 db 0x90 245 | 246 | align 8 247 | exception_gate_09: 248 | mov [rsp-16], rax 249 | xor eax, eax 250 | mov [rsp-8], rax 251 | sub rsp, 16 252 | mov al, 0x09 253 | jmp exception_gate_main 254 | 255 | align 8 256 | exception_gate_10: ; TS 257 | push rax 258 | mov al, 0x0A 259 | jmp exception_gate_main 260 | times 16 db 0x90 261 | 262 | align 8 263 | exception_gate_11: ; NP 264 | push rax 265 | mov al, 0x0B 266 | jmp exception_gate_main 267 | times 16 db 0x90 268 | 269 | align 8 270 | exception_gate_12: ; SS 271 | push rax 272 | mov al, 0x0C 273 | jmp exception_gate_main 274 | times 16 db 0x90 275 | 276 | align 8 277 | exception_gate_13: ; GP 278 | push rax 279 | mov al, 0x0D 280 | jmp exception_gate_main 281 | times 16 db 0x90 282 | 283 | align 8 284 | exception_gate_14: ; PF (Page Fault) 285 | ; An error code is store in RAX (EAX padded) 286 | ; Register CR2 is set to the virtual address which caused the Page Fault 287 | push rax 288 | mov al, 0x0E 289 | jmp exception_gate_main 290 | times 16 db 0x90 291 | 292 | align 8 293 | exception_gate_15: 294 | mov [rsp-16], rax 295 | xor eax, eax 296 | mov [rsp-8], rax 297 | sub rsp, 16 298 | mov al, 0x0F 299 | jmp exception_gate_main 300 | 301 | align 8 302 | exception_gate_16: ; MF 303 | mov [rsp-16], rax 304 | xor eax, eax 305 | mov [rsp-8], rax 306 | sub rsp, 16 307 | mov al, 0x10 308 | jmp exception_gate_main 309 | 310 | align 8 311 | exception_gate_17: ; AC 312 | push rax 313 | mov al, 0x11 314 | jmp exception_gate_main 315 | times 16 db 0x90 316 | 317 | align 8 318 | exception_gate_18: ; MC 319 | mov [rsp-16], rax 320 | xor eax, eax 321 | mov [rsp-8], rax 322 | sub rsp, 16 323 | mov al, 0x12 324 | jmp exception_gate_main 325 | 326 | align 8 327 | exception_gate_19: ; XM 328 | mov [rsp-16], rax 329 | xor eax, eax 330 | mov [rsp-8], rax 331 | sub rsp, 16 332 | mov al, 0x13 333 | jmp exception_gate_main 334 | 335 | align 8 336 | exception_gate_20: ; VE 337 | mov [rsp-16], rax 338 | xor eax, eax 339 | mov [rsp-8], rax 340 | sub rsp, 16 341 | mov al, 0x14 342 | jmp exception_gate_main 343 | 344 | ; ----------------------------------------------------------------------------- 345 | ; Main exception handler 346 | align 8 347 | exception_gate_main: 348 | mov qword [os_NetworkCallback], 0 ; Reset the network callback 349 | mov qword [os_ClockCallback], 0 ; Reset the clock callback 350 | 351 | ; Display exception message, APIC ID, and exception type 352 | push rbx 353 | push rdi 354 | push rsi 355 | push rcx ; Char counter for b_output 356 | push rax ; Save RAX since b_smp_get_id clobbers it 357 | call os_debug_newline 358 | mov rsi, int_string00 359 | mov rcx, 6 360 | call b_output 361 | call b_smp_get_id ; Get the local CPU ID and print it 362 | call os_debug_dump_ax 363 | mov rsi, int_string01 364 | mov rcx, 15 365 | call b_output 366 | mov rsi, exc_string00 367 | pop rax 368 | and rax, 0x00000000000000FF ; Clear out everything in RAX except for AL 369 | push rax 370 | mov bl, 6 ; Length of each message 371 | mul bl ; AX = AL x BL 372 | add rsi, rax ; Use the value in RAX as an offset to get to the right message 373 | pop rax 374 | mov bl, 0x0F 375 | mov rcx, 6 376 | call b_output 377 | pop rcx 378 | pop rsi 379 | pop rdi 380 | pop rbx 381 | pop rax 382 | 383 | ; Dump all registers 384 | push r15 385 | push r14 386 | push r13 387 | push r12 388 | push r11 389 | push r10 390 | push r9 391 | push r8 392 | push rsp 393 | push rbp 394 | push rdi 395 | push rsi 396 | push rdx 397 | push rcx 398 | push rbx 399 | push rax 400 | mov rsi, reg_string00 ; Load address of first register string 401 | mov ecx, 4 ; Number of characters per reg_string 402 | mov edx, 16 ; Counter of registers to left to output 403 | xor ebx, ebx ; Counter of registers output per line 404 | call os_debug_newline 405 | exception_gate_main_nextreg: 406 | call b_output 407 | add rsi, 4 408 | pop rax 409 | call os_debug_dump_rax 410 | add ebx, 1 411 | cmp ebx, 4 ; Number of registers to output per line 412 | jne exception_gate_main_nextreg_space 413 | call os_debug_newline 414 | xor ebx, ebx 415 | jmp exception_gate_main_nextreg_continue 416 | exception_gate_main_nextreg_space: 417 | call os_debug_space 418 | exception_gate_main_nextreg_continue: 419 | dec edx 420 | jnz exception_gate_main_nextreg 421 | call b_output 422 | mov rax, [rsp+8] ; RIP of caller 423 | call os_debug_dump_rax 424 | call os_debug_space 425 | add rsi, 4 426 | call b_output 427 | mov rax, cr2 428 | call os_debug_dump_rax 429 | 430 | ; Check if the exception was on the BSP. Rerun the payload if so 431 | call b_smp_get_id ; Get the local CPU ID 432 | cmp [os_BSP], al 433 | je bsp_run_payload 434 | jmp ap_clear ; jump to AP clear code 435 | ; ----------------------------------------------------------------------------- 436 | 437 | 438 | int_string00 db 'CPU 0x' 439 | int_string01 db ' - Exception 0x' 440 | ; Strings for the error messages 441 | exc_string db 'Unknown Fatal Exception!' 442 | exc_string00 db '00(DE)' 443 | exc_string01 db '01(DB)' 444 | exc_string02 db '02 ' 445 | exc_string03 db '03(BP)' 446 | exc_string04 db '04(OF)' 447 | exc_string05 db '05(BR)' 448 | exc_string06 db '06(UD)' 449 | exc_string07 db '07(NM)' 450 | exc_string08 db '08(DF)' 451 | exc_string09 db '09 ' ; No longer generated on new CPU's 452 | exc_string10 db '10(TS)' 453 | exc_string11 db '11(NP)' 454 | exc_string12 db '12(SS)' 455 | exc_string13 db '13(GP)' 456 | exc_string14 db '14(PF)' 457 | exc_string15 db '15 ' 458 | exc_string16 db '16(MF)' 459 | exc_string17 db '17(AC)' 460 | exc_string18 db '18(MC)' 461 | exc_string19 db '19(XM)' 462 | exc_string20 db '20(VE)' 463 | 464 | ; Strings for registers 465 | reg_string00 db 'RAX=' 466 | reg_string01 db 'RBX=' 467 | reg_string02 db 'RCX=' 468 | reg_string03 db 'RDX=' 469 | reg_string04 db 'RSI=' 470 | reg_string05 db 'RDI=' 471 | reg_string06 db 'RBP=' 472 | reg_string07 db 'RSP=' 473 | reg_string08 db 'R8 =' 474 | reg_string09 db 'R9 =' 475 | reg_string10 db 'R10=' 476 | reg_string11 db 'R11=' 477 | reg_string12 db 'R12=' 478 | reg_string13 db 'R13=' 479 | reg_string14 db 'R14=' 480 | reg_string15 db 'R15=' 481 | reg_string16 db 'RIP=' 482 | reg_string17 db 'CR2=' 483 | 484 | 485 | ; ============================================================================= 486 | ; EOF 487 | -------------------------------------------------------------------------------- /src/kernel.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; The BareMetal exokernel 6 | ; ============================================================================= 7 | 8 | 9 | BITS 64 ; Specify 64-bit 10 | ORG 0x0000000000100000 ; The kernel needs to be loaded at this address 11 | 12 | %DEFINE BAREMETAL_VER 'v1.0.0 (January 21, 2020)', 13, 'Copyright (C) 2008-2025 Return Infinity', 13, 0 13 | %DEFINE BAREMETAL_API_VER 1 14 | KERNELSIZE equ 20 * 1024 ; Pad the kernel to this length 15 | 16 | 17 | kernel_start: 18 | jmp start ; Skip over the function call index 19 | nop 20 | db 'BAREMETAL' ; Kernel signature 21 | 22 | align 16 23 | dq b_input ; 0x0010 24 | dq b_output ; 0x0018 25 | dq b_net_tx ; 0x0020 26 | dq b_net_rx ; 0x0028 27 | dq b_nvs_read ; 0x0030 28 | dq b_nvs_write ; 0x0038 29 | dq b_system ; 0x0040 30 | dq b_user ; 0x0048 31 | 32 | align 16 33 | start: 34 | mov rsp, 0x10000 ; Set the temporary stack 35 | 36 | ; System and driver initialization 37 | call init_64 ; After this point we are in a working 64-bit environment 38 | call init_bus ; Initialize system busses 39 | call init_nvs ; Initialize non-volatile storage 40 | call init_net ; Initialize network 41 | call init_hid ; Initialize human interface devices 42 | 43 | ; Copy the payload after the kernel to the proper address 44 | mov rsi, 0x100000 + KERNELSIZE ; Payload starts right after the kernel 45 | cmp qword [rsi], 0 ; Is there a payload after the kernel? 46 | je ap_clear ; If not, skip to ap_clear 47 | mov rdi, 0x1E0000 48 | mov rcx, 2048 49 | rep movsq ; Copy 16384 bytes 50 | 51 | ; Set the payload to run 52 | bsp_run_payload: 53 | mov rsi, [os_LocalAPICAddress] ; We can't use b_smp_get_id as no configured stack yet 54 | xor eax, eax ; Clear Task Priority (bits 7:4) and Task Priority Sub-Class (bits 3:0) 55 | mov dword [rsi+0x80], eax ; APIC Task Priority Register (TPR) 56 | mov eax, dword [rsi+0x20] ; APIC ID in upper 8 bits 57 | shr eax, 24 ; Shift to the right and AL now holds the CPU's APIC ID 58 | mov [os_BSP], al ; Keep a record of the BSP APIC ID 59 | mov ebx, eax ; Save the APIC ID 60 | mov rdi, os_SMP ; Clear the entry in the work table 61 | shl rax, 3 ; Quick multiply by 8 to get to proper record 62 | add rdi, rax 63 | xor eax, eax 64 | or al, 1 ; Set bit 0 for "present" 65 | stosq ; Clear the code address 66 | mov rcx, rbx ; Copy the APIC ID for b_smp_set 67 | mov rax, 0x1E0000 ; Payload was copied here 68 | call b_smp_set 69 | jmp bsp ; Skip past some of the ap_clear code we have already executed 70 | 71 | ; Fall through to ap_clear as align fills the space with No-Ops 72 | ; At this point the BSP is just like one of the AP's 73 | 74 | align 16 75 | ap_clear: ; All cores start here on first start-up and after an exception 76 | cli ; Disable interrupts on this core 77 | 78 | ; Get local ID of the core 79 | mov rsi, [os_LocalAPICAddress] ; We can't use b_smp_get_id as no configured stack yet 80 | xor eax, eax ; Clear Task Priority (bits 7:4) and Task Priority Sub-Class (bits 3:0) 81 | mov dword [rsi+0x80], eax ; APIC Task Priority Register (TPR) 82 | mov eax, dword [rsi+0x20] ; APIC ID in upper 8 bits 83 | shr eax, 24 ; Shift to the right and AL now holds the CPU's APIC ID 84 | mov ebx, eax ; Save the APIC ID 85 | 86 | ; Clear the entry in the work table 87 | mov rdi, os_SMP 88 | shl rax, 3 ; Quick multiply by 8 to get to proper record 89 | add rdi, rax 90 | xor eax, eax 91 | or al, 1 ; Set bit 0 for "present" 92 | stosq ; Clear the code address 93 | 94 | bsp: 95 | ; Set up the stack 96 | mov eax, ebx ; Restore the APIC ID 97 | shl rax, 16 ; Shift left 16 bits for an 64 KiB stack 98 | add rax, [os_StackBase] ; The stack decrements when you "push", start at 64 KiB in 99 | add rax, 65536 ; 64 KiB Stack 100 | mov rsp, rax 101 | 102 | ; Clear registers. Gives us a clean slate to work with 103 | xor eax, eax ; aka r0 104 | xor ecx, ecx ; aka r1 105 | xor edx, edx ; aka r2 106 | xor ebx, ebx ; aka r3 107 | xor ebp, ebp ; aka r5, We skip RSP (aka r4) as it was previously set 108 | xor esi, esi ; aka r6 109 | xor edi, edi ; aka r7 110 | xor r8, r8 111 | xor r9, r9 112 | xor r10, r10 113 | xor r11, r11 114 | xor r12, r12 115 | xor r13, r13 116 | xor r14, r14 117 | xor r15, r15 118 | sti ; Enable interrupts on this core 119 | 120 | ap_check: 121 | call b_smp_get ; Check for an assigned workload 122 | and al, 0xF0 ; Clear the flags 123 | cmp rax, 0 ; If 0 then there is nothing to do 124 | jne ap_process 125 | 126 | ap_halt: ; Halt until a wakeup call is received 127 | hlt 128 | jmp ap_check ; Core will jump to ap_check when it wakes up 129 | 130 | ap_process: 131 | mov rcx, 1 ; Set the active flag 132 | call b_smp_setflag 133 | xor ecx, ecx 134 | call rax ; Run the code 135 | jmp ap_clear ; Reset the stack, clear the registers, and wait for something else to work on 136 | 137 | ; Includes 138 | %include "init.asm" 139 | %include "syscalls.asm" 140 | %include "drivers.asm" 141 | %include "interrupt.asm" 142 | %include "sysvar.asm" ; Include this last to keep the read/write variables away from the code 143 | 144 | EOF: 145 | db 0xDE, 0xAD, 0xC0, 0xDE 146 | 147 | times KERNELSIZE-($-$$) db 0x90 ; Set the compiled kernel binary to at least this size in bytes 148 | 149 | 150 | ; ============================================================================= 151 | ; EOF 152 | -------------------------------------------------------------------------------- /src/syscalls.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; System Call Section -- Accessible to user programs 6 | ; ============================================================================= 7 | 8 | 9 | %include "syscalls/bus.asm" 10 | %include "syscalls/debug.asm" 11 | %include "syscalls/nvs.asm" 12 | %include "syscalls/io.asm" 13 | %include "syscalls/net.asm" 14 | %include "syscalls/smp.asm" 15 | %include "syscalls/system.asm" 16 | 17 | 18 | ; ============================================================================= 19 | ; EOF 20 | -------------------------------------------------------------------------------- /src/syscalls/bus.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; Bus Functions 6 | ; ============================================================================= 7 | 8 | 9 | ; ┌───────────────────────────────────────────────────────────────┐ 10 | ; │ RDX Format │ 11 | ; ├───────────────────────────────────────────────────────────────┤ 12 | ; │63 48 47 32 31 24 23 16 15 0│ 13 | ; ├───────────────┬───────────────┬───────┬───────┬───────────────┤ 14 | ; │ │ PCIe Segment │ Bus │Device │ Register │ 15 | ; └───────────────┴───────────────┴───────┴───────┴───────────────┘ 16 | ; 17 | ; PCIe Segment Group, 16 bits (Ignored for PCI) 18 | ; Bus, 8 bits 19 | ; Device/Function, 8 bits (5 bit device, 3 bit function) 20 | ; Register, 16 bits (Uses 10 bits for PCIe and 6 bits for PCI) 21 | 22 | 23 | ; ----------------------------------------------------------------------------- 24 | ; os_bus_read -- Read from a register on a bus device 25 | ; IN: RDX = Register to read from 26 | ; OUT: EAX = Register value that was read 27 | ; All other registers preserved 28 | os_bus_read: 29 | cmp byte [os_BusEnabled], 2 ; Check if PCIe was enabled 30 | jne os_bus_read_pci ; If not, fall back to PCI 31 | os_bus_read_pcie: 32 | call os_pcie_read 33 | ret 34 | os_bus_read_pci: 35 | call os_pci_read 36 | ret 37 | ; ----------------------------------------------------------------------------- 38 | 39 | 40 | ; ----------------------------------------------------------------------------- 41 | ; os_bus_write -- Write to a register on a bus device 42 | ; IN: RDX = Register to write to 43 | ; EAX = Register value to be written 44 | ; OUT: Nothing, all registers preserved 45 | os_bus_write: 46 | cmp byte [os_BusEnabled], 2 ; Check if PCIe was enabled 47 | jne os_bus_write_pci ; If not, fall back to PCI 48 | os_bus_write_pcie: 49 | call os_pcie_write 50 | ret 51 | os_bus_write_pci: 52 | call os_pci_write 53 | ret 54 | ; ----------------------------------------------------------------------------- 55 | 56 | 57 | ; ----------------------------------------------------------------------------- 58 | ; os_bus_read_bar -- Read a BAR (Base Address Register) 59 | ; IN: RDX = Device to read from (low 8 bits is ignored and set by value in AL) 60 | ; AL = BAR # 61 | ; OUT: RAX = BAR I/O Address 62 | ; RCX = BAR Length in bytes 63 | os_bus_read_bar: 64 | push rdx 65 | push rbx 66 | 67 | ; Disable Memory and I/O Space 68 | push rax 69 | mov dl, 0x01 ; Read Status/Command 70 | call os_bus_read 71 | btc eax, 1 ; Disable Memory Space 72 | btc eax, 0 ; Disable I/O Space 73 | call os_bus_write ; Write updated Status/Command 74 | pop rax 75 | 76 | ; Read the lower 32-bit BAR and BAR Length 77 | add al, 0x04 ; BARs start at register 4 78 | mov dl, al 79 | xor eax, eax 80 | xor ebx, ebx 81 | call os_bus_read 82 | xchg eax, ebx ; Exchange the result to EBX (low 32 bits of base) 83 | not eax ; Flip all bits in EAX (which is currently 0 after XCHG with EBX) 84 | call os_bus_write ; Write 0xFFFFFFFF to the BAR 85 | call os_bus_read ; Read the low 32 bits of the length 86 | push rax 87 | mov eax, ebx ; Copy original BAR value to EAX 88 | call os_bus_write ; Write the original value back 89 | pop rax 90 | not eax 91 | mov ecx, eax 92 | bt ebx, 0 ; Bit 0 will be 0 if it is an MMIO space 93 | jc os_bus_read_bar_io 94 | bt ebx, 2 ; Bit 2 will be 1 if it is a 64-bit MMIO space 95 | jnc os_bus_read_bar_32bit 96 | 97 | ; Read the upper 32-bit BAR and BAR Length 98 | push r8 99 | inc dl ; Read next register for next BAR (Upper 32-bits of BAR) 100 | call os_bus_read 101 | push rax 102 | shl rax, 32 ; Shift the bits to the upper 32 103 | and ebx, 0xFFFFFFF0 ; Clear the low four bits 104 | add rax, rbx ; Add the upper 32 and lower 32 together 105 | mov r8, rax 106 | xor eax, eax 107 | not eax ; Flip all bits in EAX (which is currently 0 after XCHG with EBX) 108 | call os_bus_write ; Write 0xFFFFFFFF to the BAR 109 | call os_bus_read ; Read the low 32 bits of the length 110 | not eax 111 | shl rax, 32 ; Shift the bits to the upper 32 112 | add rcx, rax ; Add the upper 32 and lower 32 together 113 | inc rcx 114 | pop rax 115 | call os_bus_write ; Write the original value back 116 | mov rax, r8 117 | pop r8 118 | 119 | pop rbx 120 | pop rdx 121 | ret 122 | 123 | os_bus_read_bar_32bit: 124 | and ebx, 0xFFFFFFF0 ; Clear the low four bits 125 | mov rax, rbx ; Add the upper 32 and lower 32 together 126 | inc rcx 127 | 128 | pop rbx 129 | pop rdx 130 | ret 131 | 132 | os_bus_read_bar_io: 133 | and ebx, 0xFFFFFFFC ; Clear the low two bits 134 | mov rax, rbx ; Store Base IO address in RAX 135 | 136 | pop rbx 137 | pop rdx 138 | ret 139 | ; ----------------------------------------------------------------------------- 140 | 141 | 142 | ; ============================================================================= 143 | ; EOF 144 | -------------------------------------------------------------------------------- /src/syscalls/debug.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; Debug Functions 6 | ; ============================================================================= 7 | 8 | 9 | ; ----------------------------------------------------------------------------- 10 | ; os_debug_dump_(rax|eax|ax|al) -- Dump content of RAX, EAX, AX, or AL 11 | ; IN: RAX = content to dump 12 | ; OUT: Nothing, all registers preserved 13 | os_debug_dump_rax: 14 | rol rax, 8 15 | call os_debug_dump_al 16 | rol rax, 8 17 | call os_debug_dump_al 18 | rol rax, 8 19 | call os_debug_dump_al 20 | rol rax, 8 21 | call os_debug_dump_al 22 | rol rax, 32 23 | os_debug_dump_eax: ; RAX is used here instead of EAX to preserve the upper 32-bits 24 | rol rax, 40 25 | call os_debug_dump_al 26 | rol rax, 8 27 | call os_debug_dump_al 28 | rol rax, 16 29 | os_debug_dump_ax: 30 | rol ax, 8 31 | call os_debug_dump_al 32 | rol ax, 8 33 | os_debug_dump_al: 34 | push rax 35 | push ax ; Save AX for the low nibble 36 | shr al, 4 ; Shift the high 4 bits into the low 4, high bits cleared 37 | or al, '0' ; Add "0" 38 | cmp al, '9'+1 ; Digit? 39 | jb os_debug_dump_al_h ; Yes, store it 40 | add al, 7 ; Add offset for character "A" 41 | os_debug_dump_al_h: 42 | mov [tchar+0], al ; Store first character 43 | pop ax ; Restore AX 44 | and al, 0x0F ; Keep only the low 4 bits 45 | or al, '0' ; Add "0" 46 | cmp al, '9'+1 ; Digit? 47 | jb os_debug_dump_al_l ; Yes, store it 48 | add al, 7 ; Add offset for character "A" 49 | os_debug_dump_al_l: 50 | mov [tchar+1], al ; Store second character 51 | pop rax 52 | push rsi 53 | push rcx 54 | mov rsi, tchar 55 | mov rcx, 2 56 | call b_output 57 | pop rcx 58 | pop rsi 59 | ret 60 | ; ----------------------------------------------------------------------------- 61 | 62 | 63 | ; ----------------------------------------------------------------------------- 64 | ; os_debug_dump_mem -- Dump content of memory in hex format 65 | ; IN: RSI = starting address of memory to dump 66 | ; RCX = number of bytes 67 | ; OUT: Nothing, all registers preserved 68 | os_debug_dump_mem: 69 | push rsi 70 | push rcx ; Counter 71 | push rdx ; Total number of bytes to display 72 | push rax 73 | 74 | test rcx, rcx ; Bail out if no bytes were requested 75 | jz os_debug_dump_mem_done 76 | 77 | push rsi ; Output '0x' 78 | push rcx 79 | mov rsi, os_debug_dump_mem_chars 80 | mov rcx, 2 81 | call b_output 82 | pop rcx 83 | pop rsi 84 | 85 | mov rax, rsi ; Output the memory address 86 | call os_debug_dump_rax 87 | call os_debug_newline 88 | 89 | nextline: 90 | mov dx, 0 91 | nextchar: 92 | cmp rcx, 0 93 | je os_debug_dump_mem_done_newline 94 | push rsi ; Output ' ' 95 | push rcx 96 | mov rsi, os_debug_dump_mem_chars+3 97 | mov rcx, 1 98 | call b_output 99 | pop rcx 100 | pop rsi 101 | lodsb 102 | call os_debug_dump_al 103 | dec rcx 104 | inc rdx 105 | cmp dx, 16 ; End of line yet? 106 | jne nextchar 107 | call os_debug_newline 108 | cmp rcx, 0 109 | je os_debug_dump_mem_done 110 | jmp nextline 111 | 112 | os_debug_dump_mem_done_newline: 113 | call os_debug_newline 114 | 115 | os_debug_dump_mem_done: 116 | pop rax 117 | pop rcx 118 | pop rdx 119 | pop rsi 120 | ret 121 | 122 | os_debug_dump_mem_chars: db '0x: ' 123 | ; ----------------------------------------------------------------------------- 124 | 125 | 126 | ; ----------------------------------------------------------------------------- 127 | ; os_debug_newline -- Output a newline 128 | ; IN: Nothing 129 | ; OUT: Nothing, all registers preserved 130 | os_debug_newline: 131 | push rsi 132 | push rcx 133 | mov rsi, newline 134 | mov rcx, 2 135 | call b_output 136 | pop rcx 137 | pop rsi 138 | ret 139 | ; ----------------------------------------------------------------------------- 140 | 141 | 142 | ; ----------------------------------------------------------------------------- 143 | ; os_debug_space -- Output a space 144 | ; IN: Nothing 145 | ; OUT: Nothing, all registers preserved 146 | os_debug_space: 147 | push rsi 148 | push rcx 149 | mov rsi, space 150 | mov rcx, 1 151 | call b_output 152 | pop rcx 153 | pop rsi 154 | ret 155 | ; ----------------------------------------------------------------------------- 156 | 157 | 158 | ; ----------------------------------------------------------------------------- 159 | ; os_debug_block - Create a block (8x8 pixels) of colour on the screen 160 | ; IN: EBX = Index # 161 | os_debug_block: 162 | push rax 163 | push rbx 164 | push rcx 165 | push rdx 166 | push rdi 167 | 168 | ; Calculate parameters 169 | push rbx 170 | push rax 171 | xor edx, edx 172 | xor eax, eax 173 | xor ebx, ebx 174 | mov ax, [0x00005F00 + 0x12] ; Screen Y 175 | add ax, 16 ; Lower row 176 | shr ax, 1 ; Quick divide by 2 177 | mov bx, [0x00005F00 + 0x10] ; Screen X 178 | shl ebx, 2 ; Quick multiply by 4 179 | mul ebx ; Multiply EDX:EAX by EBX 180 | mov rdi, [0x00005F00] ; Frame buffer base 181 | add rdi, rax ; Offset is ((screeny - 8) / 2 + screenx * 4) 182 | pop rax 183 | pop rbx 184 | xor edx, edx 185 | mov dx, [0x00005F00 + 0x14] ; PixelsPerScanLine 186 | shl edx, 2 ; Quick multiply by 4 for line offset 187 | xor ecx, ecx 188 | mov cx, [0x00005F00 + 0x10] ; Screen X 189 | shr cx, 4 ; CX = total amount of 8-pixel wide blocks 190 | sub cx, 4 191 | add ebx, ecx 192 | shl ebx, 5 ; Quick multiply by 32 (8 pixels by 4 bytes each) 193 | add rdi, rbx 194 | 195 | ; Draw the 8x8 pixel block 196 | mov ebx, 8 ; 8 pixels tall 197 | mov eax, 0x00F7CA54 ; Return Infinity Yellow/Orange 198 | os_debug_block_nextline: 199 | mov ecx, 8 ; 8 pixels wide 200 | rep stosd 201 | add rdi, rdx ; Add line offset 202 | sub rdi, 8*4 203 | dec ebx 204 | jnz os_debug_block_nextline 205 | 206 | pop rdi 207 | pop rdx 208 | pop rcx 209 | pop rbx 210 | pop rax 211 | ret 212 | ; ----------------------------------------------------------------------------- 213 | 214 | 215 | ; ============================================================================= 216 | ; EOF 217 | -------------------------------------------------------------------------------- /src/syscalls/io.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; Input/Output Functions 6 | ; ============================================================================= 7 | 8 | 9 | ; ----------------------------------------------------------------------------- 10 | ; b_input -- Scans keyboard for input 11 | ; IN: Nothing 12 | ; OUT: AL = 0 if no key pressed, otherwise ASCII code, other regs preserved 13 | ; All other registers preserved 14 | b_input: 15 | mov al, [key] 16 | test al, al 17 | jz b_input_no_key 18 | mov byte [key], 0x00 ; clear the variable as the keystroke is in AL now 19 | ret 20 | 21 | b_input_no_key: 22 | bt qword [os_SysConfEn], 1 << 2 23 | jnc b_input_no_serial 24 | call serial_recv ; Try from the serial port 25 | b_input_no_serial: 26 | ret 27 | ; ----------------------------------------------------------------------------- 28 | 29 | 30 | ; ----------------------------------------------------------------------------- 31 | ; b_output -- Outputs characters 32 | ; IN: RSI = message location (non zero-terminated) 33 | ; RCX = number of chars to output 34 | ; OUT: All registers preserved 35 | b_output: 36 | push rsi ; Message location 37 | push rcx ; Counter of chars left to output 38 | push rax ; AL is used for the output function 39 | 40 | call [0x00100018] 41 | 42 | ; bt qword [os_SysConfEn], 1 << 2 43 | ; jnc b_output_done 44 | ; 45 | ;b_output_nextchar: 46 | ; jrcxz b_output_done ; If RCX is 0 then the function is complete 47 | ; dec rcx 48 | ; lodsb ; Get char from string and store in AL 49 | ; call serial_send 50 | ; jmp b_output_nextchar 51 | 52 | b_output_done: 53 | pop rax 54 | pop rcx 55 | pop rsi 56 | ret 57 | ; ----------------------------------------------------------------------------- 58 | 59 | 60 | ; ============================================================================= 61 | ; EOF 62 | -------------------------------------------------------------------------------- /src/syscalls/net.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; Network Functions 6 | ; ============================================================================= 7 | 8 | 9 | ; ----------------------------------------------------------------------------- 10 | ; b_net_status -- Check if network access is available 11 | ; IN: RDX = Interface ID 12 | ; OUT: RAX = MAC Address if net is enabled, otherwise 0 13 | b_net_status: 14 | push rsi 15 | push rcx 16 | 17 | cld 18 | xor eax, eax 19 | cmp byte [os_NetEnabled], 0 20 | je b_net_status_end 21 | 22 | mov ecx, 6 23 | mov rsi, os_NetMAC 24 | b_net_status_loadMAC: 25 | shl rax, 8 26 | lodsb 27 | sub ecx, 1 28 | test ecx, ecx 29 | jnz b_net_status_loadMAC 30 | 31 | b_net_status_end: 32 | pop rcx 33 | pop rsi 34 | ret 35 | ; ----------------------------------------------------------------------------- 36 | 37 | 38 | ; ----------------------------------------------------------------------------- 39 | ; b_net_tx -- Transmit a packet via the network 40 | ; IN: RSI = Memory location where packet is stored 41 | ; RCX = Length of packet 42 | ; RDX = Interface ID 43 | ; OUT: Nothing. All registers preserved 44 | b_net_tx: 45 | push rcx 46 | push rax 47 | 48 | cmp byte [os_NetEnabled], 1 ; Check if networking is enabled 49 | jne b_net_tx_fail 50 | 51 | b_net_tx_maxcheck: 52 | cmp rcx, 1522 ; Fail if more than 1522 bytes 53 | ja b_net_tx_fail 54 | 55 | mov rax, os_NetLock ; Lock the net so only one send can happen at a time 56 | call b_smp_lock 57 | 58 | ; Calculate where in physical memory the data should be read from 59 | xchg rax, rsi 60 | call os_virt_to_phys 61 | xchg rax, rsi 62 | 63 | inc qword [os_net_TXPackets] 64 | add qword [os_net_TXBytes], rcx 65 | call qword [os_net_transmit] ; Call the driver 66 | 67 | mov rax, os_NetLock 68 | call b_smp_unlock 69 | 70 | b_net_tx_fail: 71 | pop rax 72 | pop rcx 73 | ret 74 | ; ----------------------------------------------------------------------------- 75 | 76 | 77 | ; ----------------------------------------------------------------------------- 78 | ; b_net_rx -- Polls the network for received data 79 | ; IN: RDI = Memory location where packet will be stored 80 | ; RDX = Interface ID 81 | ; OUT: RCX = Length of packet, 0 if no data 82 | ; All other registers preserved 83 | b_net_rx: 84 | push rdi 85 | push rsi 86 | push rax 87 | 88 | xor ecx, ecx 89 | 90 | cmp byte [os_NetEnabled], 1 ; Check if networking is enabled 91 | jne b_net_rx_nodata 92 | 93 | call qword [os_net_poll] ; Call the driver 94 | cmp cx, 0 95 | je b_net_rx_nodata 96 | inc qword [os_net_RXPackets] 97 | add qword [os_net_RXBytes], rcx 98 | 99 | mov rsi, os_PacketBuffers ; Packet exists here 100 | push rcx 101 | rep movsb ; Copy packet to requested address 102 | pop rcx 103 | 104 | pop rax 105 | pop rsi 106 | pop rdi 107 | ret 108 | 109 | b_net_rx_nodata: 110 | xor ecx, ecx 111 | pop rax 112 | pop rsi 113 | pop rdi 114 | ret 115 | ; ----------------------------------------------------------------------------- 116 | 117 | 118 | ; ============================================================================= 119 | ; EOF 120 | -------------------------------------------------------------------------------- /src/syscalls/nvs.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; Non-volatile Storage Functions 6 | ; ============================================================================= 7 | 8 | 9 | ; ----------------------------------------------------------------------------- 10 | ; b_nvs_read -- Read sectors from a drive 11 | ; IN: RAX = Starting sector 12 | ; RCX = Number of sectors to read 13 | ; RDX = Drive 14 | ; RDI = Memory address to store data 15 | ; OUT: RCX = Number of sectors read (0 on error) 16 | ; All other registers preserved 17 | b_nvs_read: 18 | push r8 19 | push rdi 20 | push rcx 21 | push rbx 22 | push rax 23 | 24 | mov r8, rcx 25 | 26 | ; Calculate where in physical memory the data should be written to 27 | xchg rax, rdi 28 | call os_virt_to_phys 29 | xchg rax, rdi 30 | 31 | b_nvs_read_sector: 32 | mov rcx, 1 33 | mov ebx, 2 ; Read opcode for driver 34 | call [os_nvs_io] ; Call the non-volatile storage driver IO command 35 | add rdi, 4096 36 | sub r8, 1 37 | jne b_nvs_read_sector 38 | 39 | pop rax 40 | pop rbx 41 | pop rcx 42 | pop rdi 43 | pop r8 44 | ret 45 | ; ----------------------------------------------------------------------------- 46 | 47 | 48 | ; ----------------------------------------------------------------------------- 49 | ; b_nvs_write -- Write sectors to a drive 50 | ; IN: RAX = Starting sector 51 | ; RCX = Number of sectors to write 52 | ; RDX = Drive 53 | ; RSI = Memory address of data to store 54 | ; OUT: RCX = Number of sectors written (0 on error) 55 | ; All other registers preserved 56 | b_nvs_write: 57 | push r8 58 | push rdi 59 | push rcx 60 | push rbx 61 | push rax 62 | 63 | mov rdi, rsi ; The I/O functions only use RDI for the memory address 64 | mov r8, rcx 65 | 66 | ; Calculate where in physical memory the data should be read from 67 | xchg rax, rdi 68 | call os_virt_to_phys 69 | xchg rax, rdi 70 | 71 | b_nvs_write_sector: 72 | mov rcx, 1 73 | mov ebx, 1 ; Write opcode for driver 74 | call qword [os_nvs_io] ; Call the non-volatile driver IO command 75 | add rdi, 4096 76 | sub r8, 1 77 | jne b_nvs_write_sector 78 | 79 | pop rax 80 | pop rbx 81 | pop rcx 82 | pop rdi 83 | pop r8 84 | ret 85 | ; ----------------------------------------------------------------------------- 86 | 87 | 88 | ; ============================================================================= 89 | ; EOF 90 | -------------------------------------------------------------------------------- /src/syscalls/smp.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; SMP Functions 6 | ; ============================================================================= 7 | 8 | 9 | ; ----------------------------------------------------------------------------- 10 | ; b_smp_reset -- Resets a CPU Core 11 | ; IN: AL = CPU # 12 | ; OUT: Nothing. All registers preserved. 13 | ; Note: This code resets an AP for set-up use only. 14 | b_smp_reset: 15 | push rcx 16 | push rax 17 | 18 | cli 19 | b_smp_reset_wait: 20 | mov ecx, APIC_ICRL 21 | call os_apic_read 22 | bt eax, 12 ; Check if Delivery Status is 0 (Idle) 23 | jc b_smp_reset_wait ; If not, wait - a send is already pending 24 | mov rax, [rsp] ; Retrieve CPU APIC # from the stack 25 | mov ecx, APIC_ICRH 26 | shl eax, 24 ; AL holds the CPU APIC #, shift left 24 bits to get it into 31:24, 23:0 are reserved 27 | call os_apic_write ; Write to the high bits first 28 | mov ecx, APIC_ICRL 29 | xor eax, eax ; Clear EAX, namely bits 31:24 30 | mov al, 0x81 ; Execute interrupt 0x81 31 | call os_apic_write ; Then write to the low bits 32 | sti 33 | 34 | pop rax 35 | pop rcx 36 | ret 37 | ; ----------------------------------------------------------------------------- 38 | 39 | 40 | ; ----------------------------------------------------------------------------- 41 | ; b_smp_wakeup -- Wake up a CPU Core 42 | ; IN: AL = CPU # 43 | ; OUT: Nothing. All registers preserved. 44 | b_smp_wakeup: 45 | push rcx 46 | push rax 47 | 48 | cli 49 | b_smp_wakeup_wait: 50 | mov ecx, APIC_ICRL 51 | call os_apic_read 52 | bt eax, 12 ; Check if Delivery Status is 0 (Idle) 53 | jc b_smp_wakeup_wait ; If not, wait - a send is already pending 54 | mov rax, [rsp] ; Retrieve CPU APIC # from the stack 55 | mov ecx, APIC_ICRH 56 | shl eax, 24 ; AL holds the CPU APIC #, shift left 24 bits to get it into 31:24, 23:0 are reserved 57 | call os_apic_write ; Write to the high bits first 58 | mov ecx, APIC_ICRL 59 | xor eax, eax ; Clear EAX, namely bits 31:24 60 | mov al, 0x80 ; Execute interrupt 0x81 61 | call os_apic_write ; Then write to the low bits 62 | sti 63 | 64 | pop rax 65 | pop rcx 66 | ret 67 | ; ----------------------------------------------------------------------------- 68 | 69 | 70 | ; ----------------------------------------------------------------------------- 71 | ; b_smp_wakeup_all -- Wake up all CPU Cores 72 | ; IN: Nothing. 73 | ; OUT: Nothing. All registers preserved. 74 | b_smp_wakeup_all: 75 | push rcx 76 | push rax 77 | 78 | cli 79 | b_smp_wakeup_all_wait: 80 | mov ecx, APIC_ICRL 81 | call os_apic_read 82 | bt eax, 12 ; Check if Delivery Status is 0 (Idle) 83 | jc b_smp_wakeup_all_wait ; If not, wait - a send is already pending 84 | mov ecx, APIC_ICRH 85 | xor eax, eax 86 | call os_apic_write ; Write to the high bits first 87 | mov ecx, APIC_ICRL 88 | mov eax, 0x000C0080 ; Execute interrupt 0x80 on All Excluding Self (0xC) 89 | call os_apic_write ; Then write to the low bits 90 | sti 91 | 92 | pop rax 93 | pop rcx 94 | ret 95 | ; ----------------------------------------------------------------------------- 96 | 97 | 98 | ; ----------------------------------------------------------------------------- 99 | ; b_smp_get_id -- Returns the APIC ID of the CPU that ran this function 100 | ; IN: Nothing 101 | ; OUT: RAX = CPU's APIC ID number, All other registers preserved. 102 | b_smp_get_id: 103 | push rcx 104 | 105 | mov ecx, APIC_ID 106 | call os_apic_read ; Write to the high bits first 107 | shr rax, 24 ; AL now holds the CPU's APIC ID (0 - 255) 108 | 109 | pop rcx 110 | ret 111 | ; ----------------------------------------------------------------------------- 112 | 113 | 114 | ; ----------------------------------------------------------------------------- 115 | ; b_smp_set -- Set a specific CPU to run code 116 | ; IN: RAX = Code address 117 | ; RCX = CPU APIC ID 118 | ; OUT: RAX = 0 on error 119 | ; Note: Code address must be 16-byte aligned 120 | b_smp_set: 121 | push rdi 122 | push rax 123 | push rcx ; Save the APIC ID 124 | push rax ; Save the code address 125 | 126 | mov rdi, os_SMP 127 | shl rcx, 3 ; Quick multiply by 8 128 | add rdi, rcx ; Add the offset 129 | mov rcx, [rdi] ; Load current value for that core 130 | 131 | bt cx, 0 ; Check for "present" flag 132 | jnc b_smp_set_error ; Bail out if 0 133 | 134 | and cl, 0xF0 ; Clear the flags from the value in table 135 | cmp rcx, 0 ; Is there already a code address set? 136 | jne b_smp_set_error ; Bail out if the core is already set 137 | 138 | and al, 0x0F ; Keep only the lower 4 bits 139 | cmp al, 0 ; Are the lower 4 bits of the code address set to 0? 140 | jne b_smp_set_error ; Bail out if not as the code address isn't properly aligned 141 | 142 | pop rax ; Restore the code address 143 | or al, 0x01 ; Make sure the present flag is set 144 | mov [rdi], rax ; Store code address 145 | 146 | pop rcx ; Restore the APIC ID 147 | xchg rax, rcx 148 | call b_smp_wakeup ; Wake up the core 149 | xchg rax, rcx 150 | 151 | pop rax 152 | pop rdi 153 | ret 154 | 155 | b_smp_set_error: 156 | pop rax 157 | xor eax, eax ; Return 0 for error 158 | pop rcx 159 | pop rax 160 | pop rdi 161 | ret 162 | ; ----------------------------------------------------------------------------- 163 | 164 | 165 | ; ----------------------------------------------------------------------------- 166 | ; b_smp_get -- Returns a CPU code address and flags 167 | ; IN: Nothing 168 | ; OUT: RAX = Code address (bits 63:4) and flags (bits 3:0) 169 | b_smp_get: 170 | push rsi 171 | 172 | call b_smp_get_id ; Return APIC ID in RAX 173 | 174 | mov rsi, os_SMP 175 | shl rax, 3 ; Quick multiply by 8 176 | add rsi, rax ; Add the offset 177 | mov rax, [rsi] ; Load code address and flags 178 | pop rsi 179 | ret 180 | ; ----------------------------------------------------------------------------- 181 | 182 | 183 | ; ----------------------------------------------------------------------------- 184 | ; b_smp_setflag -- Set a CPU flag 185 | ; IN: RCX = Flag # 186 | ; OUT: Nothing 187 | b_smp_setflag: 188 | push rsi 189 | push rax 190 | 191 | cmp rcx, 4 ; If a value higher than 3 was chosen then bail out 192 | jae b_smp_setflag_done 193 | 194 | call b_smp_get_id ; Return APIC ID in RAX 195 | 196 | mov rsi, os_SMP 197 | shl rax, 3 ; Quick multiply by 8 198 | add rsi, rax ; Add the offset 199 | mov rax, [rsi] ; Load code address and flags 200 | bts rax, rcx ; Set the flag 201 | mov [rsi], rax ; Store the code address and new flags 202 | 203 | b_smp_setflag_done: 204 | pop rax 205 | pop rsi 206 | ret 207 | ; ----------------------------------------------------------------------------- 208 | 209 | 210 | ; ----------------------------------------------------------------------------- 211 | ; b_smp_busy -- Check if CPU cores are busy 212 | ; IN: Nothing 213 | ; OUT: RAX = 1 if CPU cores are busy, 0 if not. 214 | ; All other registers preserved. 215 | ; Note: This ignores the core it is running on 216 | b_smp_busy: 217 | push rsi 218 | push rcx 219 | push rbx 220 | 221 | call b_smp_get_id 222 | mov bl, al ; Store local APIC ID in BL 223 | xor ecx, ecx 224 | mov rsi, os_SMP 225 | 226 | b_smp_busy_read: 227 | lodsq ; Load a single CPU entry. Flags are in AL 228 | cmp bl, cl ; Compare entry to local APIC ID 229 | je b_smp_busy_skip ; Skip the entry for the current CPU 230 | inc cx 231 | cmp rax, 0x01 ; Bit 0 (Present) can be 0 or 1 232 | ja b_smp_busy_yes 233 | cmp cx, 0x100 ; Only read up to 256 CPU cores 234 | jne b_smp_busy_read 235 | 236 | b_smp_busy_no: 237 | xor eax, eax 238 | jmp b_smp_busy_end 239 | 240 | b_smp_busy_skip: 241 | inc cx 242 | jmp b_smp_busy_read 243 | 244 | b_smp_busy_yes: 245 | mov eax, 1 246 | 247 | b_smp_busy_end: 248 | pop rbx 249 | pop rcx 250 | pop rsi 251 | ret 252 | ; ----------------------------------------------------------------------------- 253 | 254 | 255 | ; ----------------------------------------------------------------------------- 256 | ; b_smp_config -- Just a stub for now 257 | ; IN: Nothing 258 | ; OUT: Nothing. All registers preserved. 259 | b_smp_config: 260 | ret 261 | ; ----------------------------------------------------------------------------- 262 | 263 | 264 | ; ----------------------------------------------------------------------------- 265 | ; b_smp_lock -- Attempt to lock a mutex 266 | ; IN: RAX = Address of lock variable 267 | ; OUT: Nothing. All registers preserved. 268 | b_smp_lock: 269 | bt word [rax], 0 ; Check if the mutex is free (Bit 0 cleared to 0) 270 | jc b_smp_lock ; If not check it again 271 | lock bts word [rax], 0 ; The mutex was free, lock the bus. Try to grab the mutex 272 | jc b_smp_lock ; Jump if we were unsuccessful 273 | ret ; Lock acquired. Return to the caller 274 | ; ----------------------------------------------------------------------------- 275 | 276 | 277 | ; ----------------------------------------------------------------------------- 278 | ; b_smp_unlock -- Unlock a mutex 279 | ; IN: RAX = Address of lock variable 280 | ; OUT: Nothing. All registers preserved. 281 | b_smp_unlock: 282 | btr word [rax], 0 ; Release the lock (Bit 0 cleared to 0) 283 | ret ; Lock released. Return to the caller 284 | ; ----------------------------------------------------------------------------- 285 | 286 | 287 | ; ============================================================================= 288 | ; EOF 289 | -------------------------------------------------------------------------------- /src/syscalls/system.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS written in Assembly for x86-64 systems 3 | ; Copyright (C) 2008-2025 Return Infinity -- see LICENSE.TXT 4 | ; 5 | ; System Functions 6 | ; ============================================================================= 7 | 8 | 9 | ; ----------------------------------------------------------------------------- 10 | ; b_system - Call system functions 11 | ; IN: RCX = Function 12 | ; RAX = Variable 1 13 | ; RDX = Variable 2 14 | ; OUT: RAX = Result 15 | ; All other registers preserved 16 | b_system: 17 | cmp rcx, 0x80 18 | jae b_system_end 19 | 20 | ; Use CX register as an index to the function table 21 | ; To save memory, the functions are placed in 16-bit frames 22 | push rcx 23 | lea ecx, [b_system_table+ecx*2] ; extract function from table by index 24 | mov cx, [ecx] ; limit jump to 16-bit 25 | call rcx ; call function 26 | pop rcx 27 | 28 | b_system_end: 29 | ret 30 | 31 | ; Basic 32 | 33 | b_system_timecounter: 34 | mov ecx, 0xF0 35 | call os_hpet_read 36 | ret 37 | 38 | b_system_free_memory: 39 | mov eax, [os_MemAmount] 40 | ret 41 | 42 | b_system_getmouse: 43 | mov rax, [os_ps2_mouse] 44 | ret 45 | 46 | ; CPU 47 | 48 | b_system_smp_get_id: 49 | call b_smp_get_id 50 | ret 51 | 52 | b_system_smp_numcores: 53 | xor eax, eax 54 | mov ax, [os_NumCores] 55 | ret 56 | 57 | b_system_smp_set: 58 | mov rcx, rdx 59 | call b_smp_set 60 | ret 61 | 62 | b_system_smp_get: 63 | call b_smp_get 64 | ret 65 | 66 | b_system_smp_lock: 67 | call b_smp_lock 68 | ret 69 | 70 | b_system_smp_unlock: 71 | call b_smp_unlock 72 | ret 73 | 74 | b_system_smp_busy: 75 | call b_smp_busy 76 | ret 77 | 78 | b_system_tsc: 79 | call b_tsc 80 | ret 81 | 82 | ; Video 83 | 84 | b_system_screen_lfb_get: 85 | mov rax, [os_screen_lfb] 86 | ret 87 | 88 | b_system_screen_x_get: 89 | xor eax, eax 90 | mov ax, [os_screen_x] 91 | ret 92 | 93 | b_system_screen_y_get: 94 | xor eax, eax 95 | mov ax, [os_screen_y] 96 | ret 97 | 98 | b_system_screen_ppsl_get: 99 | xor eax, eax 100 | mov ax, [os_screen_ppsl] 101 | ret 102 | 103 | b_system_screen_bpp_get: 104 | xor eax, eax 105 | mov ax, [os_screen_bpp] 106 | ret 107 | 108 | ; Network 109 | 110 | b_system_mac_get: 111 | call b_net_status 112 | ret 113 | 114 | ; Bus 115 | 116 | b_system_pci_read: 117 | call os_bus_read 118 | ret 119 | 120 | b_system_pci_write: 121 | call os_bus_write 122 | ret 123 | 124 | ; Standard Output 125 | 126 | b_system_stdout_get: 127 | mov rax, qword [0x100018] 128 | ret 129 | 130 | b_system_stdout_set: 131 | mov qword [0x100018], rax 132 | ret 133 | 134 | ; Misc 135 | 136 | b_system_callback_timer: 137 | ret 138 | 139 | b_system_callback_network: 140 | ret 141 | 142 | b_system_callback_keyboard: 143 | ret 144 | 145 | b_system_callback_mouse: 146 | mov [os_MouseCallback], rax 147 | ret 148 | 149 | b_system_debug_dump_mem: 150 | push rsi 151 | mov rsi, rax 152 | mov rcx, rdx 153 | call os_debug_dump_mem 154 | pop rsi 155 | ret 156 | 157 | b_system_debug_dump_rax: 158 | call os_debug_dump_rax 159 | ret 160 | 161 | b_system_delay: 162 | call b_delay 163 | ret 164 | 165 | b_system_reset: 166 | xor eax, eax 167 | call b_smp_get_id ; Reset all other cpu cores 168 | mov rbx, rax 169 | mov esi, 0x00005100 ; Location in memory of the Pure64 CPU data 170 | b_system_reset_next_ap: 171 | test cx, cx 172 | jz b_system_reset_no_more_aps 173 | lodsb ; Load the CPU APIC ID 174 | cmp al, bl 175 | je b_system_reset_skip_ap 176 | call b_smp_reset ; Reset the CPU 177 | b_system_reset_skip_ap: 178 | dec cx 179 | jmp b_system_reset_next_ap 180 | b_system_reset_no_more_aps: 181 | int 0x81 ; Reset this core 182 | 183 | b_system_reboot: 184 | call reboot 185 | 186 | b_system_shutdown: 187 | mov ax, 0x2000 188 | mov dx, 0xB004 189 | out dx, ax ; Bochs/QEMU < v2 190 | mov dx, 0x0604 191 | out dx, ax ; QEMU 192 | mov ax, 0x3400 193 | mov dx, 0x4004 194 | out dx, ax ; VirtualBox 195 | jmp $ 196 | ; ----------------------------------------------------------------------------- 197 | 198 | 199 | ; ----------------------------------------------------------------------------- 200 | ; b_delay -- Delay by X microseconds 201 | ; IN: RAX = Time microseconds 202 | ; OUT: All registers preserved 203 | ; Note: There are 1,000,000 microseconds in a second 204 | ; There are 1,000 milliseconds in a second 205 | b_delay: 206 | push rdx 207 | push rcx 208 | push rbx 209 | push rax 210 | 211 | mov rbx, rax ; Save delay to RBX 212 | xor edx, edx 213 | mov ecx, [os_HPET_Frequency] 214 | mov rax, 1000000000 215 | div rcx ; RAX = RDX:RAX / RCX (converting from period in femtoseconds to frequency in MHz) 216 | xor edx, edx 217 | mul rbx ; RAX *= RBX, should get number of HPET cycles to wait, save result in RBX 218 | mov rbx, rax 219 | mov ecx, HPET_MAIN_COUNTER 220 | call os_hpet_read ; Get HPET counter in RAX 221 | add rbx, rax ; RBX += RAX Until when to wait 222 | b_delay_loop: ; Stay in this loop until the HPET timer reaches the expected value 223 | mov ecx, HPET_MAIN_COUNTER 224 | call os_hpet_read ; Get HPET counter in RAX 225 | cmp rax, rbx ; If RAX < RBX then jump to loop, otherwise fall through to end 226 | jb b_delay_loop 227 | b_delay_end: 228 | 229 | pop rax 230 | pop rbx 231 | pop rcx 232 | pop rdx 233 | ret 234 | ; ----------------------------------------------------------------------------- 235 | 236 | 237 | ; ----------------------------------------------------------------------------- 238 | ; b_tsc -- Read the Time-Stamp Counter and store in RAX 239 | ; IN: Nothing 240 | ; OUT: RAX = Current Time-Stamp Counter value 241 | ; All other registers preserved 242 | b_tsc: 243 | push rdx 244 | rdtsc ; Reads the TSC into EDX:EAX 245 | shl rdx, 32 ; Shift the low 32-bits to the high 32-bits 246 | or rax, rdx ; Combine RAX and RDX 247 | pop rdx 248 | ret 249 | ; ----------------------------------------------------------------------------- 250 | 251 | 252 | ; ----------------------------------------------------------------------------- 253 | ; reboot -- Reboot the computer 254 | reboot: 255 | mov al, PS2_RESET_CPU 256 | call ps2_send_cmd 257 | jmp reboot 258 | ; ----------------------------------------------------------------------------- 259 | 260 | 261 | ; ----------------------------------------------------------------------------- 262 | ; os_virt_to_phys -- Function to convert a virtual address to a physical address 263 | ; IN: RAX = Virtual Memory Address 264 | ; OUT: RAX = Physical Memory Address 265 | ; All other registers preserved 266 | ; NOTE: BareMetal uses two ranges of memory. One physical 1-to-1 map and one virtual 267 | ; range for free memory 268 | os_virt_to_phys: 269 | push r15 270 | push rbx 271 | 272 | mov r15, 0xFFFF800000000000 ; Starting address of the higher half 273 | cmp rax, r15 ; Check if RAX is in the upper canonical range 274 | jb os_virt_to_phys_done ; If not, it is already a physical address - bail out 275 | mov rbx, rax ; Save RAX 276 | and rbx, 0x1FFFFF ; Save the low 20 bits 277 | mov r15, 0x7FFFFFFFFFFF 278 | and rax, r15 279 | mov r15, sys_pdh ; Location of virtual memory PDs 280 | shr rax, 21 ; Convert 2MB page to entry 281 | shl rax, 3 282 | add r15, rax 283 | mov rax, [r15] ; Load the entry into RAX 284 | shr rax, 8 ; Clear the low 8 bits 285 | shl rax, 8 286 | add rax, rbx 287 | 288 | os_virt_to_phys_done: 289 | pop rbx 290 | pop r15 291 | ret 292 | ; ----------------------------------------------------------------------------- 293 | 294 | 295 | ; ----------------------------------------------------------------------------- 296 | ; os_stub -- A function that just returns 297 | b_user: 298 | os_stub: 299 | none: 300 | ret 301 | ; ----------------------------------------------------------------------------- 302 | 303 | 304 | ; ----------------------------------------------------------------------------- 305 | ; System function index table 306 | b_system_table: 307 | ; Basic 308 | dw b_system_timecounter ; 0x00 309 | dw b_system_free_memory ; 0x01 310 | dw b_system_getmouse ; 0x02 311 | dw none ; 0x03 312 | dw none ; 0x04 313 | dw none ; 0x05 314 | dw none ; 0x06 315 | dw none ; 0x07 316 | dw none ; 0x08 317 | dw none ; 0x09 318 | dw none ; 0x0A 319 | dw none ; 0x0B 320 | dw none ; 0x0C 321 | dw none ; 0x0D 322 | dw none ; 0x0E 323 | dw none ; 0x0F 324 | 325 | ; CPU 326 | dw b_system_smp_get_id ; 0x10 327 | dw b_system_smp_numcores ; 0x11 328 | dw b_system_smp_set ; 0x12 329 | dw b_system_smp_get ; 0x13 330 | dw b_system_smp_lock ; 0x14 331 | dw b_system_smp_unlock ; 0x15 332 | dw b_system_smp_busy ; 0x16 333 | dw none ; 0x17 334 | dw none ; 0x18 335 | dw none ; 0x19 336 | dw none ; 0x1A 337 | dw none ; 0x1B 338 | dw none ; 0x1C 339 | dw none ; 0x1D 340 | dw none ; 0x1E 341 | dw b_system_tsc ; 0x1F 342 | 343 | ; Video 344 | dw b_system_screen_lfb_get ; 0x20 345 | dw b_system_screen_x_get ; 0x21 346 | dw b_system_screen_y_get ; 0x22 347 | dw b_system_screen_ppsl_get ; 0x23 348 | dw b_system_screen_bpp_get ; 0x24 349 | dw none ; 0x25 350 | dw none ; 0x26 351 | dw none ; 0x27 352 | dw none ; 0x28 353 | dw none ; 0x29 354 | dw none ; 0x2A 355 | dw none ; 0x2B 356 | dw none ; 0x2C 357 | dw none ; 0x2D 358 | dw none ; 0x2E 359 | dw none ; 0x2F 360 | 361 | ; Network 362 | dw b_system_mac_get ; 0x30 363 | dw none ; 0x31 364 | dw none ; 0x32 365 | dw none ; 0x33 366 | dw none ; 0x34 367 | dw none ; 0x35 368 | dw none ; 0x36 369 | dw none ; 0x37 370 | dw none ; 0x38 371 | dw none ; 0x39 372 | dw none ; 0x3A 373 | dw none ; 0x3B 374 | dw none ; 0x3C 375 | dw none ; 0x3D 376 | dw none ; 0x3E 377 | dw none ; 0x3F 378 | 379 | ; Storage 380 | dw none ; 0x40 381 | dw none ; 0x41 382 | dw none ; 0x42 383 | dw none ; 0x43 384 | dw none ; 0x44 385 | dw none ; 0x45 386 | dw none ; 0x46 387 | dw none ; 0x47 388 | dw none ; 0x48 389 | dw none ; 0x49 390 | dw none ; 0x4A 391 | dw none ; 0x4B 392 | dw none ; 0x4C 393 | dw none ; 0x4D 394 | dw none ; 0x4E 395 | dw none ; 0x4F 396 | 397 | ; Misc 398 | dw b_system_pci_read ; 0x50 399 | dw b_system_pci_write ; 0x51 400 | dw b_system_stdout_set ; 0x52 401 | dw b_system_stdout_get ; 0x53 402 | dw none ; 0x54 403 | dw none ; 0x55 404 | dw none ; 0x56 405 | dw none ; 0x57 406 | dw none ; 0x58 407 | dw none ; 0x59 408 | dw none ; 0x5A 409 | dw none ; 0x5B 410 | dw none ; 0x5C 411 | dw none ; 0x5D 412 | dw none ; 0x5E 413 | dw none ; 0x5F 414 | dw b_system_callback_timer ; 0x60 415 | dw b_system_callback_network ; 0x61 416 | dw b_system_callback_keyboard ; 0x62 417 | dw b_system_callback_mouse ; 0x63 418 | dw none ; 0x64 419 | dw none ; 0x65 420 | dw none ; 0x66 421 | dw none ; 0x67 422 | dw none ; 0x68 423 | dw none ; 0x69 424 | dw none ; 0x6A 425 | dw none ; 0x6B 426 | dw none ; 0x6C 427 | dw none ; 0x6D 428 | dw none ; 0x6E 429 | dw none ; 0x6F 430 | 431 | ; Misc 432 | dw b_system_debug_dump_mem ; 0x70 433 | dw b_system_debug_dump_rax ; 0x71 434 | dw b_system_delay ; 0x72 435 | dw none ; 0x73 436 | dw none ; 0x74 437 | dw none ; 0x75 438 | dw none ; 0x76 439 | dw none ; 0x77 440 | dw none ; 0x78 441 | dw none ; 0x79 442 | dw none ; 0x7A 443 | dw none ; 0x7B 444 | dw none ; 0x7C 445 | dw b_system_reset ; 0x7D 446 | dw b_system_reboot ; 0x7E 447 | dw b_system_shutdown ; 0x7F 448 | ; ----------------------------------------------------------------------------- 449 | 450 | 451 | ; ============================================================================= 452 | ; EOF 453 | -------------------------------------------------------------------------------- /src/sysvar.asm: -------------------------------------------------------------------------------- 1 | ; ============================================================================= 2 | ; BareMetal -- a 64-bit OS 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 | ; Strings 10 | newline: db 13, 10, 0 11 | space: db ' ', 0 12 | system_status_header: db 'BareMetal v1.0.0', 0 13 | 14 | ; Memory addresses 15 | 16 | ; x86-64 structures 17 | sys_idt: equ 0x0000000000000000 ; 0x000000 -> 0x000FFF 4K Interrupt descriptor table 18 | sys_gdt: equ 0x0000000000001000 ; 0x001000 -> 0x001FFF 4K Global descriptor table 19 | sys_pml4: equ 0x0000000000002000 ; 0x002000 -> 0x002FFF 4K PML4 table 20 | sys_pdpl: equ 0x0000000000003000 ; 0x003000 -> 0x003FFF 4K PDP table low 21 | sys_pdph: equ 0x0000000000004000 ; 0x004000 -> 0x004FFF 4K PDP table high 22 | sys_Pure64: equ 0x0000000000005000 ; 0x005000 -> 0x007FFF 12K Pure64 system data 23 | 24 | ; 0x008000 -> 0x00FFFF 32K Free 25 | 26 | sys_pdl: equ 0x0000000000010000 ; 0x010000 -> 0x01FFFF 64K Page directory low (Maps up to 16GB of 2MiB pages or 8TB of 1GiB pages) 27 | sys_pdh: equ 0x0000000000020000 ; 0x020000 -> 0x09FFFF 512K Page directory high (Maps up to 128GB) 28 | 29 | sys_ROM: equ 0x00000000000A0000 ; 0x0A0000 -> 0x0FFFFF 384K System ROM 30 | 31 | ; Kernel memory 32 | os_KernelStart: equ 0x0000000000100000 ; 0x100000 -> 0x10FFFF 64K Kernel 33 | os_SystemVariables: equ 0x0000000000110000 ; 0x110000 -> 0x11FFFF 64K System Variables 34 | 35 | ; 0x012000 -> 0x012FFF 64K Free 36 | 37 | ; System memory 38 | 39 | ; Non-volatile Storage memory 40 | os_nvs_mem: equ 0x0000000000130000 ; 0x130000 -> 0x15FFFF 192K NVS structures/buffers 41 | 42 | ; USB memory 43 | os_usb_mem: equ 0x0000000000160000 ; 0x160000 -> 0x19FFFF 256K USB structures/buffers 44 | 45 | ; Network memory 46 | os_net_mem: equ 0x00000000001A0000 ; 0x1A0000 -> 0x1BFFFF 128K Network descriptors/buffers 47 | os_rx_desc: equ 0x00000000001A0000 ; 0x1A0000 -> 0x1A7FFF 32K Ethernet receive descriptors 48 | os_tx_desc: equ 0x00000000001A8000 ; 0x1A8000 -> 0x1AFFFF 32K Ethernet transmit descriptors 49 | os_PacketBuffers: equ 0x00000000001B0000 ; 0x1B0000 -> 0x1BFFFF 64K Ethernet packet buffers 50 | 51 | ; LFB font data 52 | os_font: equ 0x00000000001C0000 ; 0x1C0000 -> 0x1CFFFF 64K Font video data 53 | 54 | ; 0x1D0000 -> 0x1DFFFF 64K Free 55 | 56 | ; Misc memory 57 | os_SMP: equ 0x00000000001FF800 ; SMP table. Each item is 8 bytes. (2KiB before the 2MiB mark, Room for 256 entries) 58 | app_start: equ 0xFFFF800000000000 ; Location of application memory 59 | 60 | 61 | ; DQ - Starting at offset 0, increments by 8 62 | os_LocalAPICAddress: equ os_SystemVariables + 0x0000 63 | os_IOAPICAddress: equ os_SystemVariables + 0x0008 64 | os_SysConfEn: equ os_SystemVariables + 0x0010 ; Enabled bits: 0=PS/2 Keyboard, 1=PS/2 Mouse, 2=Serial, 4=HPET, 5=xHCI 65 | os_PacketAddress: equ os_SystemVariables + 0x0018 66 | os_StackBase: equ os_SystemVariables + 0x0020 67 | os_net_transmit: equ os_SystemVariables + 0x0028 68 | os_net_poll: equ os_SystemVariables + 0x0030 69 | os_net_ackint: equ os_SystemVariables + 0x0038 70 | os_NetIOBaseMem: equ os_SystemVariables + 0x0040 71 | os_NetMAC: equ os_SystemVariables + 0x0048 72 | os_HPET_Address: equ os_SystemVariables + 0x0050 73 | os_AHCI_Base: equ os_SystemVariables + 0x0058 74 | os_NetworkCallback: equ os_SystemVariables + 0x0060 75 | os_KeyboardCallback: equ os_SystemVariables + 0x0068 76 | os_ClockCallback: equ os_SystemVariables + 0x0070 77 | os_net_TXBytes: equ os_SystemVariables + 0x0078 78 | os_net_TXPackets: equ os_SystemVariables + 0x0080 79 | os_net_RXBytes: equ os_SystemVariables + 0x0088 80 | os_net_RXPackets: equ os_SystemVariables + 0x0090 81 | ;os_hdd_BytesRead: equ os_SystemVariables + 0x0098 82 | ;os_hdd_BytesWrite: equ os_SystemVariables + 0x00A0 83 | os_NVMe_Base: equ os_SystemVariables + 0x00A8 84 | os_nvs_io: equ os_SystemVariables + 0x00B0 85 | os_nvs_id: equ os_SystemVariables + 0x00B8 86 | os_screen_lfb: equ os_SystemVariables + 0x00C0 87 | os_virtioblk_base: equ os_SystemVariables + 0x00C8 88 | os_NetIOLength: equ os_SystemVariables + 0x00D0 89 | os_MouseCallback: equ os_SystemVariables + 0x00D8 90 | os_xHCI_Base: equ os_SystemVariables + 0x00E0 91 | os_usb_evtoken: equ os_SystemVariables + 0x00E8 92 | 93 | 94 | ; DD - Starting at offset 256, increments by 4 95 | os_HPETRate: equ os_SystemVariables + 0x0100 96 | os_MemAmount: equ os_SystemVariables + 0x0104 ; in MiB 97 | os_AHCI_PA: equ os_SystemVariables + 0x0108 ; Each set bit is an active port 98 | os_NVMeTotalLBA: equ os_SystemVariables + 0x010C 99 | os_apic_ver: equ os_SystemVariables + 0x0110 100 | os_HPET_Frequency: equ os_SystemVariables + 0x0114 101 | os_ps2_mouse_packet: equ os_SystemVariables + 0x0118 102 | os_xhci_int0_count: equ os_SystemVariables + 0x011C ; Incremented on xHCI Interrupter 0 103 | 104 | 105 | ; DW - Starting at offset 512, increments by 2 106 | os_NumCores: equ os_SystemVariables + 0x0200 107 | os_CoreSpeed: equ os_SystemVariables + 0x0202 108 | os_NetIOAddress: equ os_SystemVariables + 0x0204 109 | os_NetLock: equ os_SystemVariables + 0x0206 110 | os_nvsVar: equ os_SystemVariables + 0x0208 ; Bit 0 for NVMe, 1 for AHCI, 2 for ATA, 3 for Virtio Block 111 | os_screen_x: equ os_SystemVariables + 0x020A 112 | os_screen_y: equ os_SystemVariables + 0x020C 113 | os_screen_ppsl: equ os_SystemVariables + 0x020E 114 | os_screen_bpp: equ os_SystemVariables + 0x0210 115 | os_pcie_count: equ os_SystemVariables + 0x0212 116 | os_HPET_CounterMin: equ os_SystemVariables + 0x0214 117 | os_ps2_mouse: equ os_SystemVariables + 0x0218 118 | os_ps2_mouse_buttons: equ os_SystemVariables + 0x0218 ; Button state, bit 0 - left, bit 1 - right, bit 3 - middle. 0-released, 1-pressed 119 | os_ps2_mouse_x: equ os_SystemVariables + 0x021A ; Cursor screen position on X axis 120 | os_ps2_mouse_y: equ os_SystemVariables + 0x021C ; Cursor screen position on Y axis 121 | os_ps2_mouse_count: equ os_SystemVariables + 0x021E ; Byte counter 122 | os_boot_arch: equ os_SystemVariables + 0x0220 ; Bit 0 set for legacy ports, bit 1 set for 60/64 support 123 | 124 | 125 | ; DB - Starting at offset 768, increments by 1 126 | scancode: equ os_SystemVariables + 0x0300 127 | key: equ os_SystemVariables + 0x0301 128 | key_shift: equ os_SystemVariables + 0x0302 129 | os_BusEnabled: equ os_SystemVariables + 0x0303 ; 1 if PCI is enabled, 2 if PCIe is enabled 130 | os_NetEnabled: equ os_SystemVariables + 0x0304 ; 1 if a supported network card was enabled 131 | os_NetIRQ: equ os_SystemVariables + 0x0305 ; Set to Interrupt line that NIC is connected to 132 | ;os_NetActivity_TX: equ os_SystemVariables + 0x0306 133 | ;os_NetActivity_RX: equ os_SystemVariables + 0x0307 134 | ;os_EthernetBuffer_C1: equ os_SystemVariables + 0x0308 ; Counter 1 for the Ethernet RX Ring Buffer 135 | ;os_EthernetBuffer_C2: equ os_SystemVariables + 0x0309 ; Counter 2 for the Ethernet RX Ring Buffer 136 | ;os_nvsEnabled: equ os_SystemVariables + 0x030A 137 | ;os_nvsActivity: equ os_SystemVariables + 0x030B 138 | os_NVMeIRQ: equ os_SystemVariables + 0x030C 139 | os_NVMeMJR: equ os_SystemVariables + 0x030D 140 | os_NVMeMNR: equ os_SystemVariables + 0x030E 141 | os_NVMeTER: equ os_SystemVariables + 0x030F 142 | os_NVMeLBA: equ os_SystemVariables + 0x0310 143 | os_NVMe_atail: equ os_SystemVariables + 0x0311 144 | os_NVMe_iotail: equ os_SystemVariables + 0x0312 145 | os_AHCI_MJR: equ os_SystemVariables + 0x0313 146 | os_AHCI_MNR: equ os_SystemVariables + 0x0314 147 | os_AHCI_IRQ: equ os_SystemVariables + 0x0315 148 | os_ioapic_ver: equ os_SystemVariables + 0x0316 149 | os_ioapic_mde: equ os_SystemVariables + 0x0317 150 | key_control: equ os_SystemVariables + 0x0318 151 | os_BSP: equ os_SystemVariables + 0x0319 152 | os_HPET_IRQ: equ os_SystemVariables + 0x031A 153 | os_net_icount: equ os_SystemVariables + 0x031B 154 | 155 | ; Network Device Table Base 156 | os_net_table: equ os_SystemVariables + 0x2000 157 | 158 | ; Network Devices Entries - each entry is 64 bytes 159 | os_net_entry_base: equ os_SystemVariables + 0x2000 160 | os_net_entry_transmit: equ os_SystemVariables + 0x2008 161 | os_net_entry_poll: equ os_SystemVariables + 0x2010 162 | os_net_entry_stats: equ os_SystemVariables + 0x2018 163 | os_net_entry_MAC: equ os_SystemVariables + 0x2020 164 | os_net_entry_tx_tail: equ os_SystemVariables + 0x2028 165 | os_net_entry_rx_tail: equ os_SystemVariables + 0x202C 166 | ; Next Device starts at 0x8040 167 | 168 | bus_table: equ os_SystemVariables + 0x8000 169 | 170 | ; Misc 171 | tchar: db 0, 0 172 | 173 | 174 | ;------------------------------------------------------------------------------ 175 | 176 | SYS64_CODE_SEL equ 8 ; defined by Pure64 177 | 178 | ; ============================================================================= 179 | ; EOF 180 | --------------------------------------------------------------------------------