├── .gitattributes ├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE.txt ├── README.md ├── bochsrc ├── build.zig ├── config ├── default.json ├── files ├── gdb_script ├── image_config.json └── qemu.json ├── resources ├── FiraSans-Regular.otf └── zap-light16.psf ├── src ├── bootloader.zig ├── bootloader │ ├── arch.zig │ ├── arch │ │ ├── x86.zig │ │ ├── x86 │ │ │ └── 64 │ │ │ │ └── smp_trampoline.S │ │ └── x86_64.zig │ ├── bios.zig │ ├── limine.zig │ ├── limine │ │ ├── LICENSE.md │ │ ├── arch │ │ │ └── x86_64 │ │ │ │ └── linker_script.ld │ │ ├── installables │ │ │ ├── BOOTAA64.EFI │ │ │ ├── BOOTIA32.EFI │ │ │ ├── BOOTX64.EFI │ │ │ ├── LICENSE.md │ │ │ ├── Makefile │ │ │ ├── install-sh │ │ │ ├── limine-cd-efi.bin │ │ │ ├── limine-cd.bin │ │ │ ├── limine-deploy.c │ │ │ ├── limine-pxe.bin │ │ │ ├── limine-version.c │ │ │ ├── limine.cfg │ │ │ ├── limine.h │ │ │ └── limine.sys │ │ ├── installer.zig │ │ ├── main.zig │ │ └── test.zig │ ├── rise │ │ ├── bios │ │ │ ├── linker_script.ld │ │ │ ├── main.zig │ │ │ ├── test.zig │ │ │ └── unreal_mode.S │ │ └── uefi │ │ │ ├── main.zig │ │ │ └── test.zig │ ├── todo │ │ ├── draw_context.zig │ │ ├── font.zig │ │ └── smp.zig │ └── uefi.zig ├── common.zig ├── cpu.zig ├── cpu │ ├── arch.zig │ ├── arch │ │ ├── x86 │ │ │ └── 64 │ │ │ │ ├── init.zig │ │ │ │ ├── linker_script.ld │ │ │ │ └── syscall.zig │ │ └── x86_64.zig │ ├── capabilities.zig │ ├── main.zig │ ├── test.zig │ └── test_runner.zig ├── host.zig ├── host │ ├── disk_image_builder.zig │ ├── disk_image_builder │ │ ├── boot_disk.zig │ │ ├── main.zig │ │ └── test.zig │ ├── runner │ │ └── main.zig │ └── test.zig ├── lib.zig ├── lib │ ├── arch.zig │ ├── arch │ │ ├── x86.zig │ │ ├── x86 │ │ │ ├── 64 │ │ │ │ └── registers.zig │ │ │ └── common.zig │ │ └── x86_64.zig │ ├── config.zig │ ├── crc32.zig │ ├── disk.zig │ ├── extern_enum_array.zig │ ├── filesystem.zig │ ├── filesystem │ │ └── fat32.zig │ ├── graphics.zig │ ├── nls.zig │ ├── nls │ │ └── ascii.zig │ ├── partition_table.zig │ ├── partition_table │ │ ├── gpt.zig │ │ └── mbr.zig │ └── psf1.zig ├── privileged.zig ├── privileged │ ├── acpi.zig │ ├── arch.zig │ └── arch │ │ ├── x86.zig │ │ ├── x86 │ │ ├── 32 │ │ │ └── io.zig │ │ ├── 64 │ │ │ ├── apic.zig │ │ │ ├── io.zig │ │ │ ├── paging.zig │ │ │ └── registers.zig │ │ └── common.zig │ │ └── x86_64.zig ├── rise.zig ├── rise │ ├── arch.zig │ ├── arch │ │ └── x64_64.zig │ ├── capabilities.zig │ └── syscall.zig ├── user.zig └── user │ ├── arch.zig │ ├── arch │ ├── x86_64.zig │ └── x86_64 │ │ └── linker_script.ld │ ├── programs │ ├── device_manager │ │ ├── main.zig │ │ ├── module.json │ │ └── test.zig │ └── init │ │ ├── main.zig │ │ ├── module.json │ │ └── test.zig │ └── thread.zig └── tools ├── OVMF_CODE-pure-efi.fd ├── format_loopback_fat32.sh ├── loopback_end.sh ├── loopback_mount.sh └── loopback_start.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | # Default behavior, if core.autocrlf is unset. 2 | * text=auto 3 | 4 | # Files to be converted to native line endings on checkout. 5 | *.cpp text 6 | *.h text 7 | 8 | # Text files to always have LF (unix) line endings on checkout. 9 | *.zig text eol=lf 10 | 11 | tools/OVMF_CODE-pure-efi.fd binary 12 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [davidgm94] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | zig-cache/* 2 | zig-out/* 3 | *.png 4 | logfile 5 | debug_disk 6 | .gdb_history 7 | bochsout.txt 8 | *.hdd 9 | loopback_device 10 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, David Gonzalez Martin 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Moved to Birth](https://github.com/birth-software/birth) 2 | -------------------------------------------------------------------------------- /bochsrc: -------------------------------------------------------------------------------- 1 | cpu: count=2, reset_on_triple_fault=0 2 | 3 | display_library: x, options="gui_debug" 4 | 5 | megs: 512 6 | 7 | clock: sync=realtime, time0=local 8 | 9 | ata0-master: type=disk, path="zig-cache/rise_Debug_x86_64_rise_bios_normal_exe.hdd", mode=flat 10 | 11 | boot: c 12 | 13 | log: ./bochsout.txt 14 | 15 | mouse: enabled=0 16 | 17 | magic_break: enabled=1 18 | -------------------------------------------------------------------------------- /config/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "architecture": "x86_64", 3 | "bootloader": "rise", 4 | "boot_protocol": "bios", 5 | "execution_environment": "qemu", 6 | "optimize_mode": "Debug", 7 | "execution_type": "emulated", 8 | "executable_kind": "exe" 9 | } 10 | -------------------------------------------------------------------------------- /config/files: -------------------------------------------------------------------------------- 1 | .{ .host_path = "zig-cache", .host_base = "cpu_driver", .suffix_type = "arch", .guest = "/cpudriv.er", .type = "cpu_driver", }, 2 | .{ .host_path = "resources", .host_base = "zap-light16.psf", .suffix_type = "none", .guest = "/font.psf", .type = "font", }, 3 | .{ .host_path = "zig-cache", .host_base = "init", .suffix_type = "arch", .guest = "/init", .type = "init", }, 4 | -------------------------------------------------------------------------------- /config/gdb_script: -------------------------------------------------------------------------------- 1 | set can-use-hw-watchpoints 1 2 | -------------------------------------------------------------------------------- /config/image_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "sector_count": 131072, 3 | "sector_size": 512, 4 | "image_name": "rise", 5 | "partition_table": "gpt", 6 | "partition": { 7 | "name": "ESP", 8 | "filesystem": "fat32", 9 | "first_lba": 2048 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /config/qemu.json: -------------------------------------------------------------------------------- 1 | { 2 | "memory": { 3 | "amount": "2048", 4 | "unit": "megabyte" 5 | }, 6 | "virtualize": false, 7 | "vga": "std", 8 | "smp": 2, 9 | "debugcon": "stdio", 10 | "log": { 11 | "file": null, 12 | "guest_errors": true, 13 | "assembly": false, 14 | "interrupts": true 15 | }, 16 | "trace": [ 17 | "nvme", 18 | "pci", 19 | "ide", 20 | "ata", 21 | "ahci", 22 | "sata", 23 | "apic_report_irq_delivered", 24 | "apic_reset_irq_delivered", 25 | "apic_get_irq_delivered", 26 | "apic_local_deliver", 27 | "apic_deliver_irq", 28 | "apic_mem_readl", 29 | "apic_mem_writel" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /resources/FiraSans-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidgmbb/birth/a8d3984edb43e95696dc0361429e518524462f70/resources/FiraSans-Regular.otf -------------------------------------------------------------------------------- /resources/zap-light16.psf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidgmbb/birth/a8d3984edb43e95696dc0361429e518524462f70/resources/zap-light16.psf -------------------------------------------------------------------------------- /src/bootloader/arch.zig: -------------------------------------------------------------------------------- 1 | pub const x86 = @import("arch/x86.zig"); 2 | pub const x86_64 = @import("arch/x86_64.zig"); 3 | -------------------------------------------------------------------------------- /src/bootloader/arch/x86.zig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidgmbb/birth/a8d3984edb43e95696dc0361429e518524462f70/src/bootloader/arch/x86.zig -------------------------------------------------------------------------------- /src/bootloader/arch/x86/64/smp_trampoline.S: -------------------------------------------------------------------------------- 1 | .section .smp_trampoline 2 | .align 0x1000 3 | 4 | .global smp_trampoline 5 | .global smp_trampoline_arg_start 6 | .global smp_trampoline_arg_end 7 | .global smp_gdt_descriptor 8 | .global smp_core_booted 9 | .global smp_trampoline_end 10 | 11 | .code16 12 | smp_trampoline: 13 | cli 14 | cld 15 | 16 | mov %cs, %ebx 17 | shl $0x4, %ebx 18 | 19 | lidtl %cs:(invalid_idt - smp_trampoline) 20 | lgdtl %cs:(smp_gdt_descriptor - smp_trampoline) 21 | leal (protected_mode - smp_trampoline)(%ebx), %eax 22 | movl %eax, %cs:(far_jump_offset - smp_trampoline) 23 | movl $0x11, %eax 24 | movl %eax, %cr0 25 | mov %cs:(gdt32_ds - smp_trampoline), %eax 26 | ljmpl *%cs:(far_jump - smp_trampoline) 27 | 28 | far_jump: 29 | far_jump_offset: .long 0 30 | gdt32_cs: .long 0x18 31 | gdt32_ds: .long 0x20 32 | 33 | .code32 34 | protected_mode: 35 | movw %ax, %ds 36 | movw %ax, %es 37 | movw %ax, %fs 38 | movw %ax, %gs 39 | movw %ax, %ss 40 | xorl %eax, %eax 41 | lldtw %ax 42 | xorl %eax, %eax 43 | movl %eax, %cr4 44 | 45 | // TODO: Change 46 | // always no x2apic 47 | leal (temporal_stack_top - smp_trampoline)(%ebx), %esp 48 | 49 | // Long mode activation 50 | 51 | // In CR4 52 | mov %cr4, %eax 53 | bts $0x5, %eax 54 | mov %eax, %cr4 55 | 56 | // In EFER: 57 | mov $0xc0000080, %ecx 58 | mov $0x900, %eax 59 | xor %edx, %edx 60 | wrmsr 61 | 62 | // Setup CR3 63 | mov (arg_cr3 - smp_trampoline)(%ebx), %eax 64 | mov %eax, %cr3 65 | 66 | mov %cr0, %eax 67 | bts $31, %eax 68 | mov %eax, %cr0 69 | 70 | leal (bits64 - smp_trampoline)(%ebx), %eax 71 | push $0x28 72 | push %eax 73 | lretl 74 | 75 | .code64 76 | bits64: 77 | mov $0x30, %rax 78 | mov %rax, %ds 79 | mov %rax, %es 80 | mov %rax, %fs 81 | mov %rax, %gs 82 | mov %rax, %ss 83 | 84 | mov %ebx, %ebx 85 | 86 | // Enable NXE 87 | mov $0xc0000080, %ecx 88 | rdmsr 89 | bts $11, %eax 90 | wrmsr 91 | 92 | // Enable write protect 93 | mov %cr0, %rax 94 | bts $16, %rax 95 | mov %rax, %cr0 96 | 97 | // TODO: before park 98 | mov $1, %al 99 | lock xchgb (smp_core_booted - smp_trampoline)(%rbx), %al 100 | xor %rax, %rax 101 | cli 102 | hlt 103 | 104 | .align 16 105 | temporal_stack: 106 | .fill 128, 1, 0 107 | temporal_stack_top: 108 | 109 | invalid_idt: 110 | .quad 0 111 | .quad 0 112 | 113 | .align 16 114 | smp_trampoline_arg_start: 115 | arg_hhdm: 116 | .quad 0 117 | arg_cr3: 118 | .long 0 119 | reserved: .word 0 120 | smp_gdt_descriptor: 121 | .limit: .word 0 122 | .address: .quad 0 123 | smp_gdt: 124 | smp_gdt_null: .quad 0 125 | smp_gdt_code_16: .quad 0 126 | smp_gdt_data_16: .quad 0 127 | smp_gdt_code_32: .quad 0 128 | smp_gdt_data_32: .quad 0 129 | smp_gdt_code_64: .quad 0 130 | smp_gdt_data_64: .quad 0 131 | smp_trampoline_arg_end: 132 | 133 | smp_core_booted: .byte 0 134 | 135 | smp_trampoline_end: 136 | -------------------------------------------------------------------------------- /src/bootloader/arch/x86_64.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const assert = lib.assert; 3 | const bootloader = @import("bootloader"); 4 | const privileged = @import("privileged"); 5 | const paging = privileged.arch.paging; 6 | const x86_64 = privileged.arch.x86_64; 7 | 8 | pub const GDT = extern struct { 9 | null_entry: Entry = Entry.null_entry, 10 | // 0x08 11 | code_16: Entry = Entry.code_16, 12 | // 0x10 13 | data_16: Entry = Entry.data_16, 14 | // 0x18 15 | code_32: Entry = Entry.code_32, 16 | // 0x20 17 | data_32: Entry = Entry.data_32, 18 | // 0x28 19 | code_64: Entry = Entry.code_64, 20 | // 0x30 21 | data_64: Entry = Entry.data_64, 22 | 23 | pub const Entry = x86_64.GDT.Entry; 24 | pub const Descriptor = x86_64.GDT.Descriptor; 25 | 26 | pub fn getDescriptor(gdt: *const GDT) GDT.Descriptor { 27 | return .{ 28 | .limit = @sizeOf(GDT) - 1, 29 | .address = @ptrToInt(gdt), 30 | }; 31 | } 32 | }; 33 | 34 | const code_segment_selector = @offsetOf(GDT, "code_64"); 35 | const data_segment_selector = @offsetOf(GDT, "data_64"); 36 | const entry_point_offset = @offsetOf(bootloader.Information, "entry_point"); 37 | const higher_half_offset = @offsetOf(bootloader.Information, "higher_half"); 38 | 39 | pub fn jumpToKernel(bootloader_information_arg: *bootloader.Information, minimal_paging: paging.Specific) noreturn { 40 | if (@ptrToInt(bootloader_information_arg) >= lib.config.cpu_driver_higher_half_address) { 41 | // Error 42 | privileged.arch.stopCPU(); 43 | } 44 | 45 | // Enable long mode and certain important bits 46 | var efer = privileged.arch.x86_64.registers.IA32_EFER.read(); 47 | efer.LME = true; 48 | efer.NXE = true; 49 | efer.SCE = true; 50 | efer.write(); 51 | 52 | minimal_paging.cr3.write(); 53 | 54 | if (lib.cpu.arch == .x86) { 55 | // Enable PAE 56 | var cr4 = asm volatile ( 57 | \\mov %cr4, %[cr4] 58 | : [cr4] "=r" (-> u32), 59 | : 60 | : "memory" 61 | ); 62 | cr4 |= (1 << 5); 63 | asm volatile ( 64 | \\mov %[cr4], %cr4 65 | : 66 | : [cr4] "r" (cr4), 67 | : "memory" 68 | ); 69 | 70 | // Enable paging 71 | var cr0 = asm volatile ( 72 | \\mov %cr0, %[cr0] 73 | : [cr0] "=r" (-> u32), 74 | : 75 | : "memory" 76 | ); 77 | cr0 |= (1 << 31); 78 | asm volatile ( 79 | \\mov %[cr0], %cr0 80 | : 81 | : [cr0] "r" (cr0), 82 | : "memory" 83 | ); 84 | 85 | asm volatile ( 86 | \\jmp %[code_segment_selector], $bits64 87 | \\.code64 88 | \\bits64: 89 | \\mov %[data_segment_selector], %ds 90 | \\mov %[data_segment_selector], %es 91 | \\mov %[data_segment_selector], %fs 92 | \\mov %[data_segment_selector], %gs 93 | \\mov %[data_segment_selector], %ss 94 | : 95 | : [code_segment_selector] "i" (code_segment_selector), 96 | [data_segment_selector] "r" (data_segment_selector), 97 | : "memory" 98 | ); 99 | } 100 | 101 | switch (lib.cpu.arch) { 102 | .x86_64 => { 103 | const bootloader_information = @intToPtr(*bootloader.Information, @ptrToInt(bootloader_information_arg) + lib.config.cpu_driver_higher_half_address); 104 | const entry_point = bootloader_information.entry_point; 105 | asm volatile ( 106 | \\.code64 107 | \\jmp *%[entry_point] 108 | \\cli 109 | \\hlt 110 | : 111 | : [entry_point] "r" (entry_point), 112 | [bootloader_information] "{rdi}" (bootloader_information), 113 | : "memory" 114 | ); 115 | }, 116 | .x86 => asm volatile ( 117 | \\mov %edi, %eax 118 | \\add %[higher_half_offset], %eax 119 | \\.byte 0x48 120 | \\add (%eax), %edi 121 | \\.byte 0x48 122 | \\mov %edi, %eax 123 | \\.byte 0x48 124 | \\mov %edi, %eax 125 | \\add %[entry_point_offset], %eax 126 | \\.byte 0x48 127 | \\mov (%eax), %eax 128 | \\jmp *%eax 129 | \\cli 130 | \\hlt 131 | : 132 | : [bootloader_information] "{edi}" (bootloader_information_arg), 133 | [higher_half_offset] "i" (higher_half_offset), 134 | [slice_offset] "i" (@offsetOf(bootloader.Information.Slice, "offset")), 135 | [slice_size_slide] "i" (@offsetOf(bootloader.Information.Slice, "size") - @offsetOf(bootloader.Information.Slice, "offset")), 136 | [entry_point_offset] "i" (entry_point_offset), 137 | : "memory" 138 | ), 139 | else => @compileError("Architecture not supported"), 140 | } 141 | 142 | unreachable; 143 | } 144 | 145 | pub inline fn delay(cycles: u64) void { 146 | const next_stop = lib.arch.x86_64.readTimestamp() + cycles; 147 | while (lib.arch.x86_64.readTimestamp() < next_stop) {} 148 | } 149 | 150 | pub extern fn smp_trampoline() align(0x1000) callconv(.Naked) noreturn; 151 | -------------------------------------------------------------------------------- /src/bootloader/limine.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const PhysicalMemoryRegion = lib.PhysicalMemoryRegion; 3 | 4 | const ID = [4]u64; 5 | 6 | fn requestID(c: u64, d: u64) ID { 7 | return .{ 0xc7b1dd30df4c8b88, 0x0a82e883a194f07b, c, d }; 8 | } 9 | 10 | pub const UUID = extern struct { 11 | a: u32, 12 | b: u16, 13 | c: u16, 14 | d: [8]u8, 15 | }; 16 | 17 | pub const File = extern struct { 18 | revision: u64, 19 | address: u64, 20 | size: u64, 21 | path: [*:0]const u8, 22 | command_line: [*:0]const u8, 23 | media_type: MediaType, 24 | unused: u32, 25 | tftp_ip: u32, 26 | tftp_port: u32, 27 | partition_index: u32, 28 | mbr_disk_id: u32, 29 | gpt_disk_uuid: UUID, 30 | gpt_part_uuid: UUID, 31 | part_uuid: UUID, 32 | 33 | pub const MediaType = enum(u32) { 34 | generic = 0, 35 | optical = 1, 36 | tftp = 2, 37 | }; 38 | 39 | pub inline fn getPath(file: *const File) []const u8 { 40 | const path = file.path[0..lib.length(file.path)]; 41 | return path; 42 | } 43 | 44 | pub inline fn getContent(file: *const File) []const u8 { 45 | const content = @intToPtr([*]const u8, file.address)[0..file.size]; 46 | return content; 47 | } 48 | }; 49 | 50 | pub const BootloaderInfo = extern struct { 51 | pub const Request = extern struct { 52 | id: ID = requestID(0xf55038d8e2a1202f, 0x279426fcf5f59740), 53 | revision: u64, 54 | response: ?*const Response = null, 55 | }; 56 | 57 | pub const Response = extern struct { 58 | revision: u64, 59 | name: [*:0]const u8, 60 | version: [*:0]const u8, 61 | }; 62 | }; 63 | 64 | pub const StackSize = extern struct { 65 | pub const Request = extern struct { 66 | id: ID = requestID(0x224ef0460a8e8926, 0xe1cb0fc25f46ea3d), 67 | revision: u64, 68 | response: ?*const Response = null, 69 | stack_size: u64, 70 | }; 71 | 72 | pub const Response = extern struct { 73 | revision: u64, 74 | }; 75 | }; 76 | 77 | pub const HHDM = extern struct { 78 | pub const Request = extern struct { 79 | id: ID = requestID(0x48dcf1cb8ad2b852, 0x63984e959a98244b), 80 | revision: u64, 81 | response: ?*const Response = null, 82 | }; 83 | 84 | pub const Response = extern struct { 85 | revision: u64, 86 | offset: u64, 87 | }; 88 | }; 89 | 90 | pub const VideoMode = extern struct { 91 | pitch: u64, 92 | width: u64, 93 | height: u64, 94 | bpp: u16, 95 | memory_model: u8, 96 | red_mask_size: u8, 97 | red_mask_shift: u8, 98 | green_mask_size: u8, 99 | green_mask_shift: u8, 100 | blue_mask_size: u8, 101 | blue_mask_shift: u8, 102 | }; 103 | 104 | pub const Framebuffer = extern struct { 105 | address: u64, 106 | width: u64, 107 | height: u64, 108 | pitch: u64, 109 | bpp: u16, 110 | memory_model: u8, 111 | red_mask_size: u8, 112 | red_mask_shift: u8, 113 | green_mask_size: u8, 114 | green_mask_shift: u8, 115 | blue_mask_size: u8, 116 | blue_mask_shift: u8, 117 | unused: [7]u8, 118 | edid_size: u64, 119 | edid: u64, 120 | mode_count: u64, 121 | modes: [*]const *const VideoMode, 122 | 123 | pub const Request = extern struct { 124 | id: ID = requestID(0x9d5827dcd881dd75, 0xa3148604f6fab11b), 125 | revision: u64, 126 | response: ?*const Response = null, 127 | }; 128 | 129 | pub const Response = extern struct { 130 | revision: u64, 131 | framebuffer_count: u64, 132 | framebuffers: *const [*]const Framebuffer, 133 | }; 134 | }; 135 | 136 | pub const Terminal = extern struct { 137 | columns: u64, 138 | rows: u64, 139 | framebuffer: ?*Framebuffer, 140 | 141 | pub const Request = extern struct { 142 | id: ID = requestID(0xc8ac59310c2b0844, 0xa68d0c7265d38878), 143 | revision: u64, 144 | response: ?*const Response = null, 145 | callback: ?*const Callback, 146 | }; 147 | 148 | pub const Response = extern struct { 149 | revision: u64, 150 | terminal_count: u64, 151 | terminals: ?*const [*]Terminal, 152 | write: ?*const Write, 153 | }; 154 | 155 | pub const Write = fn (*Terminal, [*:0]const u8, u64) callconv(.C) void; 156 | pub const Callback = fn (*Terminal, u64, u64, u64, u64) callconv(.C) void; 157 | }; 158 | 159 | pub const Paging5Level = extern struct { 160 | pub const Request = extern struct { 161 | id: ID = requestID(0x94469551da9b3192, 0xebe5e86db7382888), 162 | revision: u64, 163 | response: ?*const Response = null, 164 | }; 165 | 166 | pub const Response = extern struct { 167 | revision: u64, 168 | }; 169 | }; 170 | 171 | const SMPInfoGoToAddress = fn (*SMPInfo) callconv(.C) noreturn; 172 | 173 | pub const SMPInfoRequest = extern struct { 174 | id: ID = requestID(0x95a67b819a1b857e, 0xa0b61b723b6a73e0), 175 | revision: u64, 176 | response: ?*const SMPInfo.Response = null, 177 | flags: packed struct(u64) { 178 | x2apic: bool, 179 | reserved: u63 = 0, 180 | }, 181 | }; 182 | 183 | pub const SMPInfo = switch (@import("builtin").cpu.arch) { 184 | .x86_64 => extern struct { 185 | processor_id: u32, 186 | lapic_id: u32, 187 | reserved: u64, 188 | goto_address: ?*const SMPInfoGoToAddress, 189 | extra_argument: u64, 190 | 191 | pub const Response = extern struct { 192 | revision: u64, 193 | flags: u32, 194 | bsp_lapic_id: u32, 195 | cpu_count: u64, 196 | cpus: ?*const [*]SMPInfo, 197 | }; 198 | }, 199 | .aarch64 => extern struct { 200 | processor_id: u32, 201 | gic_iface_no: u32, 202 | mpidr: u64, 203 | reserved: u64, 204 | goto_address: ?*const SMPInfoGoToAddress, 205 | extra_argument: u64, 206 | 207 | pub const Request = SMPInfoRequest; 208 | 209 | pub const Response = extern struct { 210 | revision: u64, 211 | flags: u32, 212 | bsp_mpidr: u64, 213 | cpu_count: u64, 214 | cpus: ?*const [*]const SMPInfo, 215 | }; 216 | }, 217 | else => @compileError("Architecture not supported"), 218 | }; 219 | 220 | pub const MemoryMap = extern struct { 221 | pub const Entry = extern struct { 222 | region: PhysicalMemoryRegion, 223 | type: Type, 224 | 225 | const Type = enum(u64) { 226 | usable = 0, 227 | reserved = 1, 228 | acpi_reclaimable = 2, 229 | acpi_nvs = 3, 230 | bad_memory = 4, 231 | bootloader_reclaimable = 5, 232 | kernel_and_modules = 6, 233 | framebuffer = 7, 234 | }; 235 | }; 236 | 237 | pub const Request = extern struct { 238 | id: ID = requestID(0x67cf3d9d378a806f, 0xe304acdfc50c3c62), 239 | revision: u64, 240 | response: ?*const Response = null, 241 | }; 242 | 243 | pub const Response = extern struct { 244 | revision: u64, 245 | entry_count: u64, 246 | entries: *const [*]const Entry, 247 | }; 248 | }; 249 | 250 | pub const EntryPoint = extern struct { 251 | pub const Function = fn () callconv(.C) noreturn; 252 | 253 | pub const Request = extern struct { 254 | id: ID = requestID(0x13d86c035a1cd3e1, 0x2b0caa89d8f3026a), 255 | revision: u64, 256 | response: ?*const Response = null, 257 | entry_point: *const Function, 258 | }; 259 | 260 | pub const Response = extern struct { 261 | revision: u64, 262 | }; 263 | }; 264 | 265 | pub const KernelFile = extern struct { 266 | pub const Request = extern struct { 267 | id: ID = requestID(0xad97e90e83f1ed67, 0x31eb5d1c5ff23b69), 268 | revision: u64, 269 | response: ?*const Response = null, 270 | }; 271 | 272 | pub const Response = extern struct { 273 | revision: u64, 274 | file: ?*const File, 275 | }; 276 | }; 277 | 278 | pub const Module = extern struct { 279 | pub const Request = extern struct { 280 | id: ID = requestID(0x3e7e279702be32af, 0xca1c4f3bd1280cee), 281 | revision: u64, 282 | response: ?*const Response = null, 283 | }; 284 | 285 | pub const Response = extern struct { 286 | revision: u64, 287 | module_count: u64, 288 | modules: *const [*]const File, 289 | }; 290 | }; 291 | 292 | pub const RSDP = extern struct { 293 | pub const Request = extern struct { 294 | id: ID = requestID(0xc5e77b6b397e7b43, 0x27637845accdcf3c), 295 | revision: u64, 296 | response: ?*const Response = null, 297 | }; 298 | 299 | pub const Response = extern struct { 300 | revision: u64, 301 | address: u64, 302 | }; 303 | }; 304 | 305 | pub const SMBIOS = extern struct { 306 | pub const Request = extern struct { 307 | id: ID = requestID(0x9e9046f11e095391, 0xaa4a520fefbde5ee), 308 | revision: u64, 309 | response: ?*const Response = null, 310 | }; 311 | 312 | pub const Response = extern struct { 313 | revision: u64, 314 | entry_32: u64, 315 | entry_64: u64, 316 | }; 317 | }; 318 | 319 | pub const EFISystemTable = extern struct { 320 | pub const Request = extern struct { 321 | id: ID = requestID(0x5ceba5163eaaf6d6, 0x0a6981610cf65fcc), 322 | revision: u64, 323 | response: ?*const Response = null, 324 | }; 325 | 326 | pub const Response = extern struct { 327 | revision: u64, 328 | address: u64, 329 | }; 330 | }; 331 | 332 | pub const BootTime = extern struct { 333 | pub const Request = extern struct { 334 | id: ID = requestID(0x502746e184c088aa, 0xfbc5ec83e6327893), 335 | revision: u64, 336 | response: ?*const Response = null, 337 | }; 338 | 339 | pub const Response = extern struct { 340 | revision: u64, 341 | boot_time: i64, 342 | }; 343 | }; 344 | 345 | pub const KernelAddress = extern struct { 346 | pub const Request = extern struct { 347 | id: ID = requestID(0x71ba76863cc55f63, 0xb2644a48c516a487), 348 | revision: u64, 349 | response: ?*const Response = null, 350 | }; 351 | 352 | pub const Response = extern struct { 353 | revision: u64, 354 | physical_address: u64, 355 | virtual_address: u64, 356 | }; 357 | }; 358 | 359 | pub const DTB = extern struct { 360 | pub const Request = extern struct { 361 | id: ID = requestID(0xb40ddb48fb54bac7, 0x545081493f81ffb7), 362 | revision: u64, 363 | response: ?*const Response = null, 364 | }; 365 | pub const Response = extern struct { 366 | revision: u64, 367 | address: u64, 368 | }; 369 | }; 370 | -------------------------------------------------------------------------------- /src/bootloader/limine/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2019, 2020, 2021, 2022 mintsuki and contributors. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /src/bootloader/limine/arch/x86_64/linker_script.ld: -------------------------------------------------------------------------------- 1 | PHDRS { 2 | none PT_NULL FLAGS(0); 3 | text PT_LOAD FLAGS((1 << 2) | (1 << 0) /* Readable | Executable */); 4 | rodata PT_LOAD FLAGS((1 << 2) /* Readable */); 5 | data PT_LOAD FLAGS((1 << 2) | (1 << 1) /* Readable | Writeable */); 6 | } 7 | 8 | SECTIONS { 9 | /* Start here so there is no conflict with the CPU driver */ 10 | . = 0xFFFFFFFFF0000000; 11 | 12 | PROVIDE(text_section_start = .); 13 | .text . : { 14 | *(.text*) 15 | }:text 16 | 17 | . = ALIGN(4K); 18 | PROVIDE(text_section_end = .); 19 | 20 | PROVIDE(rodata_section_start = .); 21 | .rodata . : { 22 | *(.rodata*) 23 | }:rodata 24 | 25 | . = ALIGN(4K); 26 | PROVIDE(rodata_section_end = .); 27 | 28 | PROVIDE(data_section_start = .); 29 | .data . : { 30 | *(.data*) 31 | *(.bss*) 32 | *(.got*) 33 | }:data 34 | 35 | . = ALIGN(4K); 36 | PROVIDE(data_section_end = .); 37 | } 38 | -------------------------------------------------------------------------------- /src/bootloader/limine/installables/BOOTAA64.EFI: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidgmbb/birth/a8d3984edb43e95696dc0361429e518524462f70/src/bootloader/limine/installables/BOOTAA64.EFI -------------------------------------------------------------------------------- /src/bootloader/limine/installables/BOOTIA32.EFI: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidgmbb/birth/a8d3984edb43e95696dc0361429e518524462f70/src/bootloader/limine/installables/BOOTIA32.EFI -------------------------------------------------------------------------------- /src/bootloader/limine/installables/BOOTX64.EFI: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidgmbb/birth/a8d3984edb43e95696dc0361429e518524462f70/src/bootloader/limine/installables/BOOTX64.EFI -------------------------------------------------------------------------------- /src/bootloader/limine/installables/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (C) 2019-2023 mintsuki and contributors. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /src/bootloader/limine/installables/Makefile: -------------------------------------------------------------------------------- 1 | CC ?= cc 2 | INSTALL ?= ./install-sh 3 | 4 | PREFIX ?= /usr/local 5 | 6 | CFLAGS ?= -g -O2 -pipe -Wall -Wextra 7 | 8 | .PHONY: all 9 | all: limine-deploy limine-version 10 | 11 | .PHONY: install-data 12 | install-data: all 13 | $(INSTALL) -d '$(DESTDIR)$(PREFIX)/share' 14 | $(INSTALL) -d '$(DESTDIR)$(PREFIX)/share/limine' 15 | $(INSTALL) -m 644 limine.sys '$(DESTDIR)$(PREFIX)/share/limine/' 16 | $(INSTALL) -m 644 limine-cd.bin '$(DESTDIR)$(PREFIX)/share/limine/' 17 | $(INSTALL) -m 644 limine-cd-efi.bin '$(DESTDIR)$(PREFIX)/share/limine/' 18 | $(INSTALL) -m 644 limine-pxe.bin '$(DESTDIR)$(PREFIX)/share/limine/' 19 | $(INSTALL) -m 644 BOOTX64.EFI '$(DESTDIR)$(PREFIX)/share/limine/' 20 | $(INSTALL) -m 644 BOOTIA32.EFI '$(DESTDIR)$(PREFIX)/share/limine/' 21 | $(INSTALL) -d '$(DESTDIR)$(PREFIX)/include' 22 | $(INSTALL) -m 644 limine.h '$(DESTDIR)$(PREFIX)/include/' 23 | 24 | .PHONY: install 25 | install: install-data 26 | $(INSTALL) -d '$(DESTDIR)$(PREFIX)/bin' 27 | $(INSTALL) limine-deploy '$(DESTDIR)$(PREFIX)/bin/' 28 | $(INSTALL) limine-version '$(DESTDIR)$(PREFIX)/bin/' 29 | 30 | .PHONY: install-strip 31 | install-strip: install-data 32 | $(INSTALL) -d '$(DESTDIR)$(PREFIX)/bin' 33 | $(INSTALL) -s limine-deploy '$(DESTDIR)$(PREFIX)/bin/' 34 | $(INSTALL) -s limine-version '$(DESTDIR)$(PREFIX)/bin/' 35 | 36 | .PHONY: clean 37 | clean: 38 | rm -f limine-deploy limine-deploy.exe 39 | rm -f limine-version limine-version.exe 40 | 41 | limine-deploy: limine-deploy.c limine-hdd.h 42 | $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -std=c99 -D__USE_MINGW_ANSI_STDIO limine-deploy.c $(LIBS) -o $@ 43 | 44 | limine-version: limine-version.c 45 | $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -std=c99 -D__USE_MINGW_ANSI_STDIO limine-version.c $(LIBS) -o $@ 46 | -------------------------------------------------------------------------------- /src/bootloader/limine/installables/limine-cd-efi.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidgmbb/birth/a8d3984edb43e95696dc0361429e518524462f70/src/bootloader/limine/installables/limine-cd-efi.bin -------------------------------------------------------------------------------- /src/bootloader/limine/installables/limine-cd.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidgmbb/birth/a8d3984edb43e95696dc0361429e518524462f70/src/bootloader/limine/installables/limine-cd.bin -------------------------------------------------------------------------------- /src/bootloader/limine/installables/limine-pxe.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidgmbb/birth/a8d3984edb43e95696dc0361429e518524462f70/src/bootloader/limine/installables/limine-pxe.bin -------------------------------------------------------------------------------- /src/bootloader/limine/installables/limine-version.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define LIMINE_VERSION "4.20230120.0" 4 | 5 | int main(void) { 6 | puts(LIMINE_VERSION); 7 | } 8 | -------------------------------------------------------------------------------- /src/bootloader/limine/installables/limine.cfg: -------------------------------------------------------------------------------- 1 | # Timeout in seconds that Limine will use before automatically booting. 2 | TIMEOUT=0 3 | 4 | # The entry name that will be displayed in the boot menu 5 | :Rise 6 | 7 | # Change the protocol line depending on the used protocol. 8 | PROTOCOL=limine 9 | 10 | # Path to the kernel to boot. boot:/// represents the partition on which limine.cfg is located. 11 | KERNEL_PATH=boot:///CPUDRIV 12 | 13 | DEFAULT_ENTRY=0 14 | -------------------------------------------------------------------------------- /src/bootloader/limine/installables/limine.sys: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidgmbb/birth/a8d3984edb43e95696dc0361429e518524462f70/src/bootloader/limine/installables/limine.sys -------------------------------------------------------------------------------- /src/bootloader/limine/test.zig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidgmbb/birth/a8d3984edb43e95696dc0361429e518524462f70/src/bootloader/limine/test.zig -------------------------------------------------------------------------------- /src/bootloader/rise/bios/linker_script.ld: -------------------------------------------------------------------------------- 1 | PHDRS { 2 | none PT_NULL FLAGS(0); 3 | text PT_LOAD FLAGS((1 << 2) | (1 << 0) /* Readable | Executable */); 4 | rodata PT_LOAD FLAGS((1 << 2) /* Readable */); 5 | data PT_LOAD FLAGS((1 << 2) | (1 << 1) /* Readable | Writeable */); 6 | } 7 | 8 | SECTIONS { 9 | . = 0x1000; 10 | loader_start = .; 11 | .text . : { 12 | *(.smp_trampoline*) 13 | *(.realmode*) 14 | *(.text*) 15 | }:text 16 | . = ALIGN(0x10); 17 | .rodata . : { 18 | *(.rodata*) 19 | }:rodata 20 | . = ALIGN(0x10); 21 | .data . : { 22 | *(.data*) 23 | *(.bss*) 24 | }:data 25 | . = ALIGN(0x10); 26 | loader_end = .; 27 | } 28 | -------------------------------------------------------------------------------- /src/bootloader/rise/bios/main.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const Allocator = lib.Allocator; 3 | const assert = lib.assert; 4 | const log = lib.log; 5 | const privileged = @import("privileged"); 6 | const ACPI = privileged.ACPI; 7 | const MemoryManager = privileged.MemoryManager; 8 | const PhysicalHeap = privileged.PhyicalHeap; 9 | const writer = privileged.writer; 10 | 11 | const stopCPU = privileged.arch.stopCPU; 12 | const GDT = privileged.arch.x86_64.GDT; 13 | const Mapping = privileged.Mapping; 14 | const PageAllocator = privileged.PageAllocator; 15 | const PhysicalAddress = lib.PhysicalAddress; 16 | const VirtualAddress = lib.VirtualAddress; 17 | const PhysicalMemoryRegion = lib.PhysicalMemoryRegion; 18 | const VirtualMemoryRegion = lib.VirtualMemoryRegion; 19 | 20 | const bootloader = @import("bootloader"); 21 | const bios = @import("bios"); 22 | 23 | extern const loader_start: u8; 24 | extern const loader_end: u8; 25 | 26 | const FATAllocator = extern struct { 27 | buffer: [0x2000]u8 = undefined, 28 | allocated: usize = 0, 29 | allocator: Allocator = .{ 30 | .callbacks = .{ 31 | .allocate = allocate, 32 | }, 33 | }, 34 | 35 | pub fn allocate(allocator: *Allocator, size: u64, alignment: u64) Allocator.Allocate.Error!Allocator.Allocate.Result { 36 | const fat = @fieldParentPtr(FATAllocator, "allocator", allocator); 37 | const aligned_allocated = lib.alignForward(usize, fat.allocated, @intCast(usize, alignment)); 38 | if (aligned_allocated + size > fat.buffer.len) @panic("no alloc"); 39 | fat.allocated = aligned_allocated; 40 | const result = Allocator.Allocate.Result{ 41 | .address = @ptrToInt(&fat.buffer) + fat.allocated, 42 | .size = size, 43 | }; 44 | fat.allocated += @intCast(usize, size); 45 | return result; 46 | } 47 | }; 48 | 49 | pub const std_options = struct { 50 | pub const log_level = lib.std.log.Level.debug; 51 | 52 | pub fn logFn(comptime level: lib.std.log.Level, comptime scope: @TypeOf(.EnumLiteral), comptime format: []const u8, args: anytype) void { 53 | _ = args; 54 | _ = format; 55 | _ = scope; 56 | _ = level; 57 | // _ = level; 58 | // writer.writeByte('[') catch stopCPU(); 59 | // writer.writeAll(@tagName(scope)) catch stopCPU(); 60 | // writer.writeAll("] ") catch stopCPU(); 61 | // lib.format(writer, format, args) catch stopCPU(); 62 | // writer.writeByte('\n') catch stopCPU(); 63 | } 64 | }; 65 | 66 | pub fn panic(message: []const u8, _: ?*lib.StackTrace, _: ?usize) noreturn { 67 | privileged.arch.disableInterrupts(); 68 | writer.writeAll("[PANIC] ") catch stopCPU(); 69 | writer.writeAll(message) catch stopCPU(); 70 | writer.writeByte('\n') catch stopCPU(); 71 | 72 | if (lib.is_test) { 73 | privileged.exitFromQEMU(.failure); 74 | } else { 75 | privileged.arch.stopCPU(); 76 | } 77 | } 78 | 79 | const Filesystem = extern struct { 80 | fat_allocator: FATAllocator = .{}, 81 | fat_cache: lib.Filesystem.FAT32.Cache, 82 | disk: bios.Disk = .{}, 83 | cache_index: usize = 0, 84 | 85 | pub fn deinitialize(filesystem: *Filesystem) !void { 86 | filesystem.fat_allocator.allocated = filesystem.cache_index; 87 | } 88 | 89 | pub fn readFile(filesystem: *Filesystem, file_path: []const u8, file_buffer: []u8) ![]const u8 { 90 | log.debug("File {s} read started", .{file_path}); 91 | assert(filesystem.fat_allocator.allocated <= filesystem.fat_allocator.buffer.len); 92 | const file = try filesystem.fat_cache.readFileToBuffer(file_path, file_buffer); 93 | log.debug("File read succeeded", .{}); 94 | return file; 95 | } 96 | 97 | pub fn sneakFile(filesystem: *Filesystem, file_path: []const u8, size: usize) ![]const u8 { 98 | log.debug("File {s} read started", .{file_path}); 99 | const file = try filesystem.fat_cache.readFileToCache(file_path, size); 100 | log.debug("File read succeeded", .{}); 101 | return file; 102 | } 103 | 104 | pub fn getFileSize(filesystem: *Filesystem, file_path: []const u8) !u32 { 105 | const file_size = try filesystem.fat_cache.getFileSize(file_path); 106 | filesystem.fat_allocator.allocated = filesystem.cache_index; 107 | return file_size; 108 | } 109 | 110 | pub fn getSectorSize(filesystem: *Filesystem) u16 { 111 | return filesystem.disk.disk.sector_size; 112 | } 113 | }; 114 | 115 | const MemoryMap = extern struct { 116 | iterator: bios.E820Iterator, 117 | entry_count: u32, 118 | 119 | pub fn getEntryCount(memory_map: *const MemoryMap) u32 { 120 | return memory_map.entry_count; 121 | } 122 | 123 | pub fn next(memory_map: *MemoryMap) !?bootloader.MemoryMapEntry { 124 | if (memory_map.iterator.next()) |bios_entry| { 125 | return .{ 126 | .region = bios_entry.toPhysicalMemoryRegion(), 127 | .type = switch (bios_entry.type) { 128 | .usable => if (bios_entry.isUsable()) .usable else .reserved, 129 | .bad_memory => .bad_memory, 130 | else => .reserved, 131 | }, 132 | }; 133 | } 134 | 135 | return null; 136 | } 137 | }; 138 | 139 | const Initialization = struct { 140 | filesystem: Filesystem, 141 | memory_map: MemoryMap, 142 | framebuffer: bootloader.Framebuffer, 143 | architecture: switch (lib.cpu.arch) { 144 | .x86, .x86_64 => struct { 145 | rsdp: u32, 146 | }, 147 | else => @compileError("Architecture not supported"), 148 | }, 149 | early_initialized: bool = false, 150 | framebuffer_initialized: bool = false, 151 | memory_map_initialized: bool = false, 152 | filesystem_initialized: bool = false, 153 | 154 | pub fn getCPUCount(init: *Initialization) !u32 { 155 | return switch (lib.cpu.arch) { 156 | .x86, .x86_64 => blk: { 157 | const rsdp = @intToPtr(*ACPI.RSDP.Descriptor1, init.architecture.rsdp); 158 | const madt_header = try rsdp.findTable(.APIC); 159 | const madt = @ptrCast(*align(1) const ACPI.MADT, madt_header); 160 | break :blk madt.getCPUCount(); 161 | }, 162 | else => @compileError("Architecture not supported"), 163 | }; 164 | } 165 | 166 | pub fn getRSDPAddress(init: *Initialization) u32 { 167 | return init.architecture.rsdp; 168 | } 169 | 170 | pub fn deinitializeMemoryMap(init: *Initialization) !void { 171 | init.memory_map.iterator = bios.E820Iterator{}; 172 | } 173 | 174 | pub fn ensureLoaderIsMapped(init: *Initialization, paging: privileged.arch.paging.Specific, page_allocator: PageAllocator, bootloader_information: *bootloader.Information) !void { 175 | _ = init; 176 | _ = bootloader_information; 177 | const loader_physical_start = PhysicalAddress.new(lib.alignBackward(usize, @ptrToInt(&loader_start), lib.arch.valid_page_sizes[0])); 178 | const loader_size = lib.alignForward(u64, @ptrToInt(&loader_end) - @ptrToInt(&loader_start) + @ptrToInt(&loader_start) - loader_physical_start.value(), lib.arch.valid_page_sizes[0]); 179 | // Not caring about safety here 180 | try paging.map(loader_physical_start, loader_physical_start.toIdentityMappedVirtualAddress(), lib.alignForward(u64, loader_size, lib.arch.valid_page_sizes[0]), .{ .write = true, .execute = true }, page_allocator); 181 | } 182 | 183 | pub fn ensureStackIsMapped(init: *Initialization, paging: privileged.arch.paging.Specific, page_allocator: PageAllocator) !void { 184 | _ = init; 185 | const loader_stack_size = bios.stack_size; 186 | const loader_stack = PhysicalAddress.new(lib.alignForward(u32, bios.stack_top, lib.arch.valid_page_sizes[0]) - loader_stack_size); 187 | try paging.map(loader_stack, loader_stack.toIdentityMappedVirtualAddress(), loader_stack_size, .{ .write = true, .execute = false }, page_allocator); 188 | } 189 | 190 | pub fn initialize(init: *Initialization) !void { 191 | // assert(!init.filesystem.initialized); 192 | // defer init.filesystem.initialized = true; 193 | init.* = .{ 194 | .filesystem = .{ 195 | .fat_cache = undefined, 196 | }, 197 | .memory_map = .{ 198 | .iterator = .{}, 199 | .entry_count = bios.getMemoryMapEntryCount(), 200 | }, 201 | .architecture = switch (lib.cpu.arch) { 202 | .x86, .x86_64 => .{ 203 | .rsdp = @ptrToInt(try bios.findRSDP()), 204 | }, 205 | else => @compileError("Architecture not supported"), 206 | }, 207 | .framebuffer = blk: { 208 | var vbe_info: bios.VBE.Information = undefined; 209 | 210 | const edid_info = bios.VBE.getEDIDInfo() catch @panic("No EDID"); 211 | const edid_width = edid_info.getWidth(); 212 | const edid_height = edid_info.getHeight(); 213 | const edid_bpp = 32; 214 | const preferred_resolution = if (edid_width != 0 and edid_height != 0) .{ .x = edid_width, .y = edid_height } else @panic("No EDID"); 215 | _ = preferred_resolution; 216 | bios.VBE.getControllerInformation(&vbe_info) catch @panic("No VBE information"); 217 | 218 | if (!lib.equal(u8, &vbe_info.signature, "VESA")) { 219 | @panic("VESA signature"); 220 | } 221 | 222 | if (vbe_info.version_major != 3 and vbe_info.version_minor != 0) { 223 | @panic("VESA version"); 224 | } 225 | 226 | const edid_video_mode = vbe_info.getVideoMode(bios.VBE.Mode.defaultIsValid, edid_width, edid_height, edid_bpp) orelse @panic("No video mode"); 227 | const framebuffer_region = PhysicalMemoryRegion.fromRaw(.{ 228 | .raw_address = edid_video_mode.framebuffer_address, 229 | .size = edid_video_mode.linear_bytes_per_scanline * edid_video_mode.resolution_y, 230 | }); 231 | 232 | const framebuffer = .{ 233 | .address = framebuffer_region.address.value(), 234 | .pitch = edid_video_mode.linear_bytes_per_scanline, 235 | .width = edid_video_mode.resolution_x, 236 | .height = edid_video_mode.resolution_y, 237 | .bpp = edid_video_mode.bpp, 238 | .red_mask = .{ 239 | .shift = edid_video_mode.linear_red_mask_shift, 240 | .size = edid_video_mode.linear_red_mask_size, 241 | }, 242 | .green_mask = .{ 243 | .shift = edid_video_mode.linear_green_mask_shift, 244 | .size = edid_video_mode.linear_green_mask_size, 245 | }, 246 | .blue_mask = .{ 247 | .shift = edid_video_mode.linear_blue_mask_shift, 248 | .size = edid_video_mode.linear_blue_mask_size, 249 | }, 250 | .memory_model = 0x06, 251 | }; 252 | 253 | break :blk framebuffer; 254 | }, 255 | }; 256 | 257 | const gpt_cache = try lib.PartitionTable.GPT.Partition.Cache.fromPartitionIndex(&init.filesystem.disk.disk, 0, &init.filesystem.fat_allocator.allocator); 258 | init.filesystem.fat_cache = try lib.Filesystem.FAT32.Cache.fromGPTPartitionCache(&init.filesystem.fat_allocator.allocator, gpt_cache); 259 | init.filesystem.cache_index = init.filesystem.fat_allocator.allocated; 260 | try init.deinitializeMemoryMap(); 261 | 262 | init.memory_map_initialized = true; 263 | init.filesystem_initialized = true; 264 | init.framebuffer_initialized = true; 265 | 266 | init.early_initialized = true; 267 | } 268 | }; 269 | 270 | var initialization: Initialization = undefined; 271 | 272 | export fn _start() callconv(.C) noreturn { 273 | bios.A20Enable() catch @panic("A20 is not enabled"); 274 | 275 | initialization.initialize() catch |err| @panic(@errorName(err)); 276 | bootloader.Information.initialize(&initialization, .rise, .bios) catch |err| { 277 | @panic(@errorName(err)); 278 | }; 279 | } 280 | -------------------------------------------------------------------------------- /src/bootloader/rise/bios/test.zig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidgmbb/birth/a8d3984edb43e95696dc0361429e518524462f70/src/bootloader/rise/bios/test.zig -------------------------------------------------------------------------------- /src/bootloader/rise/bios/unreal_mode.S: -------------------------------------------------------------------------------- 1 | .code32 2 | .align 0x10 3 | .global interrupt 4 | interrupt: 5 | movb 4(%esp), %al 6 | movb %al, (.interrupt_number) 7 | 8 | movl 8(%esp), %eax 9 | movl %eax, (.out_registers) 10 | 11 | movl 12(%esp), %eax 12 | movl %eax, (.in_registers) 13 | 14 | sgdt [.protected_mode_gdt] 15 | sidt [.protected_mode_idt] 16 | lidt [.real_mode_idt] 17 | 18 | push %ebx 19 | push %esi 20 | push %edi 21 | push %ebp 22 | 23 | jmp $0x08, $.bits16 24 | 25 | .code16 26 | .bits16: 27 | mov $0x10, %ax 28 | mov %ax, %ds 29 | mov %ax, %es 30 | mov %ax, %fs 31 | mov %ax, %gs 32 | mov %ax, %ss 33 | mov %cr0, %eax 34 | and $0xfe, %al 35 | mov %eax, %cr0 36 | jmp $0x00, $.cs_zero 37 | .cs_zero: 38 | xor %ax, %ax 39 | mov %ax, %ss 40 | mov %esp, %ss:(.esp) 41 | mov %ss:(.in_registers), %esp 42 | pop %gs 43 | pop %fs 44 | pop %es 45 | pop %ds 46 | popfd 47 | pop %ebp 48 | pop %edi 49 | pop %esi 50 | pop %edx 51 | pop %ecx 52 | pop %ebx 53 | pop %eax 54 | mov %ss:(.esp), %esp 55 | sti 56 | .byte 0xcd 57 | .interrupt_number: .byte 0 58 | cli 59 | 60 | mov %esp, %ss:(.esp) 61 | mov %ss:(.out_registers), %esp 62 | lea 0x28(%esp), %esp 63 | push %eax 64 | push %ebx 65 | push %ecx 66 | push %edx 67 | push %esi 68 | push %ebp 69 | pushfd 70 | push %ds 71 | push %es 72 | push %fs 73 | push %gs 74 | mov %ss:(.esp), %esp 75 | 76 | lgdtl %ss:(.protected_mode_gdt) 77 | lidtl %ss:(.protected_mode_idt) 78 | 79 | mov %cr0, %eax 80 | or $0x1, %al 81 | mov %eax, %cr0 82 | jmp $0x18, $.bits32 83 | 84 | err: 85 | cli 86 | hlt 87 | 88 | .bits32: 89 | .code32 90 | mov $0x20, %eax 91 | mov %eax, %ds 92 | mov %eax, %es 93 | mov %eax, %fs 94 | mov %eax, %gs 95 | mov %eax, %ss 96 | 97 | pop %ebp 98 | pop %edi 99 | pop %esi 100 | pop %ebx 101 | 102 | 103 | ret 104 | 105 | .align 0x10 106 | .esp: .long 0 107 | .out_registers: .long 0 108 | .in_registers: .long 0 109 | .protected_mode_gdt: .quad 0 110 | .protected_mode_idt: .quad 0 111 | .real_mode_idt: 112 | .word 0x3ff 113 | .long 0 114 | -------------------------------------------------------------------------------- /src/bootloader/rise/uefi/test.zig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidgmbb/birth/a8d3984edb43e95696dc0361429e518524462f70/src/bootloader/rise/uefi/test.zig -------------------------------------------------------------------------------- /src/bootloader/todo/draw_context.zig: -------------------------------------------------------------------------------- 1 | pub const DrawContext = extern struct { 2 | x: u32 = 0, 3 | y: u32 = 0, 4 | color: u32 = 0xff_ff_ff_ff, 5 | reserved: u32 = 0, 6 | 7 | pub const Error = error{}; 8 | pub const Writer = lib.Writer(*DrawContext, DrawContext.Error, DrawContext.write); 9 | 10 | pub fn write(draw_context: *DrawContext, bytes: []const u8) DrawContext.Error!usize { 11 | const bootloader_information = @fieldParentPtr(Information, "draw_context", draw_context); 12 | const color = draw_context.color; 13 | for (bytes) |byte| { 14 | if (byte != '\n') { 15 | bootloader_information.font.draw(&bootloader_information.font, &bootloader_information.framebuffer, byte, color, draw_context.x, draw_context.y); 16 | if (draw_context.x + 8 < bootloader_information.framebuffer.width) { 17 | draw_context.x += @bitSizeOf(u8); 18 | continue; 19 | } 20 | } 21 | 22 | if (draw_context.y < bootloader_information.framebuffer.width) { 23 | draw_context.y += bootloader_information.font.character_size; 24 | draw_context.x = 0; 25 | } else { 26 | asm volatile ( 27 | \\cli 28 | \\hlt 29 | ); 30 | } 31 | } 32 | 33 | return bytes.len; 34 | } 35 | 36 | pub inline fn clearScreen(draw_context: *DrawContext, color: u32) void { 37 | const bootloader_information = @fieldParentPtr(Information, "draw_context", draw_context); 38 | const pixels_per_scanline = @divExact(bootloader_information.framebuffer.pitch, @divExact(bootloader_information.framebuffer.bpp, @bitSizeOf(u8))); 39 | const framebuffer_pixels = @intToPtr([*]u32, bootloader_information.framebuffer.address)[0 .. pixels_per_scanline * bootloader_information.framebuffer.height]; 40 | var y: u32 = 0; 41 | while (y < bootloader_information.framebuffer.height) : (y += 1) { 42 | const line = framebuffer_pixels[y * pixels_per_scanline .. y * pixels_per_scanline + pixels_per_scanline]; 43 | for (line) |*pixel| { 44 | pixel.* = color; 45 | } 46 | } 47 | } 48 | }; 49 | -------------------------------------------------------------------------------- /src/bootloader/todo/font.zig: -------------------------------------------------------------------------------- 1 | pub const Font = extern struct { 2 | file: PhysicalMemoryRegion align(8), // so 32-bit doesn't whine 3 | glyph_buffer_size: u32, 4 | character_size: u8, 5 | draw: *const fn (font: *const Font, framebuffer: *const Framebuffer, character: u8, color: u32, offset_x: u32, offset_y: u32) void, 6 | 7 | pub fn fromPSF1(file: []const u8) !Font { 8 | const header = @ptrCast(*const lib.PSF1.Header, file.ptr); 9 | if (!lib.equal(u8, &header.magic, &lib.PSF1.Header.magic)) { 10 | return lib.PSF1.Error.invalid_magic; 11 | } 12 | 13 | const glyph_buffer_size = @as(u32, header.character_size) * (lib.maxInt(u8) + 1) * (1 + @boolToInt(header.mode == 1)); 14 | 15 | return .{ 16 | .file = PhysicalMemoryRegion.new(PhysicalAddress.new(@ptrToInt(file.ptr)), file.len), 17 | .glyph_buffer_size = glyph_buffer_size, 18 | .character_size = header.character_size, 19 | .draw = drawPSF1, 20 | }; 21 | } 22 | 23 | fn drawPSF1(font: *const Font, framebuffer: *const Framebuffer, character: u8, color: u32, offset_x: u32, offset_y: u32) void { 24 | const bootloader_information = @fieldParentPtr(Information, "framebuffer", framebuffer); 25 | const glyph_buffer_virtual_region = if (bootloader_information.stage == .trampoline) font.file.toHigherHalfVirtualAddress() else font.file.toIdentityMappedVirtualAddress(); 26 | const glyph_buffer = glyph_buffer_virtual_region.access(u8)[@sizeOf(lib.PSF1.Header)..][0..font.glyph_buffer_size]; 27 | const glyph_offset = @as(usize, character) * font.character_size; 28 | const glyph = glyph_buffer[glyph_offset .. glyph_offset + font.character_size]; 29 | 30 | var glyph_index: usize = 0; 31 | _ = glyph_index; 32 | 33 | const pixels_per_scanline = @divExact(framebuffer.pitch, @divExact(framebuffer.bpp, @bitSizeOf(u8))); 34 | const fb = @intToPtr([*]u32, framebuffer.address)[0 .. pixels_per_scanline * framebuffer.height]; 35 | var y = offset_y; 36 | 37 | for (glyph) |byte| { 38 | const base_index = y * pixels_per_scanline + offset_x; 39 | if (byte & 1 << 7 != 0) fb[base_index + 0] = color; 40 | if (byte & 1 << 6 != 0) fb[base_index + 1] = color; 41 | if (byte & 1 << 5 != 0) fb[base_index + 2] = color; 42 | if (byte & 1 << 4 != 0) fb[base_index + 3] = color; 43 | if (byte & 1 << 3 != 0) fb[base_index + 4] = color; 44 | if (byte & 1 << 2 != 0) fb[base_index + 5] = color; 45 | if (byte & 1 << 1 != 0) fb[base_index + 6] = color; 46 | if (byte & 1 << 0 != 0) fb[base_index + 7] = color; 47 | 48 | y += 1; 49 | } 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /src/bootloader/todo/smp.zig: -------------------------------------------------------------------------------- 1 | // TODO: legacy stuff; refactor when SMP is implemented 2 | 3 | // pub fn initializeSMP(bootloader_information: *Information, madt: *const ACPI.MADT) void { 4 | // if (bootloader_information.bootloader != .rise) @panic("Protocol not supported"); 5 | // 6 | // const smp_records = bootloader_information.getSlice(.smps); 7 | // 8 | // switch (lib.cpu.arch) { 9 | // .x86, .x86_64 => { 10 | // const cr3 = bootloader_information.virtual_address_space.arch.cr3; 11 | // if (@bitCast(u64, cr3) > lib.maxInt(u32)) { 12 | // lib.log.err("CR3: 0x{x}, {}", .{ @bitCast(u64, cr3), cr3 }); 13 | // @panic("CR3 overflow"); 14 | // } 15 | // 16 | // const cpuid = lib.arch.x86_64.cpuid; 17 | // const lapicWrite = privileged.arch.x86_64.APIC.lapicWrite; 18 | // 19 | // if (cpuid(1).edx & (1 << 9) == 0) { 20 | // @panic("No APIC detected"); 21 | // } 22 | // 23 | // var iterator = madt.getIterator(); 24 | // var smp_index: usize = 0; 25 | // 26 | // const smp_trampoline_physical_address = PhysicalAddress.new(@ptrToInt(&arch.x86_64.smp_trampoline)); 27 | // // Sanity checks 28 | // const trampoline_argument_symbol = @extern(*SMP.Trampoline.Argument, .{ .name = "smp_trampoline_arg_start" }); 29 | // const smp_core_booted_symbol = @extern(*bool, .{ .name = "smp_core_booted" }); 30 | // const trampoline_argument_start = @ptrToInt(trampoline_argument_symbol); 31 | // const trampoline_argument_offset = @intCast(u32, trampoline_argument_start - smp_trampoline_physical_address.value()); 32 | // const smp_core_booted_offset = @intCast(u32, @ptrToInt(smp_core_booted_symbol) - smp_trampoline_physical_address.value()); 33 | // if (!lib.isAligned(trampoline_argument_start, @alignOf(SMP.Trampoline.Argument))) @panic("SMP trampoline argument alignment must match"); 34 | // const trampoline_argument_end = @ptrToInt(@extern(*u8, .{ .name = "smp_trampoline_arg_end" })); 35 | // const trampoline_argument_size = trampoline_argument_end - trampoline_argument_start; 36 | // if (trampoline_argument_size != @sizeOf(SMP.Trampoline.Argument)) { 37 | // @panic("SMP trampoline argument size must match"); 38 | // } 39 | // 40 | // const smp_trampoline_size = @ptrToInt(@extern(*u8, .{ .name = "smp_trampoline_end" })) - smp_trampoline_physical_address.value(); 41 | // if (smp_trampoline_size > lib.arch.valid_page_sizes[0]) { 42 | // @panic("SMP trampoline too big"); 43 | // } 44 | // 45 | // const smp_trampoline = @intCast(u32, switch (lib.cpu.arch) { 46 | // .x86 => smp_trampoline_physical_address.toIdentityMappedVirtualAddress().value(), 47 | // .x86_64 => blk: { 48 | // const page_counters = bootloader_information.getPageCounters(); 49 | // for (bootloader_information.getMemoryMapEntries(), 0..) |memory_map_entry, index| { 50 | // if (memory_map_entry.type == .usable and memory_map_entry.region.address.value() < lib.mb and lib.isAligned(memory_map_entry.region.address.value(), lib.arch.valid_page_sizes[0])) { 51 | // const page_counter = &page_counters[index]; 52 | // const offset = page_counter.* * lib.arch.valid_page_sizes[0]; 53 | // if (offset < memory_map_entry.region.size) { 54 | // page_counter.* += 1; 55 | // const smp_trampoline_buffer_region = memory_map_entry.region.offset(offset).toIdentityMappedVirtualAddress(); 56 | // 57 | // privileged.arch.x86_64.paging.setMappingFlags(&bootloader_information.virtual_address_space, smp_trampoline_buffer_region.address.value(), .{ 58 | // .write = true, 59 | // .execute = true, 60 | // .global = true, 61 | // }) catch @panic("can't set smp trampoline flags"); 62 | // 63 | // const smp_trampoline_buffer = smp_trampoline_buffer_region.access(u8); 64 | // const smp_trampoline_region = PhysicalMemoryRegion.new(smp_trampoline_physical_address, smp_trampoline_size); 65 | // const smp_trampoline_source = smp_trampoline_region.toIdentityMappedVirtualAddress().access(u8); 66 | // 67 | // @memcpy(smp_trampoline_buffer, smp_trampoline_source); 68 | // break :blk smp_trampoline_buffer_region.address.value(); 69 | // } 70 | // } 71 | // } 72 | // 73 | // @panic("No suitable region found for SMP trampoline"); 74 | // }, 75 | // else => @compileError("Architecture not supported"), 76 | // }); 77 | // 78 | // const trampoline_argument = @intToPtr(*SMP.Trampoline.Argument, smp_trampoline + trampoline_argument_offset); 79 | // trampoline_argument.* = .{ 80 | // .hhdm = bootloader_information.higher_half, 81 | // .cr3 = @intCast(u32, @bitCast(u64, cr3)), 82 | // .gdt_descriptor = undefined, 83 | // .gdt = .{}, 84 | // }; 85 | // 86 | // trampoline_argument.gdt_descriptor = trampoline_argument.gdt.getDescriptor(); 87 | // 88 | // const smp_core_booted = @intToPtr(*bool, smp_trampoline + smp_core_booted_offset); 89 | // 90 | // while (iterator.next()) |entry| { 91 | // switch (entry.type) { 92 | // .LAPIC => { 93 | // const lapic_entry = @fieldParentPtr(ACPI.MADT.LAPIC, "record", entry); 94 | // const lapic_id = @as(u32, lapic_entry.APIC_ID); 95 | // smp_records[smp_index] = .{ 96 | // .acpi_id = lapic_entry.ACPI_processor_UID, 97 | // .lapic_id = lapic_id, 98 | // .entry_point = 0, 99 | // .argument = 0, 100 | // }; 101 | // 102 | // if (lapic_entry.APIC_ID == bootloader_information.smp.bsp_lapic_id) { 103 | // smp_index += 1; 104 | // continue; 105 | // } 106 | // 107 | // lapicWrite(.icr_high, lapic_id << 24); 108 | // lapicWrite(.icr_low, 0x4500); 109 | // 110 | // arch.x86_64.delay(10_000_000); 111 | // 112 | // const icr_low = (smp_trampoline >> 12) | 0x4600; 113 | // lapicWrite(.icr_high, lapic_id << 24); 114 | // lapicWrite(.icr_low, icr_low); 115 | // 116 | // for (0..100) |_| { 117 | // if (@cmpxchgStrong(bool, smp_core_booted, true, false, .SeqCst, .SeqCst) == null) { 118 | // lib.log.debug("Booted core #{}", .{lapic_id}); 119 | // break; 120 | // } 121 | // 122 | // arch.x86_64.delay(10_000_000); 123 | // } else @panic("SMP not booted"); 124 | // }, 125 | // .x2APIC => @panic("x2APIC"), 126 | // else => { 127 | // lib.log.warn("Unhandled {s} entry", .{@tagName(entry.type)}); 128 | // }, 129 | // } 130 | // } 131 | // 132 | // lib.log.debug("Enabled all cores!", .{}); 133 | // }, 134 | // else => @compileError("Architecture not supported"), 135 | // } 136 | // } 137 | -------------------------------------------------------------------------------- /src/bootloader/uefi.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const alignForward = lib.alignForward; 3 | const assert = lib.assert; 4 | const CustomAllocator = lib.CustomAllocator; 5 | const log = lib.log.scoped(.UEFI); 6 | const uefi = lib.uefi; 7 | 8 | pub const BootServices = uefi.tables.BootServices; 9 | pub const ConfigurationTable = uefi.tables.ConfigurationTable; 10 | pub const Error = Status.EfiError; 11 | pub const FileInfo = uefi.protocols.FileInfo; 12 | pub const FileProtocol = uefi.protocols.FileProtocol; 13 | pub const GraphicsOutputProtocol = uefi.protocols.GraphicsOutputProtocol; 14 | pub const LoadedImageProtocol = uefi.protocols.LoadedImageProtocol; 15 | pub const Handle = uefi.Handle; 16 | pub const MemoryDescriptor = uefi.tables.MemoryDescriptor; 17 | pub const SimpleFilesystemProtocol = uefi.protocols.SimpleFileSystemProtocol; 18 | pub const Status = uefi.Status; 19 | pub const SystemTable = uefi.tables.SystemTable; 20 | pub const Try = Status.err; 21 | 22 | const str16 = lib.std.unicode.utf8ToUtf16LeStringLiteral; 23 | 24 | pub const page_size = 0x1000; 25 | pub const page_shifter = lib.arch.page_shifter(page_size); 26 | 27 | const privileged = @import("privileged"); 28 | const PhysicalAddress = privileged.PhysicalAddress; 29 | const VirtualAddress = privileged.VirtualAddress; 30 | const VirtualAddressSpace = privileged.VirtualAddressSpace; 31 | const VirtualMemoryRegion = privileged.VirtualMemoryRegion; 32 | const stopCPU = privileged.arch.stopCPU; 33 | 34 | pub fn panic(comptime format: []const u8, arguments: anytype) noreturn { 35 | privileged.arch.disableInterrupts(); 36 | lib.log.scoped(.PANIC).err(format, arguments); 37 | privileged.arch.stopCPU(); 38 | } 39 | 40 | pub fn result(src: lib.SourceLocation, status: Status) void { 41 | Try(status) catch |err| { 42 | panic("UEFI error {} at {s}:{}:{} in function {s}", .{ err, src.file, src.line, src.column, src.fn_name }); 43 | }; 44 | } 45 | 46 | pub inline fn getSystemTable() *SystemTable { 47 | return uefi.system_table; 48 | } 49 | 50 | pub inline fn getHandle() Handle { 51 | return uefi.handle; 52 | } 53 | 54 | pub const Protocol = struct { 55 | pub fn locate(comptime ProtocolT: type, boot_services: *BootServices) !*ProtocolT { 56 | var pointer_buffer: ?*anyopaque = null; 57 | try Try(boot_services.locateProtocol(&ProtocolT.guid, null, &pointer_buffer)); 58 | return cast(ProtocolT, pointer_buffer); 59 | } 60 | 61 | pub fn handle(comptime ProtocolT: type, boot_services: *BootServices, efi_handle: Handle) !*ProtocolT { 62 | var interface_buffer: ?*anyopaque = null; 63 | try Try(boot_services.handleProtocol(efi_handle, &ProtocolT.guid, &interface_buffer)); 64 | return cast(ProtocolT, interface_buffer); 65 | } 66 | 67 | pub fn open(comptime ProtocolT: type, boot_services: *BootServices, efi_handle: Handle) !*ProtocolT { 68 | var interface_buffer: ?*anyopaque = null; 69 | try Try(boot_services.openProtocol(efi_handle, &ProtocolT.guid, &interface_buffer, efi_handle, null, .{ .get_protocol = true })); 70 | return cast(ProtocolT, interface_buffer); 71 | } 72 | 73 | fn cast(comptime ProtocolT: type, ptr: ?*anyopaque) *ProtocolT { 74 | return @ptrCast(*ProtocolT, @alignCast(@alignOf(ProtocolT), ptr)); 75 | } 76 | }; 77 | 78 | pub const ProgramSegment = extern struct { 79 | physical: u64, 80 | virtual: u64, 81 | size: u32, 82 | file_offset: u32, 83 | mappings: extern struct { 84 | write: bool, 85 | execute: bool, 86 | }, 87 | }; 88 | 89 | //pub const LoadKernelFunction = fn (bootloader_information: *BootloaderInformation, kernel_start_address: u64, cr3: privileged.arch.x86_64.registers.cr3, stack: u64, gdt_descriptor: *privileged.arch.x86_64.GDT.Descriptor) callconv(.SysV) noreturn; 90 | -------------------------------------------------------------------------------- /src/cpu/arch.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | 3 | pub const current = switch (lib.cpu.arch) { 4 | .x86_64 => x86_64, 5 | else => @compileError("Architecture not supported"), 6 | }; 7 | 8 | pub const x86_64 = @import("arch/x86_64.zig"); 9 | pub usingnamespace current; 10 | 11 | pub const entryPoint = current.entryPoint; 12 | pub const virtualAddressSpaceallocatePages = current.virtualAddressSpaceallocatePages; 13 | pub const root_page_table_type = current.root_page_table_entry; 14 | -------------------------------------------------------------------------------- /src/cpu/arch/x86/64/linker_script.ld: -------------------------------------------------------------------------------- 1 | PHDRS { 2 | none PT_NULL FLAGS(0); 3 | text PT_LOAD FLAGS((1 << 2) | (1 << 0) /* Readable | Executable */); 4 | rodata PT_LOAD FLAGS((1 << 2) /* Readable */); 5 | data PT_LOAD FLAGS((1 << 2) | (1 << 1) /* Readable | Writeable */); 6 | } 7 | 8 | SECTIONS { 9 | . = 0xFFFFFFFF80000000; 10 | PROVIDE(cpu_driver_start = .); 11 | 12 | 13 | PROVIDE(text_section_start = .); 14 | .text . : { 15 | *(.text*) 16 | }:text 17 | 18 | . = ALIGN(4K); 19 | PROVIDE(text_section_end = .); 20 | 21 | PROVIDE(rodata_section_start = .); 22 | .rodata . : { 23 | *(.rodata*) 24 | }:rodata 25 | 26 | . = ALIGN(4K); 27 | PROVIDE(rodata_section_end = .); 28 | 29 | PROVIDE(data_section_start = .); 30 | .data . : { 31 | *(.data*) 32 | *(.bss*) 33 | *(.got*) 34 | }:data 35 | 36 | . = ALIGN(4K); 37 | PROVIDE(data_section_end = .); 38 | PROVIDE(cpu_driver_end = .); 39 | } 40 | -------------------------------------------------------------------------------- /src/cpu/arch/x86/64/syscall.zig: -------------------------------------------------------------------------------- 1 | const cpu = @import("cpu"); 2 | const lib = @import("lib"); 3 | const log = lib.log; 4 | const privileged = @import("privileged"); 5 | const rise = @import("rise"); 6 | 7 | const assert = lib.assert; 8 | 9 | const cr3 = privileged.arch.x86_64.registers.cr3; 10 | 11 | const cr3_user_page_table_mask = 1 << @bitOffsetOf(cr3, "address"); 12 | const cr3_user_page_table_and_pcid_mask = cr3_user_page_table_mask | pcid_mask; 13 | const pcid_bit = 11; 14 | const pcid_mask = 1 << pcid_bit; 15 | 16 | /// SYSCALL documentation 17 | /// ABI: 18 | /// - RAX: System call options (number for Linux) 19 | /// - RCX: Return address 20 | /// - R11: Saved rflags 21 | /// - RDI: argument 0 22 | /// - RSI: argument 1 23 | /// - RDX: argument 2 24 | /// - R10: argument 3 25 | /// - R8: argument 4 26 | /// - R9: argument 5 27 | fn riseSyscall(comptime Syscall: type, raw_arguments: rise.syscall.Arguments) Syscall.ErrorSet.Error!Syscall.Result { 28 | comptime assert(Syscall == rise.capabilities.Syscall(Syscall.capability, Syscall.command)); 29 | const capability: rise.capabilities.Type = Syscall.capability; 30 | const command: rise.capabilities.Command(capability) = Syscall.command; 31 | const arguments = try Syscall.toArguments(raw_arguments); 32 | 33 | if (cpu.user_scheduler.capability_root_node.hasPermissions(capability, command)) { 34 | const result = switch (capability) { 35 | .io => switch (command) { 36 | .copy, .mint, .retype, .delete, .revoke, .create => unreachable, 37 | .log => blk: { 38 | const message = arguments; 39 | cpu.writer.writeAll(message) catch unreachable; 40 | comptime assert(Syscall.Result == usize); 41 | break :blk message.len; 42 | }, 43 | }, 44 | .cpu => switch (command) { 45 | .copy, .mint, .retype, .delete, .revoke, .create => unreachable, 46 | .get_core_id => cpu.core_id, 47 | .shutdown => privileged.exitFromQEMU(.success), 48 | }, 49 | .cpu_memory => switch (command) { 50 | .allocate => blk: { 51 | comptime assert(@TypeOf(arguments) == usize); 52 | const size = arguments; 53 | const physical_region = try cpu.user_scheduler.capability_root_node.allocatePages(size); 54 | try cpu.user_scheduler.capability_root_node.allocateCPUMemory(physical_region, .{ .privileged = false }); 55 | break :blk physical_region.address; 56 | }, 57 | else => @panic(@tagName(command)), 58 | }, 59 | .ram => unreachable, 60 | .boot => switch (command) { 61 | .get_bundle_size => cpu.bundle.len, 62 | .get_bundle_file_list_size => cpu.bundle_files.len, 63 | else => @panic(@tagName(command)), 64 | }, 65 | }; 66 | 67 | return result; 68 | } else { 69 | return error.forbidden; 70 | } 71 | } 72 | 73 | export fn syscall(registers: *const Registers) callconv(.C) rise.syscall.Result { 74 | const options = @bitCast(rise.syscall.Options, registers.syscall_number); 75 | const arguments = rise.syscall.Arguments{ registers.rdi, registers.rsi, registers.rdx, registers.r10, registers.r8, registers.r9 }; 76 | 77 | return switch (options.general.convention) { 78 | .rise => switch (options.rise.type) { 79 | inline else => |capability| switch (@intToEnum(rise.capabilities.Command(capability), options.rise.command)) { 80 | inline else => |command| blk: { 81 | const Syscall = rise.capabilities.Syscall(capability, command); 82 | const result: Syscall.Result = riseSyscall(Syscall, arguments) catch |err| break :blk Syscall.errorToRaw(err); 83 | break :blk Syscall.resultToRaw(result); 84 | }, 85 | }, 86 | }, 87 | .linux => @panic("linux syscall"), 88 | }; 89 | } 90 | 91 | /// SYSCALL documentation 92 | /// ABI: 93 | /// - RAX: System call number 94 | /// - RCX: Return address 95 | /// - R11: Saved rflags 96 | /// - RDI: argument 0 97 | /// - RSI: argument 1 98 | /// - RDX: argument 2 99 | /// - R10: argument 3 100 | /// - R8: argument 4 101 | /// - R9: argument 5 102 | pub fn entryPoint() callconv(.Naked) void { 103 | asm volatile ( 104 | \\endbr64 105 | \\swapgs 106 | \\movq %rsp, user_stack(%rip) 107 | ); 108 | 109 | if (cpu.arch.x86_64.kpti) { 110 | asm volatile ( 111 | \\mov %cr3, %rsp 112 | ::: "memory"); 113 | 114 | if (cpu.arch.pcid) { 115 | @compileError("pcid support not yet implemented"); 116 | } 117 | 118 | asm volatile ( 119 | \\andq %[mask], %rsp 120 | \\mov %rsp, %cr3 121 | : 122 | : [mask] "i" (~@as(u64, cr3_user_page_table_and_pcid_mask)), 123 | : "memory" 124 | ); 125 | } 126 | 127 | // Safe stack 128 | asm volatile ("movabsq %[capability_address_space_stack_top], %rsp" 129 | : 130 | : [capability_address_space_stack_top] "i" (cpu.arch.x86_64.capability_address_space_stack_top), 131 | : "memory", "rsp" 132 | ); 133 | 134 | asm volatile ( 135 | \\pushq %[user_ds] 136 | \\pushq (user_stack) 137 | \\pushq %r11 138 | \\pushq %[user_cs] 139 | \\pushq %rcx 140 | \\pushq %rax 141 | : 142 | : [user_ds] "i" (cpu.arch.x86_64.user_data_selector), 143 | [user_cs] "i" (cpu.arch.x86_64.user_code_selector), 144 | : "memory" 145 | ); 146 | 147 | // Push and clear registers 148 | asm volatile ( 149 | // Push 150 | \\pushq %rdi 151 | \\pushq %rsi 152 | \\pushq %rdx 153 | \\pushq %rcx 154 | \\pushq %rax 155 | \\pushq %r8 156 | \\pushq %r9 157 | \\pushq %r10 158 | \\pushq %r11 159 | \\pushq %rbx 160 | \\pushq %rbp 161 | \\pushq %r12 162 | \\pushq %r13 163 | \\pushq %r14 164 | \\pushq %r15 165 | // Clear 166 | \\xorl %esi, %esi 167 | \\xorl %edx, %edx 168 | \\xorl %ecx, %ecx 169 | \\xorl %r8d, %r8d 170 | \\xorl %r9d, %r9d 171 | \\xorl %r10d, %r10d 172 | \\xorl %r11d, %r11d 173 | \\xorl %ebx, %ebx 174 | \\xorl %ebp, %ebp 175 | \\xorl %r12d, %r12d 176 | \\xorl %r13d, %r13d 177 | \\xorl %r14d, %r14d 178 | \\xorl %r15d, %r15d 179 | ::: "memory"); 180 | 181 | // Pass arguments 182 | asm volatile ( 183 | \\mov %rsp, %rdi 184 | \\mov %rax, %rsi 185 | ::: "memory"); 186 | 187 | // TODO: more security stuff 188 | asm volatile ( 189 | \\call syscall 190 | ::: "memory"); 191 | 192 | // TODO: more security stuff 193 | 194 | // Pop registers 195 | asm volatile ( 196 | \\popq %r15 197 | \\popq %r14 198 | \\popq %r13 199 | \\popq %r12 200 | \\popq %rbp 201 | \\popq %rbx 202 | \\popq %r11 203 | \\popq %r10 204 | \\popq %r9 205 | \\popq %r8 206 | \\popq %rcx 207 | // RAX 208 | \\popq %rcx 209 | // RDX 210 | \\popq %rsi 211 | \\popq %rsi 212 | \\popq %rdi 213 | ::: "memory"); 214 | 215 | if (cpu.arch.x86_64.kpti) { 216 | // Restore CR3 217 | asm volatile ( 218 | \\mov %cr3, %rsp 219 | ::: "memory"); 220 | 221 | if (cpu.arch.x86_64.pcid) { 222 | @compileError("PCID not supported yet"); 223 | } 224 | 225 | asm volatile ( 226 | \\orq %[user_cr3_mask], %rsp 227 | \\mov %rsp, %cr3 228 | : 229 | : [user_cr3_mask] "i" (cr3_user_page_table_mask), 230 | : "memory" 231 | ); 232 | } 233 | 234 | // Restore RSP 235 | asm volatile ( 236 | \\mov user_stack(%rip), %rsp 237 | ::: "memory"); 238 | 239 | asm volatile ( 240 | \\swapgs 241 | \\sysretq 242 | ::: "memory"); 243 | 244 | asm volatile ( 245 | \\int3 246 | ::: "memory"); 247 | 248 | unreachable; 249 | } 250 | 251 | pub const Registers = extern struct { 252 | r15: u64, 253 | r14: u64, 254 | r13: u64, 255 | r12: u64, 256 | rbp: u64, 257 | rbx: u64, 258 | r11: u64, 259 | r10: u64, 260 | r9: u64, 261 | r8: u64, 262 | rax: u64, 263 | rcx: u64, 264 | rdx: u64, 265 | rsi: u64, 266 | rdi: u64, 267 | syscall_number: u64, 268 | rip: u64, 269 | cs: u64, 270 | rflags: u64, 271 | rsp: u64, 272 | ss: u64, 273 | }; 274 | -------------------------------------------------------------------------------- /src/cpu/arch/x86_64.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const Allocator = lib.Allocator; 3 | const assert = lib.assert; 4 | const ELF = lib.ELF(64); 5 | const log = lib.log; 6 | const Spinlock = lib.Spinlock; 7 | const bootloader = @import("bootloader"); 8 | const privileged = @import("privileged"); 9 | const panic = cpu.panic; 10 | const PageAllocator = cpu.PageAllocator; 11 | const x86_64 = privileged.arch.x86_64; 12 | const APIC = x86_64.APIC; 13 | const paging = x86_64.paging; 14 | const cr0 = x86_64.registers.cr0; 15 | const cr3 = x86_64.registers.cr3; 16 | const cr4 = x86_64.registers.cr4; 17 | const PhysicalAddress = lib.PhysicalAddress; 18 | const PhysicalMemoryRegion = lib.PhysicalMemoryRegion; 19 | const VirtualAddress = lib.VirtualAddress; 20 | const VirtualMemoryRegion = lib.VirtualMemoryRegion; 21 | 22 | const cpu = @import("cpu"); 23 | const Heap = cpu.Heap; 24 | const VirtualAddressSpace = cpu.VirtualAddressSpace; 25 | 26 | const init = @import("./x86/64/init.zig"); 27 | pub const syscall = @import("./x86/64/syscall.zig"); 28 | pub const entryPoint = init.entryPoint; 29 | 30 | const rise = @import("rise"); 31 | 32 | var writer_lock: Spinlock = .released; 33 | 34 | pub const Registers = extern struct { 35 | r15: u64, 36 | r14: u64, 37 | r13: u64, 38 | r12: u64, 39 | rbp: u64, 40 | rbx: u64, 41 | r11: u64, 42 | r10: u64, 43 | r9: u64, 44 | r8: u64, 45 | rax: u64, 46 | rcx: u64, 47 | rdx: u64, 48 | rsi: u64, 49 | rdi: u64, 50 | syscall_number_or_error_code: u64, 51 | rip: u64, 52 | cs: u64, 53 | rflags: lib.arch.x86_64.registers.RFLAGS, 54 | rsp: u64, 55 | ss: u64, 56 | }; 57 | 58 | const interrupt_kind: u32 = 0; 59 | 60 | export fn interruptHandler(regs: *const InterruptRegisters, interrupt_number: u8) void { 61 | switch (interrupt_number) { 62 | local_timer_vector => { 63 | APIC.write(.eoi, 0); 64 | nextTimer(10); 65 | }, 66 | else => cpu.panicFromInstructionPointerAndFramePointer(regs.rip, regs.rbp, "Exception: 0x{x}", .{interrupt_number}), 67 | } 68 | } 69 | 70 | const InterruptRegisters = extern struct { 71 | r15: u64, 72 | r14: u64, 73 | r13: u64, 74 | r12: u64, 75 | rbp: u64, 76 | rbx: u64, 77 | r11: u64, 78 | r10: u64, 79 | r9: u64, 80 | r8: u64, 81 | rax: u64, 82 | rcx: u64, 83 | rdx: u64, 84 | rsi: u64, 85 | rdi: u64, 86 | error_code: u64, 87 | rip: u64, 88 | cs: u64, 89 | rflags: u64, 90 | rsp: u64, 91 | ss: u64, 92 | }; 93 | 94 | const local_timer_vector = 0xef; 95 | pub export var ticks_per_ms: privileged.arch.x86_64.TicksPerMS = undefined; 96 | pub inline fn nextTimer(ms: u32) void { 97 | APIC.write(.lvt_timer, local_timer_vector | (1 << 17)); 98 | APIC.write(.timer_initcnt, ticks_per_ms.lapic * ms); 99 | } 100 | pub const kpti = true; 101 | pub const pcid = false; 102 | pub const smap = false; 103 | pub const invariant_tsc = false; 104 | pub const capability_address_space_size = 1 * lib.gb; 105 | pub const capability_address_space_start = capability_address_space_stack_top - capability_address_space_size; 106 | pub const capability_address_space_stack_top = 0xffff_ffff_8000_0000; 107 | pub const capability_address_space_stack_size = privileged.default_stack_size; 108 | pub const capability_address_space_stack_alignment = lib.arch.valid_page_sizes[0]; 109 | pub const capability_address_space_stack_address = VirtualAddress.new(capability_address_space_stack_top - capability_address_space_stack_size); 110 | pub const code_64 = @offsetOf(GDT, "code_64"); 111 | pub const data_64 = @offsetOf(GDT, "data_64"); 112 | pub const user_code_64 = @offsetOf(GDT, "user_code_64"); 113 | pub const user_data_64 = @offsetOf(GDT, "user_data_64"); 114 | pub const tss_selector = @offsetOf(GDT, "tss_descriptor"); 115 | pub const user_code_selector = user_code_64 | user_dpl; 116 | pub const user_data_selector = user_data_64 | user_dpl; 117 | pub const user_dpl = 3; 118 | 119 | pub const GDT = extern struct { 120 | null: Entry = GDT.Entry.null_entry, // 0x00 121 | code_16: Entry = GDT.Entry.code_16, // 0x08 122 | data_16: Entry = GDT.Entry.data_16, // 0x10 123 | code_32: Entry = GDT.Entry.code_32, // 0x18 124 | data_32: Entry = GDT.Entry.data_32, // 0x20 125 | code_64: u64 = 0x00A09A0000000000, // 0x28 126 | data_64: u64 = 0x0000920000000000, // 0x30 127 | user_data_64: u64 = @as(u64, 0x0000920000000000) | (3 << 45), //GDT.Entry.user_data_64, // 0x38 128 | user_code_64: u64 = @as(u64, 0x00A09A0000000000) | (3 << 45), //GDT.Entry.user_code_64, // 0x40 129 | tss_descriptor: TSS.Descriptor = undefined, // 0x48 130 | 131 | const Entry = privileged.arch.x86_64.GDT.Entry; 132 | 133 | pub const Descriptor = privileged.arch.x86_64.GDT.Descriptor; 134 | 135 | comptime { 136 | const entry_count = 9; 137 | const target_size = entry_count * @sizeOf(Entry) + @sizeOf(TSS.Descriptor); 138 | 139 | assert(@sizeOf(GDT) == target_size); 140 | assert(@offsetOf(GDT, "code_64") == 0x28); 141 | assert(@offsetOf(GDT, "data_64") == 0x30); 142 | assert(@offsetOf(GDT, "user_data_64") == 0x38); 143 | assert(@offsetOf(GDT, "user_code_64") == 0x40); 144 | assert(@offsetOf(GDT, "tss_descriptor") == entry_count * @sizeOf(Entry)); 145 | } 146 | 147 | pub fn getDescriptor(global_descriptor_table: *const GDT) GDT.Descriptor { 148 | return .{ 149 | .limit = @sizeOf(GDT) - 1, 150 | .address = @ptrToInt(global_descriptor_table), 151 | }; 152 | } 153 | }; 154 | 155 | pub const SystemSegmentDescriptor = extern struct { 156 | const Type = enum(u4) { 157 | ldt = 0b0010, 158 | tss_available = 0b1001, 159 | tss_busy = 0b1011, 160 | call_gate = 0b1100, 161 | interrupt_gate = 0b1110, 162 | trap_gate = 0b1111, 163 | }; 164 | }; 165 | 166 | pub const TSS = extern struct { 167 | reserved0: u32 = 0, 168 | rsp: [3]u64 align(4) = [3]u64{ 0, 0, 0 }, 169 | reserved1: u64 align(4) = 0, 170 | IST: [7]u64 align(4) = [7]u64{ 0, 0, 0, 0, 0, 0, 0 }, 171 | reserved3: u64 align(4) = 0, 172 | reserved4: u16 = 0, 173 | IO_map_base_address: u16 = 104, 174 | 175 | comptime { 176 | assert(@sizeOf(TSS) == 104); 177 | } 178 | 179 | pub const Descriptor = extern struct { 180 | limit_low: u16, 181 | base_low: u16, 182 | base_mid_low: u8, 183 | access: Access, 184 | attributes: Attributes, 185 | base_mid_high: u8, 186 | base_high: u32, 187 | reserved: u32 = 0, 188 | 189 | pub const Access = packed struct(u8) { 190 | type: SystemSegmentDescriptor.Type, 191 | reserved: u1 = 0, 192 | dpl: u2, 193 | present: bool, 194 | }; 195 | 196 | pub const Attributes = packed struct(u8) { 197 | limit: u4, 198 | available_for_system_software: bool, 199 | reserved: u2 = 0, 200 | granularity: bool, 201 | }; 202 | 203 | comptime { 204 | assert(@sizeOf(TSS.Descriptor) == 0x10); 205 | } 206 | }; 207 | 208 | pub fn getDescriptor(tss_struct: *const TSS, offset: u64) Descriptor { 209 | const address = @ptrToInt(tss_struct) + offset; 210 | return Descriptor{ 211 | .low = .{ 212 | .limit_low = @truncate(u16, @sizeOf(TSS) - 1), 213 | .base_low = @truncate(u16, address), 214 | .base_low_mid = @truncate(u8, address >> 16), 215 | .type = 0b1001, 216 | .descriptor_privilege_level = 0, 217 | .present = 1, 218 | .limit_high = 0, 219 | .available_for_system_software = 0, 220 | .granularity = 0, 221 | .base_mid = @truncate(u8, address >> 24), 222 | }, 223 | .base_high = @truncate(u32, address >> 32), 224 | }; 225 | } 226 | }; 227 | 228 | pub const IDT = extern struct { 229 | descriptors: [entry_count]GateDescriptor = undefined, 230 | pub const Descriptor = privileged.arch.x86_64.SegmentDescriptor; 231 | pub const GateDescriptor = extern struct { 232 | offset_low: u16, 233 | segment_selector: u16, 234 | flags: packed struct(u16) { 235 | ist: u3, 236 | reserved: u5 = 0, 237 | type: SystemSegmentDescriptor.Type, 238 | reserved1: u1 = 0, 239 | dpl: u2, 240 | present: bool, 241 | }, 242 | offset_mid: u16, 243 | offset_high: u32, 244 | reserved: u32 = 0, 245 | 246 | comptime { 247 | assert(@sizeOf(@This()) == 0x10); 248 | } 249 | }; 250 | pub const entry_count = 256; 251 | }; 252 | 253 | const ApicPageAllocator = extern struct { 254 | pages: [4]PhysicalAddress = .{PhysicalAddress.invalid()} ** 4, 255 | 256 | const PageEntry = cpu.VirtualAddressSpace.PageEntry; 257 | 258 | fn allocate(context: ?*anyopaque, size: u64, alignment: u64, options: privileged.PageAllocator.AllocateOptions) Allocator.Allocate.Error!PhysicalMemoryRegion { 259 | const apic_allocator = @ptrCast(?*ApicPageAllocator, @alignCast(@alignOf(ApicPageAllocator), context)) orelse return Allocator.Allocate.Error.OutOfMemory; 260 | assert(size == lib.arch.valid_page_sizes[0]); 261 | assert(alignment == lib.arch.valid_page_sizes[0]); 262 | assert(options.count == 1); 263 | assert(options.level_valid); 264 | const physical_memory = try cpu.page_allocator.allocate(size, alignment); 265 | apic_allocator.pages[@enumToInt(options.level)] = physical_memory.address; 266 | return physical_memory; 267 | } 268 | }; 269 | 270 | var apic_page_allocator = ApicPageAllocator{}; 271 | const apic_page_allocator_interface = privileged.PageAllocator{ 272 | .allocate = ApicPageAllocator.allocate, 273 | .context = &apic_page_allocator, 274 | .context_type = .cpu, 275 | }; 276 | 277 | pub inline fn writerStart() void { 278 | writer_lock.acquire(); 279 | } 280 | 281 | pub inline fn writerEnd() void { 282 | writer_lock.release(); 283 | } 284 | 285 | /// Architecture-specific implementation of mapping when you already can create user-space virtual address spaces 286 | pub fn map(virtual_address_space: *VirtualAddressSpace, asked_physical_address: PhysicalAddress, asked_virtual_address: VirtualAddress, size: u64, general_flags: privileged.Mapping.Flags) !void { 287 | if (general_flags.user) { 288 | assert(!general_flags.secret); 289 | } 290 | 291 | try virtual_address_space.arch.map(asked_physical_address, asked_virtual_address, size, general_flags, virtual_address_space.getPageAllocatorInterface()); 292 | if (!general_flags.secret) { 293 | const cpu_pml4 = try virtual_address_space.arch.getCpuPML4Table(); 294 | const user_pml4 = try virtual_address_space.arch.getUserPML4Table(); 295 | const first_indices = paging.computeIndices(asked_virtual_address.value()); 296 | const last_indices = paging.computeIndices(asked_virtual_address.offset(size - lib.arch.valid_page_sizes[0]).value()); 297 | const first_index = first_indices[@enumToInt(paging.Level.PML4)]; 298 | const last_index = @intCast(u9, last_indices[@enumToInt(paging.Level.PML4)]) +| 1; 299 | 300 | for (cpu_pml4[first_index..last_index], user_pml4[first_index..last_index]) |cpu_pml4te, *user_pml4te| { 301 | user_pml4te.* = cpu_pml4te; 302 | } 303 | } 304 | } 305 | pub const PageTableEntry = paging.Level; 306 | pub const root_page_table_entry = @intToEnum(cpu.arch.PageTableEntry, 0); 307 | 308 | pub const IOMap = extern struct { 309 | debug: bool, 310 | }; 311 | -------------------------------------------------------------------------------- /src/cpu/main.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const assert = lib.assert; 3 | const log = lib.log; 4 | 5 | const privileged = @import("privileged"); 6 | const stopCPU = privileged.arch.stopCPU; 7 | 8 | const cpu = @import("cpu"); 9 | 10 | var lock: lib.Spinlock = .released; 11 | 12 | pub const std_options = struct { 13 | pub fn logFn(comptime level: lib.log.Level, comptime scope: @TypeOf(.EnumLiteral), comptime format: []const u8, args: anytype) void { 14 | lock.acquire(); 15 | cpu.writer.writeAll("[CPU DRIVER] ") catch unreachable; 16 | cpu.writer.writeByte('[') catch unreachable; 17 | cpu.writer.writeAll(@tagName(scope)) catch unreachable; 18 | cpu.writer.writeAll("] ") catch unreachable; 19 | cpu.writer.writeByte('[') catch unreachable; 20 | cpu.writer.writeAll(@tagName(level)) catch unreachable; 21 | cpu.writer.writeAll("] ") catch unreachable; 22 | lib.format(cpu.writer, format, args) catch unreachable; 23 | cpu.writer.writeByte('\n') catch unreachable; 24 | 25 | lock.release(); 26 | } 27 | 28 | pub const log_level = lib.log.Level.debug; 29 | }; 30 | 31 | pub fn panic(message: []const u8, _: ?*lib.StackTrace, _: ?usize) noreturn { 32 | @call(.always_inline, cpu.panic, .{ "{s}", .{message} }); 33 | } 34 | 35 | comptime { 36 | @export(cpu.arch.entryPoint, .{ .name = "_start", .linkage = .Strong }); 37 | } 38 | -------------------------------------------------------------------------------- /src/cpu/test.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const log = lib.log.scoped(.TEST); 3 | const privileged = @import("privileged"); 4 | const writer = privileged.writer; 5 | 6 | test "Hello kernel" { 7 | lib.testing.log_level = .debug; 8 | log.debug("Hello kernel test", .{}); 9 | } 10 | 11 | pub const std_options = struct { 12 | pub fn logFn(comptime level: lib.log.Level, comptime scope: @TypeOf(.EnumLiteral), comptime format: []const u8, args: anytype) void { 13 | _ = level; 14 | writer.writeAll("[CPU DRIVER] ") catch unreachable; 15 | writer.writeByte('[') catch unreachable; 16 | writer.writeAll(@tagName(scope)) catch unreachable; 17 | writer.writeAll("] ") catch unreachable; 18 | lib.format(writer, format, args) catch unreachable; 19 | writer.writeByte('\n') catch unreachable; 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /src/cpu/test_runner.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const assert = lib.assert; 3 | const log = lib.log.scoped(.TEST); 4 | const privileged = @import("privileged"); 5 | const QEMU = lib.QEMU; 6 | 7 | const RunAllTestResult = error{ 8 | failure, 9 | }; 10 | 11 | pub fn runAllTests() RunAllTestResult!void { 12 | comptime assert(lib.is_test); 13 | const test_functions = @import("builtin").test_functions; 14 | var failed_test_count: usize = 0; 15 | for (test_functions) |test_function| { 16 | test_function.func() catch |err| { 17 | log.err("Test failed: {}", .{err}); 18 | failed_test_count += 1; 19 | }; 20 | } 21 | 22 | const test_count = test_functions.len; 23 | assert(QEMU.isa_debug_exit.io_size == @sizeOf(u32)); 24 | const success = failed_test_count == 0; 25 | if (success) { 26 | log.info("All {} tests passed.", .{test_count}); 27 | } else { 28 | log.info("Run {} tests. Failed {}.", .{ test_count, failed_test_count }); 29 | } 30 | 31 | privileged.exitFromQEMU(success); 32 | } 33 | -------------------------------------------------------------------------------- /src/host.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | 3 | comptime { 4 | if (lib.os == .freestanding) @compileError("Host file included in non-host target"); 5 | } 6 | 7 | const std = @import("std"); 8 | pub const ChildProcess = std.ChildProcess; 9 | 10 | pub const posix = std.os; 11 | pub const sync = std.os.sync; 12 | 13 | pub const fs = std.fs; 14 | pub const cwd = fs.cwd; 15 | pub const Dir = fs.Dir; 16 | pub const basename = fs.path.basename; 17 | pub const dirname = fs.path.dirname; 18 | 19 | const io = std.io; 20 | pub const getStdOut = std.io.getStdOut; 21 | 22 | const heap = std.heap; 23 | pub const ArenaAllocator = heap.ArenaAllocator; 24 | pub const page_allocator = heap.page_allocator; 25 | 26 | pub const time = std.time; 27 | 28 | pub const ArrayList = std.ArrayList; 29 | pub const ArrayListAligned = std.ArrayListAligned; 30 | 31 | // Build imports 32 | pub const build = std.build; 33 | 34 | pub fn allocateZeroMemory(bytes: u64) ![]align(0x1000) u8 { 35 | switch (lib.os) { 36 | .windows => { 37 | const windows = std.os.windows; 38 | return @ptrCast([*]align(0x1000) u8, @alignCast(0x1000, try windows.VirtualAlloc(null, bytes, windows.MEM_RESERVE | windows.MEM_COMMIT, windows.PAGE_READWRITE)))[0..bytes]; 39 | }, 40 | // Assume all systems are POSIX 41 | else => { 42 | const mmap = std.os.mmap; 43 | const PROT = std.os.PROT; 44 | const MAP = std.os.MAP; 45 | return try mmap(null, bytes, PROT.READ | PROT.WRITE, MAP.PRIVATE | MAP.ANONYMOUS, -1, 0); 46 | }, 47 | .freestanding => @compileError("Not implemented yet"), 48 | } 49 | } 50 | 51 | pub const ExecutionError = error{failed}; 52 | pub fn spawnProcess(arguments: []const []const u8, allocator: lib.ZigAllocator) !void { 53 | var process = ChildProcess.init(arguments, allocator); 54 | process.stdout_behavior = .Ignore; 55 | const execution_result = try process.spawnAndWait(); 56 | 57 | switch (execution_result) { 58 | .Exited => |exit_code| { 59 | switch (exit_code) { 60 | 0 => {}, 61 | else => return ExecutionError.failed, 62 | } 63 | }, 64 | .Signal => |signal_code| { 65 | _ = signal_code; 66 | unreachable; 67 | }, 68 | .Stopped, .Unknown => unreachable, 69 | } 70 | } 71 | 72 | pub const panic = std.debug.panic; 73 | 74 | pub const allocateArguments = std.process.argsAlloc; 75 | -------------------------------------------------------------------------------- /src/host/disk_image_builder.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const assert = lib.assert; 3 | const FAT32 = lib.Filesystem.FAT32; 4 | const PartitionTable = lib.PartitionTable; 5 | const GPT = PartitionTable.GPT; 6 | const MBR = PartitionTable.MBR; 7 | const host = @import("host"); 8 | 9 | pub const ImageDescription = struct { 10 | partition_name: []const u8, 11 | partition_start_lba: u64, 12 | disk_sector_count: u64, 13 | disk_sector_size: u64, 14 | partition_filesystem: lib.FilesystemType, 15 | }; 16 | 17 | pub extern fn deploy(device_path: [*:0]const u8, limine_hdd_ptr: [*]const u8, limine_hdd_len: usize) callconv(.C) c_int; 18 | 19 | const Disk = lib.Disk; 20 | pub const DiskImage = extern struct { 21 | disk: Disk, 22 | buffer_ptr: [*]u8, 23 | 24 | pub fn write(disk: *Disk, bytes: []const u8, sector_offset: u64, commit_memory_to_disk: bool) Disk.WriteError!void { 25 | const need_write = !(disk.type == .memory and !commit_memory_to_disk); 26 | if (need_write) { 27 | const disk_image = @fieldParentPtr(DiskImage, "disk", disk); 28 | assert(disk_image.disk.disk_size > 0); 29 | //assert(disk.disk.partition_count == 1); 30 | assert(bytes.len > 0); 31 | //assert(disk.disk.disk_size == disk.buffer.items.len); 32 | const byte_offset = sector_offset * disk_image.disk.sector_size; 33 | 34 | if (byte_offset + bytes.len > disk_image.disk.disk_size) return Disk.WriteError.disk_size_overflow; 35 | 36 | @memcpy(disk_image.getBuffer()[byte_offset .. byte_offset + bytes.len], bytes); 37 | } 38 | } 39 | 40 | pub fn read(disk: *Disk, sector_count: u64, sector_offset: u64, provided_buffer: ?[]const u8) Disk.ReadError!Disk.ReadResult { 41 | assert(provided_buffer == null); 42 | const disk_image = @fieldParentPtr(DiskImage, "disk", disk); 43 | assert(disk_image.disk.disk_size > 0); 44 | assert(sector_count > 0); 45 | //assert(disk.disk.disk_size == disk.buffer.items.len); 46 | const byte_count = sector_count * disk_image.disk.sector_size; 47 | const byte_offset = sector_offset * disk_image.disk.sector_size; 48 | if (byte_offset + byte_count > disk.disk_size) { 49 | return Disk.ReadError.read_error; 50 | } 51 | return .{ 52 | .buffer = disk_image.getBuffer()[byte_offset .. byte_offset + byte_count].ptr, 53 | .sector_count = sector_count, 54 | }; 55 | } 56 | 57 | pub fn readCache(disk: *Disk, sector_count: u64, sector_offset: u64) Disk.ReadError!Disk.ReadResult { 58 | _ = sector_count; 59 | _ = sector_offset; 60 | _ = disk; 61 | return error.read_error; 62 | } 63 | 64 | pub fn fromZero(sector_count: usize, sector_size: u16) !DiskImage { 65 | const disk_bytes = try host.allocateZeroMemory(sector_count * sector_size); 66 | var disk_image = DiskImage{ 67 | .disk = .{ 68 | .type = .memory, 69 | .callbacks = .{ 70 | .read = DiskImage.read, 71 | .write = DiskImage.write, 72 | .readCache = DiskImage.readCache, 73 | }, 74 | .disk_size = disk_bytes.len, 75 | .sector_size = sector_size, 76 | .cache_size = 0, 77 | }, 78 | .buffer_ptr = disk_bytes.ptr, 79 | }; 80 | 81 | return disk_image; 82 | } 83 | 84 | pub fn createFAT(disk_image: *DiskImage, comptime image: ImageDescription, original_gpt_cache: ?GPT.Partition.Cache) !GPT.Partition.Cache { 85 | const gpt_cache = try GPT.create(&disk_image.disk, if (original_gpt_cache) |o_gpt_cache| o_gpt_cache.gpt.header else null); 86 | const partition_name_u16 = lib.unicode.utf8ToUtf16LeStringLiteral(image.partition_name); 87 | const gpt_partition_cache = try gpt_cache.addPartition(image.partition_filesystem, partition_name_u16, image.partition_start_lba, gpt_cache.header.last_usable_lba, if (original_gpt_cache) |o_gpt_cache| o_gpt_cache.partition else null); 88 | 89 | return gpt_partition_cache; 90 | } 91 | 92 | pub fn fromFile(file_path: []const u8, sector_size: u16, allocator: lib.ZigAllocator) !DiskImage { 93 | const disk_memory = try host.cwd().readFileAlloc(allocator, file_path, lib.maxInt(usize)); 94 | 95 | var disk_image = DiskImage{ 96 | .disk = .{ 97 | .type = .memory, 98 | .callbacks = .{ 99 | .read = DiskImage.read, 100 | .write = DiskImage.write, 101 | .readCache = DiskImage.readCache, 102 | }, 103 | .disk_size = disk_memory.len, 104 | .sector_size = sector_size, 105 | .cache_size = 0, 106 | }, 107 | .buffer_ptr = disk_memory.ptr, 108 | }; 109 | 110 | return disk_image; 111 | } 112 | 113 | const File = struct { 114 | handle: lib.File, 115 | size: usize, 116 | }; 117 | 118 | pub inline fn getBuffer(disk_image: DiskImage) []u8 { 119 | return disk_image.buffer_ptr[0..disk_image.disk.disk_size]; 120 | } 121 | }; 122 | 123 | pub fn format(disk: *Disk, partition_range: Disk.PartitionRange, copy_mbr: ?*const MBR.Partition) !FAT32.Cache { 124 | if (disk.type != .memory) @panic("disk is not memory"); 125 | const fat_partition_mbr_lba = partition_range.first_lba; 126 | const fat_partition_mbr = try disk.readTypedSectors(MBR.Partition, fat_partition_mbr_lba, null, .{}); 127 | 128 | const sectors_per_track = 32; 129 | const total_sector_count_32 = @intCast(u32, lib.alignBackward(u64, partition_range.last_lba - partition_range.first_lba, sectors_per_track)); 130 | const fat_count = FAT32.count; 131 | 132 | var cluster_size: u8 = 1; 133 | const max_cluster_size = 128; 134 | var fat_data_sector_count: u32 = undefined; 135 | var fat_length_32: u32 = undefined; 136 | var cluster_count_32: u32 = undefined; 137 | 138 | while (true) { 139 | assert(cluster_size > 0); 140 | fat_data_sector_count = total_sector_count_32 - lib.alignForward(u32, FAT32.default_reserved_sector_count, cluster_size); 141 | cluster_count_32 = (fat_data_sector_count * disk.sector_size + fat_count * 8) / (cluster_size * disk.sector_size + fat_count * 4); 142 | fat_length_32 = lib.alignForward(u32, cdiv((cluster_count_32 + 2) * 4, disk.sector_size), cluster_size); 143 | cluster_count_32 = (fat_data_sector_count - fat_count * fat_length_32) / cluster_size; 144 | const max_cluster_size_32 = @min(fat_length_32 * disk.sector_size / 4, FAT32.getMaxCluster(.fat32)); 145 | if (cluster_count_32 > max_cluster_size_32) { 146 | cluster_count_32 = 0; 147 | } 148 | if (cluster_count_32 != 0 and cluster_count_32 < FAT32.getMinCluster(.fat32)) { 149 | cluster_count_32 = 0; 150 | } 151 | 152 | if (cluster_count_32 != 0) break; 153 | 154 | cluster_size <<= 1; 155 | 156 | const keep_going = cluster_size != 0 and cluster_size <= max_cluster_size; 157 | if (!keep_going) break; 158 | @panic("unexpected fat32 bug"); 159 | } 160 | 161 | var root_directory_entries: u64 = 0; 162 | _ = root_directory_entries; 163 | 164 | const reserved_sector_count = lib.alignForward(u16, FAT32.default_reserved_sector_count, cluster_size); 165 | 166 | fat_partition_mbr.* = MBR.Partition{ 167 | .bpb = .{ 168 | .dos3_31 = .{ 169 | .dos2_0 = .{ 170 | .jmp_code = .{ 0xeb, 0x58, 0x90 }, 171 | .oem_identifier = "mkfs.fat".*, 172 | .sector_size = disk.sector_size, 173 | .cluster_sector_count = cluster_size, 174 | .reserved_sector_count = reserved_sector_count, 175 | .fat_count = fat_count, 176 | .root_entry_count = 0, 177 | .total_sector_count_16 = 0, 178 | .media_descriptor = 0xf8, 179 | .fat_sector_count_16 = 0, 180 | }, 181 | .physical_sectors_per_track = sectors_per_track, 182 | .disk_head_count = 8, 183 | .hidden_sector_count = @intCast(u32, partition_range.first_lba), 184 | .total_sector_count_32 = total_sector_count_32, 185 | }, 186 | .fat_sector_count_32 = fat_length_32, 187 | .drive_description = 0, 188 | .version = .{ 0, 0 }, 189 | .root_directory_cluster_offset = FAT32.starting_cluster, 190 | .fs_info_sector = FAT32.default_fs_info_sector, 191 | .backup_boot_record_sector = FAT32.default_backup_boot_record_sector, 192 | .drive_number = 0x80, 193 | .extended_boot_signature = 0x29, 194 | .serial_number = if (copy_mbr) |copy_partition_mbr| copy_partition_mbr.bpb.serial_number else @truncate(u32, @intCast(u64, host.time.microTimestamp())), 195 | .volume_label = "NO NAME ".*, 196 | .filesystem_type = "FAT32 ".*, 197 | }, 198 | .code = [_]u8{ 199 | 0xe, 0x1f, 0xbe, 0x77, 0x7c, 0xac, 0x22, 0xc0, 0x74, 0xb, 0x56, 0xb4, 0xe, 0xbb, 0x7, 0x0, 0xcd, 0x10, 0x5e, 0xeb, 0xf0, 0x32, 0xe4, 0xcd, 0x16, 0xcd, 0x19, 0xeb, 0xfe, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x64, 0x69, 0x73, 0x6b, 0x2e, 0x20, 0x20, 0x50, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x20, 0x61, 0x20, 0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x66, 0x6c, 0x6f, 0x70, 0x70, 0x79, 0x20, 0x61, 0x6e, 0x64, 0xd, 0xa, 0x70, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x6b, 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x74, 0x72, 0x79, 0x20, 0x61, 0x67, 0x61, 0x69, 0x6e, 0x20, 0x2e, 0x2e, 0x2e, 0x20, 0xd, 0xa, 200 | } ++ [1]u8{0} ** 227, 201 | // This should be zero 202 | .partitions = lib.zeroes([4]MBR.LegacyPartition), 203 | }; 204 | 205 | try disk.writeTypedSectors(MBR.Partition, fat_partition_mbr, fat_partition_mbr_lba, false); 206 | 207 | const backup_boot_record_sector = partition_range.first_lba + fat_partition_mbr.bpb.backup_boot_record_sector; 208 | const backup_boot_record = try disk.readTypedSectors(MBR.Partition, backup_boot_record_sector, null, .{}); 209 | backup_boot_record.* = fat_partition_mbr.*; 210 | try disk.writeTypedSectors(MBR.Partition, backup_boot_record, backup_boot_record_sector, false); 211 | 212 | const fs_info_lba = partition_range.first_lba + fat_partition_mbr.bpb.fs_info_sector; 213 | const fs_info = try disk.readTypedSectors(FAT32.FSInfo, fs_info_lba, null, .{}); 214 | fs_info.* = .{ 215 | .lead_signature = 0x41615252, 216 | .signature = 0x61417272, 217 | .free_cluster_count = cluster_count_32, 218 | .last_allocated_cluster = 0, 219 | .trail_signature = 0xaa550000, 220 | }; 221 | try disk.writeTypedSectors(FAT32.FSInfo, fs_info, fs_info_lba, false); 222 | 223 | const cache = FAT32.Cache{ 224 | .disk = disk, 225 | .partition_range = partition_range, 226 | .mbr = fat_partition_mbr, 227 | .fs_info = fs_info, 228 | .allocator = null, 229 | }; 230 | 231 | // TODO: write this properly 232 | 233 | try cache.registerCluster(0, FAT32.Entry.reserved_and_should_not_be_used_eof, null); 234 | try cache.registerCluster(1, FAT32.Entry.allocated_and_eof, null); 235 | try cache.registerCluster(2, FAT32.Entry.reserved_and_should_not_be_used_eof, null); 236 | 237 | cache.fs_info.last_allocated_cluster = 2; 238 | cache.fs_info.free_cluster_count = cluster_count_32 - 1; 239 | 240 | const backup_fs_info_lba = backup_boot_record_sector + backup_boot_record.bpb.fs_info_sector; 241 | const backup_fs_info = try disk.readTypedSectors(FAT32.FSInfo, backup_fs_info_lba, null, .{}); 242 | backup_fs_info.* = fs_info.*; 243 | try disk.writeTypedSectors(FAT32.FSInfo, backup_fs_info, backup_fs_info_lba, false); 244 | 245 | return cache; 246 | } 247 | 248 | fn cdiv(a: u32, b: u32) u32 { 249 | return (a + b - 1) / b; 250 | } 251 | -------------------------------------------------------------------------------- /src/host/disk_image_builder/test.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const assert = lib.assert; 3 | const log = lib.log.scoped(.DISK_IMAGE_BUILDER); 4 | const FAT32 = lib.Filesystem.FAT32; 5 | const GPT = lib.PartitionTable.GPT; 6 | const host = @import("host"); 7 | const limine_installer = @import("limine_installer"); 8 | 9 | const disk_image_builder = @import("../disk_image_builder.zig"); 10 | const ImageDescription = disk_image_builder.ImageDescription; 11 | const DiskImage = disk_image_builder.DiskImage; 12 | 13 | const LoopbackDevice = struct { 14 | name: []const u8, 15 | mount_dir: ?[]const u8 = null, 16 | 17 | fn start(loopback_device: *LoopbackDevice, allocator: lib.ZigAllocator, image_path: []const u8) !void { 18 | try host.spawnProcess(&.{ "./tools/loopback_start.sh", image_path, loopback_device.name }, allocator); 19 | } 20 | 21 | fn end(loopback_device: *LoopbackDevice, allocator: lib.ZigAllocator) !void { 22 | loopback_device.mount_dir = null; 23 | try host.spawnProcess(&.{ "./tools/loopback_end.sh", loopback_device.name }, allocator); 24 | try host.cwd().deleteFile(loopback_device.name); 25 | } 26 | 27 | fn mount(loopback_device: *LoopbackDevice, allocator: lib.ZigAllocator, mount_dir: []const u8) !MountedPartition { 28 | try host.cwd().makePath(mount_dir); 29 | try host.spawnProcess(&.{ "./tools/loopback_mount.sh", loopback_device.name, mount_dir }, allocator); 30 | loopback_device.mount_dir = mount_dir; 31 | 32 | return MountedPartition{ 33 | .loopback_device = loopback_device.*, 34 | }; 35 | } 36 | }; 37 | 38 | const MountedPartition = struct { 39 | loopback_device: LoopbackDevice, 40 | 41 | fn mkdir(partition: MountedPartition, allocator: lib.ZigAllocator, dir: []const u8) !void { 42 | try host.spawnProcess(&.{ "sudo", "mkdir", "-p", try partition.join_with_root(allocator, dir) }, allocator); 43 | } 44 | 45 | fn join_with_root(partition: MountedPartition, allocator: lib.ZigAllocator, path: []const u8) ![]const u8 { 46 | const mount_dir = partition.get_mount_dir(); 47 | const slices_to_join: []const []const u8 = if (path[0] == '/') &.{ mount_dir, path } else &.{ mount_dir, "/", path }; 48 | const joint_path = try lib.concat(allocator, u8, slices_to_join); 49 | return joint_path; 50 | } 51 | 52 | pub fn get_mount_dir(partition: MountedPartition) []const u8 { 53 | const mount_dir = partition.loopback_device.mount_dir orelse @panic("get_mount_dir"); 54 | return mount_dir; 55 | } 56 | 57 | fn copy_file(partition: MountedPartition, allocator: lib.ZigAllocator, file_path: []const u8, file_content: []const u8) !void { 58 | // TODO: make this work for Windows? 59 | const last_slash_index = lib.lastIndexOf(u8, file_path, "/") orelse @panic("fat32: copy file last slash"); 60 | const file_name = host.basename(file_path); 61 | assert(file_name.len > 0); 62 | try host.cwd().writeFile(file_name, file_content); 63 | const dir = file_path[0..if (last_slash_index == 0) 1 else last_slash_index]; 64 | const destination_dir = try partition.join_with_root(allocator, dir); 65 | const mkdir_process_args = &.{ "sudo", "mkdir", "-p", destination_dir }; 66 | try host.spawnProcess(mkdir_process_args, allocator); 67 | const copy_process_args = &.{ "sudo", "cp", "-v", file_name, destination_dir }; 68 | try host.spawnProcess(copy_process_args, allocator); 69 | try host.cwd().deleteFile(file_name); 70 | } 71 | 72 | fn end(partition: *MountedPartition, allocator: lib.ZigAllocator) !void { 73 | const mount_dir = partition.loopback_device.mount_dir orelse @panic("mount partition end"); 74 | host.sync(); 75 | try host.spawnProcess(&.{ "sudo", "umount", mount_dir }, allocator); 76 | host.spawnProcess(&.{ "sudo", "rm", "-rf", mount_dir }, allocator) catch |err| { 77 | switch (err) { 78 | host.ExecutionError.failed => {}, 79 | else => return err, 80 | } 81 | }; 82 | } 83 | }; 84 | 85 | const ShellImage = struct { 86 | path: []const u8, 87 | description: ImageDescription, 88 | 89 | fn createFAT(image: ShellImage, allocator: lib.ZigAllocator) !void { 90 | const megabytes = @divExact(image.description.disk_sector_count * image.description.disk_sector_size, lib.mb); 91 | try host.spawnProcess(&.{ "dd", "if=/dev/zero", "bs=1M", "count=0", try lib.allocPrint(allocator, "seek={d}", .{megabytes}), try lib.allocPrint(allocator, "of={s}", .{image.path}) }, allocator); 92 | 93 | try host.spawnProcess(&.{ "parted", "-s", image.path, "mklabel", "gpt" }, allocator); 94 | try host.spawnProcess(&.{ "parted", "-s", image.path, "mkpart", image.description.partition_name, @tagName(image.description.partition_filesystem), try lib.allocPrint(allocator, "{d}s", .{image.description.partition_start_lba}), "100%" }, allocator); 95 | try host.spawnProcess(&.{ "parted", "-s", image.path, "set", "1", "esp", "on" }, allocator); 96 | } 97 | 98 | fn toDiskImage(image: ShellImage, allocator: lib.ZigAllocator) !DiskImage { 99 | return try DiskImage.fromFile(image.path, @intCast(u16, image.description.disk_sector_size), allocator); 100 | } 101 | 102 | fn delete(image: ShellImage) !void { 103 | try host.cwd().deleteFile(image.path); 104 | } 105 | }; 106 | 107 | const File = struct { 108 | path: []const u8, 109 | content: []const u8, 110 | }; 111 | 112 | const limine_directories = [_][]const u8{ 113 | "/EFI", "/EFI/BOOT", 114 | }; 115 | 116 | const limine_files = [_]File{ 117 | .{ .path = "/limine.cfg", .content = @embedFile("../../bootloader/limine/installables/limine.cfg") }, 118 | .{ .path = "/limine.sys", .content = @embedFile("../../bootloader/limine/installables/limine.sys") }, 119 | }; 120 | 121 | test "Limine barebones" { 122 | lib.testing.log_level = .debug; 123 | 124 | // 125 | // Using an arena allocator because it doesn't care about memory leaks 126 | var arena_allocator = host.ArenaAllocator.init(host.page_allocator); 127 | defer arena_allocator.deinit(); 128 | 129 | var wrapped_allocator = lib.Allocator.wrap(arena_allocator.allocator()); 130 | 131 | const deploy_limine = true; 132 | 133 | switch (lib.os) { 134 | .linux => { 135 | const image = ImageDescription{ 136 | .partition_start_lba = 0x800, 137 | .disk_sector_count = 131072, 138 | .disk_sector_size = lib.default_sector_size, 139 | .partition_name = "ESP", 140 | .partition_filesystem = .fat32, 141 | }; 142 | 143 | const test_path = "zig-cache/test_original.hdd"; 144 | const test_image = ShellImage{ 145 | .path = test_path, 146 | .description = image, 147 | }; 148 | test_image.delete() catch {}; 149 | 150 | try test_image.createFAT(wrapped_allocator.zigUnwrap()); 151 | if (deploy_limine and disk_image_builder.deploy(test_path, &limine_installer.hdd, limine_installer.hdd.len) != 0) { 152 | @panic("asjdkajsd"); 153 | } 154 | 155 | var loopback_device = LoopbackDevice{ .name = "loopback_device" }; 156 | try loopback_device.start(wrapped_allocator.zigUnwrap(), test_path); 157 | 158 | log.debug("Formatting", .{}); 159 | try host.spawnProcess(&.{ "./tools/format_loopback_fat32.sh", loopback_device.name }, wrapped_allocator.zigUnwrap()); 160 | 161 | const mount_dir = "image_mount"; 162 | 163 | var partition = try loopback_device.mount(wrapped_allocator.zigUnwrap(), mount_dir); 164 | 165 | for (limine_directories) |directory| { 166 | try partition.mkdir(wrapped_allocator.zigUnwrap(), directory); 167 | } 168 | 169 | for (limine_files) |file| { 170 | try partition.copy_file(wrapped_allocator.zigUnwrap(), file.path, file.content); 171 | } 172 | 173 | try partition.end(wrapped_allocator.zigUnwrap()); 174 | try loopback_device.end(wrapped_allocator.zigUnwrap()); 175 | 176 | var original_disk_image = try test_image.toDiskImage(wrapped_allocator.zigUnwrap()); 177 | const original_gpt_cache = try GPT.Partition.Cache.fromPartitionIndex(&original_disk_image.disk, 0, wrapped_allocator.unwrap()); 178 | const original_fat_cache = try FAT32.Cache.fromGPTPartitionCache(wrapped_allocator.unwrap(), original_gpt_cache); 179 | 180 | var disk_image = try DiskImage.fromZero(image.disk_sector_count, image.disk_sector_size); 181 | const gpt_partition_cache = try disk_image.createFAT(image, original_gpt_cache); 182 | 183 | const original_buffer = original_disk_image.getBuffer(); 184 | const my_buffer = disk_image.getBuffer(); 185 | 186 | if (deploy_limine) { 187 | try limine_installer.install(my_buffer, false, null); 188 | } 189 | 190 | const fat_partition_cache = try disk_image_builder.format(gpt_partition_cache.gpt.disk, .{ 191 | .first_lba = gpt_partition_cache.partition.first_lba, 192 | .last_lba = gpt_partition_cache.partition.last_lba, 193 | }, original_fat_cache.mbr); 194 | 195 | for (limine_directories) |directory| { 196 | log.debug("Creating directory: {s}", .{directory}); 197 | try fat_partition_cache.makeNewDirectory(directory, null, original_fat_cache, @intCast(u64, host.time.milliTimestamp())); 198 | } 199 | 200 | for (limine_files) |file| { 201 | log.debug("Creating file: {s}", .{file.path}); 202 | try fat_partition_cache.makeNewFile(file.path, file.content, wrapped_allocator.unwrap(), original_fat_cache, @intCast(u64, host.time.milliTimestamp())); 203 | } 204 | 205 | var diff_count: u32 = 0; 206 | for (my_buffer, 0..) |mb, i| { 207 | const ob = original_buffer[i]; 208 | const diff = ob != mb; 209 | if (diff) { 210 | log.debug("[0x{x}] Diff. Expected: 0x{x}. Actual: 0x{x}", .{ i, ob, mb }); 211 | } 212 | diff_count += @boolToInt(diff); 213 | } 214 | 215 | if (diff_count > 0) { 216 | log.err("Diff count: {}", .{diff_count}); 217 | } 218 | try lib.testing.expectEqualSlices(u8, original_buffer, my_buffer); 219 | 220 | try test_image.delete(); 221 | }, 222 | else => {}, 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /src/host/test.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const host = @import("host"); 3 | test "Host tests" { 4 | _ = lib; 5 | _ = host; 6 | } 7 | -------------------------------------------------------------------------------- /src/lib/arch.zig: -------------------------------------------------------------------------------- 1 | comptime { 2 | const os = @import("builtin").os.tag; 3 | switch (os) { 4 | .uefi, .freestanding => {}, 5 | else => @compileError("This file is not to be compiled with this OS"), 6 | } 7 | } 8 | 9 | pub const current = switch (@import("builtin").cpu.arch) { 10 | .x86 => x86, 11 | .x86_64 => x86_64, 12 | else => @compileError("Architecture not supported"), 13 | }; 14 | 15 | pub const x86 = @import("arch/x86.zig"); 16 | pub const x86_64 = @import("arch/x86_64.zig"); 17 | 18 | pub const default_page_size = current.default_page_size; 19 | pub const reasonable_page_size = current.reasonable_page_size; 20 | 21 | pub const valid_page_sizes = current.valid_page_sizes; 22 | pub const reverse_valid_page_sizes = current.reverse_valid_page_sizes; 23 | 24 | pub fn page_shifter(comptime asked_page_size: comptime_int) comptime_int { 25 | return @ctz(@as(u32, asked_page_size)); 26 | } 27 | 28 | pub fn page_mask(comptime asked_page_size: comptime_int) comptime_int { 29 | return asked_page_size - 1; 30 | } 31 | 32 | pub const Spinlock = current.Spinlock; 33 | 34 | pub const stack_alignment = current.stack_alignment; 35 | -------------------------------------------------------------------------------- /src/lib/arch/x86.zig: -------------------------------------------------------------------------------- 1 | const x86 = @import("x86/common.zig"); 2 | pub usingnamespace x86; 3 | 4 | pub const default_page_size = valid_page_sizes[0]; 5 | pub const reasonable_page_size = valid_page_sizes[1]; 6 | pub const valid_page_sizes = [2]comptime_int{ 0x1000, 0x1000 * 0x200 }; 7 | -------------------------------------------------------------------------------- /src/lib/arch/x86/64/registers.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const assert = lib.assert; 3 | 4 | pub const RFLAGS = packed struct(u64) { 5 | CF: bool = false, 6 | reserved0: bool = true, 7 | PF: bool = false, 8 | reserved1: bool = false, 9 | AF: bool = false, 10 | reserved2: bool = false, 11 | ZF: bool = false, 12 | SF: bool = false, 13 | TF: bool = false, 14 | IF: bool = false, 15 | DF: bool = false, 16 | OF: bool = false, 17 | IOPL: u2 = 0, 18 | NT: bool = false, 19 | reserved3: bool = false, 20 | RF: bool = false, 21 | VM: bool = false, 22 | AC: bool = false, 23 | VIF: bool = false, 24 | VIP: bool = false, 25 | ID: bool = false, 26 | reserved4: u10 = 0, 27 | reserved5: u32 = 0, 28 | 29 | comptime { 30 | assert(@sizeOf(RFLAGS) == @sizeOf(u64)); 31 | assert(@bitSizeOf(RFLAGS) == @bitSizeOf(u64)); 32 | } 33 | 34 | pub inline fn read() RFLAGS { 35 | return asm volatile ( 36 | \\pushfq 37 | \\pop %[flags] 38 | : [flags] "=r" (-> RFLAGS), 39 | : 40 | : "memory" 41 | ); 42 | } 43 | 44 | pub fn user(rflags: RFLAGS) RFLAGS { 45 | return RFLAGS{ 46 | .IF = true, 47 | .CF = rflags.CF, 48 | .PF = rflags.PF, 49 | .AF = rflags.AF, 50 | .ZF = rflags.ZF, 51 | .SF = rflags.SF, 52 | .DF = rflags.DF, 53 | .OF = rflags.OF, 54 | }; 55 | } 56 | }; 57 | 58 | pub const SimpleRegister = enum { 59 | rax, 60 | rbx, 61 | rcx, 62 | rdx, 63 | rbp, 64 | rsp, 65 | rsi, 66 | rdi, 67 | r8, 68 | r9, 69 | r10, 70 | r11, 71 | r12, 72 | r13, 73 | r14, 74 | r15, 75 | 76 | gs, 77 | cs, 78 | 79 | dr0, 80 | dr1, 81 | dr2, 82 | dr3, 83 | dr4, 84 | dr5, 85 | dr6, 86 | dr7, 87 | 88 | cr2, 89 | cr8, 90 | }; 91 | 92 | pub fn SimpleR64(comptime Register: SimpleRegister) type { 93 | return struct { 94 | pub inline fn read() u64 { 95 | return switch (Register) { 96 | .rax => asm volatile ("mov %rax, %[result]" 97 | : [result] "=r" (-> u64), 98 | ), 99 | .rbx => asm volatile ("mov %rbx, %[result]" 100 | : [result] "=r" (-> u64), 101 | ), 102 | .rcx => asm volatile ("mov %rcx, %[result]" 103 | : [result] "=r" (-> u64), 104 | ), 105 | .rdx => asm volatile ("mov %rdx, %[result]" 106 | : [result] "=r" (-> u64), 107 | ), 108 | .rbp => asm volatile ("mov %rbp, %[result]" 109 | : [result] "=r" (-> u64), 110 | ), 111 | .rsp => asm volatile ("mov %rsp, %[result]" 112 | : [result] "=r" (-> u64), 113 | ), 114 | .rsi => asm volatile ("mov %rsi, %[result]" 115 | : [result] "=r" (-> u64), 116 | ), 117 | .rdi => asm volatile ("mov %rdi, %[result]" 118 | : [result] "=r" (-> u64), 119 | ), 120 | .r8 => asm volatile ("mov %r8, %[result]" 121 | : [result] "=r" (-> u64), 122 | ), 123 | .r9 => asm volatile ("mov %r9, %[result]" 124 | : [result] "=r" (-> u64), 125 | ), 126 | .r10 => asm volatile ("mov %r10, %[result]" 127 | : [result] "=r" (-> u64), 128 | ), 129 | .r11 => asm volatile ("mov %r11, %[result]" 130 | : [result] "=r" (-> u64), 131 | ), 132 | .r12 => asm volatile ("mov %r12, %[result]" 133 | : [result] "=r" (-> u64), 134 | ), 135 | .r13 => asm volatile ("mov %r13, %[result]" 136 | : [result] "=r" (-> u64), 137 | ), 138 | .r14 => asm volatile ("mov %r14, %[result]" 139 | : [result] "=r" (-> u64), 140 | ), 141 | .r15 => asm volatile ("mov %r15, %[result]" 142 | : [result] "=r" (-> u64), 143 | ), 144 | .gs => asm volatile ("mov %gs, %[result]" 145 | : [result] "=r" (-> u64), 146 | : 147 | : "memory" 148 | ), 149 | .cs => asm volatile ("mov %cs, %[result]" 150 | : [result] "=r" (-> u64), 151 | : 152 | : "memory" 153 | ), 154 | .dr0 => asm volatile ("mov %dr0, %[result]" 155 | : [result] "=r" (-> u64), 156 | ), 157 | .dr1 => asm volatile ("mov %dr1, %[result]" 158 | : [result] "=r" (-> u64), 159 | ), 160 | .dr2 => asm volatile ("mov %dr2, %[result]" 161 | : [result] "=r" (-> u64), 162 | ), 163 | .dr3 => asm volatile ("mov %dr3, %[result]" 164 | : [result] "=r" (-> u64), 165 | ), 166 | .dr4 => asm volatile ("mov %dr4, %[result]" 167 | : [result] "=r" (-> u64), 168 | ), 169 | .dr5 => asm volatile ("mov %dr5, %[result]" 170 | : [result] "=r" (-> u64), 171 | ), 172 | .dr6 => asm volatile ("mov %dr6, %[result]" 173 | : [result] "=r" (-> u64), 174 | ), 175 | .dr7 => asm volatile ("mov %dr7, %[result]" 176 | : [result] "=r" (-> u64), 177 | ), 178 | .cr2 => asm volatile ("mov %cr2, %[result]" 179 | : [result] "=r" (-> u64), 180 | : 181 | : "memory" 182 | ), 183 | .cr8 => asm volatile ("mov %cr8, %[result]" 184 | : [result] "=r" (-> u64), 185 | : 186 | : "memory" 187 | ), 188 | }; 189 | } 190 | 191 | pub inline fn write(value: u64) void { 192 | switch (Register) { 193 | .rax => asm volatile ("mov %[in], %rax" 194 | : 195 | : [in] "r" (value), 196 | ), 197 | .rbx => asm volatile ("mov %[in], %rbx" 198 | : 199 | : [in] "r" (value), 200 | ), 201 | .rcx => asm volatile ("mov %[in], %rcx" 202 | : 203 | : [in] "r" (value), 204 | ), 205 | .rdx => asm volatile ("mov %[in], %rdx" 206 | : 207 | : [in] "r" (value), 208 | ), 209 | .rbp => asm volatile ("mov %[in], %rbp" 210 | : 211 | : [in] "r" (value), 212 | ), 213 | .rsp => asm volatile ("mov %[in], %rsp" 214 | : 215 | : [in] "r" (value), 216 | ), 217 | .rsi => asm volatile ("mov %[in], %rsi" 218 | : 219 | : [in] "r" (value), 220 | ), 221 | .rdi => asm volatile ("mov %[in], %rdi" 222 | : 223 | : [in] "r" (value), 224 | ), 225 | .r8 => asm volatile ("mov %[in], %r8" 226 | : 227 | : [in] "r" (value), 228 | ), 229 | .r9 => asm volatile ("mov %[in], %r9" 230 | : 231 | : [in] "r" (value), 232 | ), 233 | .r10 => asm volatile ("mov %[in], %r10" 234 | : 235 | : [in] "r" (value), 236 | ), 237 | .r11 => asm volatile ("mov %[in], %r11" 238 | : 239 | : [in] "r" (value), 240 | ), 241 | .r12 => asm volatile ("mov %[in], %r12" 242 | : 243 | : [in] "r" (value), 244 | ), 245 | .r13 => asm volatile ("mov %[in], %r13" 246 | : 247 | : [in] "r" (value), 248 | ), 249 | .r14 => asm volatile ("mov %[in], %r14" 250 | : 251 | : [in] "r" (value), 252 | ), 253 | .r15 => asm volatile ("mov %[in], %r15" 254 | : 255 | : [in] "r" (value), 256 | ), 257 | .gs => asm volatile ("mov %[in], %gs" 258 | : 259 | : [in] "r" (value), 260 | : "memory" 261 | ), 262 | .cs => asm volatile ("mov %[in], %cs" 263 | : 264 | : [in] "r" (value), 265 | : "memory" 266 | ), 267 | .dr0 => asm volatile ("mov %[in], %dr0" 268 | : 269 | : [in] "r" (value), 270 | ), 271 | .dr1 => asm volatile ("mov %[in], %dr1" 272 | : 273 | : [in] "r" (value), 274 | ), 275 | .dr2 => asm volatile ("mov %[in], %dr2" 276 | : 277 | : [in] "r" (value), 278 | ), 279 | .dr3 => asm volatile ("mov %[in], %dr3" 280 | : 281 | : [in] "r" (value), 282 | ), 283 | .dr4 => asm volatile ("mov %[in], %dr4" 284 | : 285 | : [in] "r" (value), 286 | ), 287 | .dr5 => asm volatile ("mov %[in], %dr5" 288 | : 289 | : [in] "r" (value), 290 | ), 291 | .dr6 => asm volatile ("mov %[in], %dr6" 292 | : 293 | : [in] "r" (value), 294 | ), 295 | .dr7 => asm volatile ("mov %[in], %dr7" 296 | : 297 | : [in] "r" (value), 298 | ), 299 | .cr2 => asm volatile ("mov %[in], %cr2" 300 | : 301 | : [in] "r" (value), 302 | : "memory" 303 | ), 304 | .cr8 => asm volatile ("mov %[in], %cr8" 305 | : 306 | : [in] "r" (value), 307 | : "memory" 308 | ), 309 | } 310 | } 311 | }; 312 | } 313 | 314 | pub const ComplexRegister = enum { cr0, cr3, cr4 }; 315 | 316 | pub const rax = SimpleR64(.rax); 317 | pub const rbx = SimpleR64(.rbx); 318 | pub const rcx = SimpleR64(.rcx); 319 | pub const rdx = SimpleR64(.rdx); 320 | pub const rbp = SimpleR64(.rbp); 321 | pub const rsp = SimpleR64(.rsp); 322 | pub const rsi = SimpleR64(.rsi); 323 | pub const rdi = SimpleR64(.rdi); 324 | pub const r8 = SimpleR64(.r8); 325 | pub const r9 = SimpleR64(.r9); 326 | pub const r10 = SimpleR64(.r10); 327 | pub const r11 = SimpleR64(.r11); 328 | pub const r12 = SimpleR64(.r12); 329 | pub const r13 = SimpleR64(.r13); 330 | pub const r14 = SimpleR64(.r14); 331 | pub const r15 = SimpleR64(.r15); 332 | 333 | pub const gs = SimpleR64(.gs); 334 | pub const cs = SimpleR64(.cs); 335 | 336 | pub const dr0 = SimpleR64(.dr0); 337 | pub const dr1 = SimpleR64(.dr1); 338 | pub const dr2 = SimpleR64(.dr2); 339 | pub const dr3 = SimpleR64(.dr3); 340 | pub const dr4 = SimpleR64(.dr4); 341 | pub const dr5 = SimpleR64(.dr5); 342 | pub const dr6 = SimpleR64(.dr6); 343 | pub const dr7 = SimpleR64(.dr7); 344 | -------------------------------------------------------------------------------- /src/lib/arch/x86/common.zig: -------------------------------------------------------------------------------- 1 | pub const CPUID = extern struct { 2 | eax: u32, 3 | ebx: u32, 4 | edx: u32, 5 | ecx: u32, 6 | }; 7 | 8 | pub inline fn cpuid(leaf: u32) CPUID { 9 | var eax: u32 = undefined; 10 | var ebx: u32 = undefined; 11 | var edx: u32 = undefined; 12 | var ecx: u32 = undefined; 13 | 14 | asm volatile ( 15 | \\cpuid 16 | : [eax] "={eax}" (eax), 17 | [ebx] "={ebx}" (ebx), 18 | [edx] "={edx}" (edx), 19 | [ecx] "={ecx}" (ecx), 20 | : [leaf] "{eax}" (leaf), 21 | ); 22 | 23 | return CPUID{ 24 | .eax = eax, 25 | .ebx = ebx, 26 | .edx = edx, 27 | .ecx = ecx, 28 | }; 29 | } 30 | 31 | pub const Spinlock = enum(u8) { 32 | released = 0, 33 | acquired = 1, 34 | 35 | pub inline fn acquire(spinlock: *volatile Spinlock) void { 36 | asm volatile ( 37 | \\0: 38 | \\xchgb %[value], %[spinlock] 39 | \\test %[value], %[value] 40 | \\jz 2f 41 | // If not acquire, go to spinloop 42 | \\1: 43 | \\pause 44 | \\cmp %[value], %[spinlock] 45 | // Retry 46 | \\jne 0b 47 | \\jmp 1b 48 | \\2: 49 | : 50 | : [spinlock] "*p" (spinlock), 51 | [value] "r" (Spinlock.acquired), 52 | : "memory" 53 | ); 54 | } 55 | 56 | pub inline fn release(spinlock: *volatile Spinlock) void { 57 | @atomicStore(Spinlock, spinlock, .released, .Release); 58 | } 59 | }; 60 | -------------------------------------------------------------------------------- /src/lib/arch/x86_64.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const x86 = @import("x86/common.zig"); 3 | pub usingnamespace x86; 4 | 5 | pub const valid_page_sizes = [3]comptime_int{ 0x1000, 0x1000 * 0x200, 0x1000 * 0x200 * 0x200 }; 6 | pub const reverse_valid_page_sizes = blk: { 7 | var reverse = valid_page_sizes; 8 | lib.reverse(@TypeOf(valid_page_sizes[0]), &reverse); 9 | // var reverse_u64: [valid_page_sizes.len]u64 = undefined; 10 | // for (reverse, &reverse_u64) |r_el, *ru64_el| { 11 | // ru64_el.* = r_el; 12 | // } 13 | 14 | break :blk reverse; 15 | }; 16 | pub const default_page_size = valid_page_sizes[0]; 17 | pub const reasonable_page_size = valid_page_sizes[1]; 18 | 19 | pub const registers = @import("x86/64/registers.zig"); 20 | 21 | pub inline fn readTimestamp() u64 { 22 | var edx: u32 = undefined; 23 | var eax: u32 = undefined; 24 | 25 | asm volatile ( 26 | \\rdtsc 27 | : [eax] "={eax}" (eax), 28 | [edx] "={edx}" (edx), 29 | ); 30 | 31 | return @as(u64, edx) << 32 | eax; 32 | } 33 | 34 | pub const stack_alignment = 0x10; 35 | -------------------------------------------------------------------------------- /src/lib/config.zig: -------------------------------------------------------------------------------- 1 | pub const cpu_driver_higher_half_address = 0xffff_8000_0000_0000; 2 | pub const cpu_driver_start = 0xffff_ffff_8000_0000; 3 | pub const real_hardware = false; 4 | pub const safe_slow = false; 5 | pub const timeslicing = true; 6 | pub const enable_smp = false; 7 | -------------------------------------------------------------------------------- /src/lib/crc32.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | 3 | pub fn compute(bytes: []const u8) u32 { 4 | var result: u32 = lib.maxInt(u32); 5 | for (bytes) |byte| { 6 | result = (result >> 8) ^ table[@truncate(u8, result ^ byte)]; 7 | } 8 | 9 | result ^= lib.maxInt(u32); 10 | return result; 11 | } 12 | 13 | const table = [_]u32{ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; 14 | -------------------------------------------------------------------------------- /src/lib/disk.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | 3 | const FAT32 = lib.Filesystem.FAT32; 4 | const GPT = lib.PartitionTable.GPT; 5 | const MBR = lib.PartitionTable.MBR; 6 | 7 | const ArrayListAligned = lib.ArrayListAligned; 8 | const assert = lib.assert; 9 | const asBytes = lib.asBytes; 10 | const log = lib.log.scoped(.Disk); 11 | const sliceAsBytes = lib.sliceAsBytes; 12 | 13 | pub const Disk = extern struct { 14 | type: Type, 15 | disk_size: u64, 16 | partition_sizes: [GPT.default_max_partition_count]u64 = [1]u64{0} ** GPT.default_max_partition_count, 17 | cache_size: u16, 18 | sector_size: u16, 19 | callbacks: Callbacks, 20 | 21 | pub const Type = lib.DiskType; 22 | 23 | pub const ReadFn = fn (disk: *Disk, sector_count: u64, sector_offset: u64, provided_buffer: ?[]u8) ReadError!ReadResult; 24 | pub const ReadError = error{ 25 | read_error, 26 | }; 27 | pub const ReadResult = extern struct { 28 | sector_count: u64, 29 | buffer: [*]u8, 30 | }; 31 | 32 | pub const ReadCacheFn = fn (disk: *Disk, sector_count: u64, sector_offset: u64) ReadError!ReadResult; 33 | 34 | pub const WriteFn = fn (disk: *Disk, bytes: []const u8, sector_offset: u64, commit_memory_to_disk: bool) WriteError!void; 35 | pub const WriteError = error{ 36 | not_supported, 37 | disk_size_overflow, 38 | }; 39 | 40 | pub const Callbacks = extern struct { 41 | read: *const ReadFn, 42 | write: *const WriteFn, 43 | readCache: *const ReadCacheFn, 44 | }; 45 | 46 | pub inline fn getProvidedBuffer(disk: *Disk, comptime T: type, count: usize, allocator: ?*lib.Allocator, force: bool) !?[]u8 { 47 | if ((disk.type == .memory and force) or (disk.type != .memory)) { 48 | if (allocator) |alloc| { 49 | const size = @sizeOf(T) * count; 50 | const alignment = @alignOf(T); 51 | const result = try alloc.allocateBytes(size, alignment); 52 | const slice = @intToPtr([*]u8, @intCast(usize, result.address))[0..@intCast(usize, result.size)]; 53 | if (slice.len != size) @panic("WTQSAD/jasD"); 54 | return slice; 55 | } 56 | } 57 | 58 | return null; 59 | } 60 | 61 | const AdvancedReadOptions = packed struct(u8) { 62 | force: bool = false, 63 | reserved: u7 = 0, 64 | }; 65 | 66 | pub fn readTypedSectors(disk: *Disk, comptime T: type, sector_offset: u64, allocator: ?*lib.Allocator, options: AdvancedReadOptions) !*T { 67 | const sector_count = @divExact(@sizeOf(T), disk.sector_size); 68 | const provided_buffer = try disk.getProvidedBuffer(T, 1, allocator, options.force); 69 | const read_result = try disk.callbacks.read(disk, sector_count, sector_offset, provided_buffer); 70 | if (read_result.sector_count != sector_count) @panic("Sector count mismatch"); 71 | // Don't need to write back since it's a memory disk 72 | const result = @ptrCast(*T, @alignCast(@alignOf(T), read_result.buffer)); 73 | return result; 74 | } 75 | 76 | pub inline fn writeTypedSectors(disk: *Disk, comptime T: type, content: *T, sector_offset: u64, commit_memory_to_disk: bool) !void { 77 | try disk.callbacks.write(disk, asBytes(content), sector_offset, commit_memory_to_disk); 78 | } 79 | 80 | pub inline fn readSlice(disk: *Disk, comptime T: type, len: usize, sector_offset: u64, allocator: ?*lib.Allocator, options: AdvancedReadOptions) ![]T { 81 | const element_count_per_sector = @divExact(disk.sector_size, @sizeOf(T)); 82 | const sector_count = @divExact(len, element_count_per_sector); 83 | const provided_buffer = try disk.getProvidedBuffer(T, len, allocator, options.force); 84 | const read_result = try disk.callbacks.read(disk, sector_count, sector_offset, provided_buffer); 85 | if (read_result.sector_count != sector_count) @panic("read_slice: sector count mismatch"); 86 | const result = @ptrCast([*]T, @alignCast(@alignOf(T), read_result.buffer))[0..len]; 87 | return result; 88 | } 89 | 90 | pub inline fn writeSlice(disk: *Disk, comptime T: type, slice: []const T, sector_offset: u64, commit_memory_to_disk: bool) !void { 91 | const byte_slice = sliceAsBytes(slice); 92 | try disk.callbacks.write(disk, byte_slice, sector_offset, commit_memory_to_disk); 93 | } 94 | 95 | pub fn verify(disk: *Disk) !void { 96 | const mbr = try disk.read_typed_sectors(MBR.Struct, 0); 97 | try mbr.verify(disk); 98 | unreachable; 99 | } 100 | 101 | pub const Work = struct { 102 | sector_offset: u64, 103 | sector_count: u64, 104 | operation: Operation, 105 | }; 106 | 107 | pub const Operation = enum(u1) { 108 | read = 0, 109 | write = 1, 110 | 111 | // This is used by NVMe and AHCI, so it is needed to match these values 112 | comptime { 113 | assert(@bitSizeOf(Operation) == @bitSizeOf(u1)); 114 | assert(@enumToInt(Operation.read) == 0); 115 | assert(@enumToInt(Operation.write) == 1); 116 | } 117 | }; 118 | 119 | pub const PartitionRange = extern struct { 120 | first_lba: u64, 121 | last_lba: u64, 122 | }; 123 | }; 124 | -------------------------------------------------------------------------------- /src/lib/extern_enum_array.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | // ENUM ARRAY 3 | pub fn IndexedArray(comptime I: type, comptime V: type, comptime Ext: fn (type) type) type { 4 | comptime ensureIndexer(I); 5 | return extern struct { 6 | const Self = @This(); 7 | 8 | pub usingnamespace Ext(Self); 9 | 10 | /// The index mapping for this map 11 | pub const Indexer = I; 12 | /// The key type used to index this map 13 | pub const Key = Indexer.Key; 14 | /// The value type stored in this map 15 | pub const Value = V; 16 | /// The number of possible keys in the map 17 | pub const len = Indexer.count; 18 | 19 | values: [Indexer.count]Value, 20 | 21 | pub fn initUndefined() Self { 22 | return Self{ .values = undefined }; 23 | } 24 | 25 | pub fn initFill(v: Value) Self { 26 | var self: Self = undefined; 27 | lib.set(Value, &self.values, v); 28 | return self; 29 | } 30 | 31 | /// Returns the value in the array associated with a key. 32 | pub fn get(self: Self, key: Key) Value { 33 | return self.values[Indexer.indexOf(key)]; 34 | } 35 | 36 | /// Returns a pointer to the slot in the array associated with a key. 37 | pub fn getPtr(self: *Self, key: Key) *Value { 38 | return &self.values[Indexer.indexOf(key)]; 39 | } 40 | 41 | /// Returns a const pointer to the slot in the array associated with a key. 42 | pub fn getPtrConst(self: *const Self, key: Key) *const Value { 43 | return &self.values[Indexer.indexOf(key)]; 44 | } 45 | 46 | /// Sets the value in the slot associated with a key. 47 | pub fn set(self: *Self, key: Key, value: Value) void { 48 | self.values[Indexer.indexOf(key)] = value; 49 | } 50 | 51 | /// Iterates over the items in the array, in index order. 52 | pub fn iterator(self: *Self) Iterator { 53 | return .{ 54 | .values = &self.values, 55 | }; 56 | } 57 | 58 | /// An entry in the array. 59 | pub const Entry = extern struct { 60 | /// The key associated with this entry. 61 | /// Modifying this key will not change the array. 62 | key: Key, 63 | 64 | /// A pointer to the value in the array associated 65 | /// with this key. Modifications through this 66 | /// pointer will modify the underlying data. 67 | value: *Value, 68 | }; 69 | 70 | pub const Iterator = extern struct { 71 | index: usize = 0, 72 | values: *[Indexer.count]Value, 73 | 74 | pub fn next(self: *Iterator) ?Entry { 75 | const index = self.index; 76 | if (index < Indexer.count) { 77 | self.index += 1; 78 | return Entry{ 79 | .key = Indexer.keyForIndex(index), 80 | .value = &self.values[index], 81 | }; 82 | } 83 | return null; 84 | } 85 | }; 86 | }; 87 | } 88 | 89 | pub fn ensureIndexer(comptime T: type) void { 90 | comptime { 91 | if (!@hasDecl(T, "Key")) @compileError("Indexer must have decl Key: type."); 92 | if (@TypeOf(T.Key) != type) @compileError("Indexer.Key must be a type."); 93 | if (!@hasDecl(T, "count")) @compileError("Indexer must have decl count: usize."); 94 | if (@TypeOf(T.count) != usize) @compileError("Indexer.count must be a usize."); 95 | if (!@hasDecl(T, "indexOf")) @compileError("Indexer.indexOf must be a fn(Key)usize."); 96 | if (@TypeOf(T.indexOf) != fn (T.Key) usize) @compileError("Indexer must have decl indexOf: fn(Key)usize."); 97 | if (!@hasDecl(T, "keyForIndex")) @compileError("Indexer must have decl keyForIndex: fn(usize)Key."); 98 | if (@TypeOf(T.keyForIndex) != fn (usize) T.Key) @compileError("Indexer.keyForIndex must be a fn(usize)Key."); 99 | } 100 | } 101 | 102 | pub fn EnumArray(comptime E: type, comptime V: type) type { 103 | const mixin = extern struct { 104 | fn EnumArrayExt(comptime Self: type) type { 105 | const Indexer = Self.Indexer; 106 | return extern struct { 107 | /// Initializes all values in the enum array 108 | pub fn init(init_values: EnumFieldStruct(E, V, @as(?V, null))) Self { 109 | return initDefault(@as(?V, null), init_values); 110 | } 111 | 112 | /// Initializes values in the enum array, with the specified default. 113 | pub fn initDefault(comptime default: ?V, init_values: EnumFieldStruct(E, V, default)) Self { 114 | var result = Self{ .values = undefined }; 115 | comptime var i: usize = 0; 116 | inline while (i < Self.len) : (i += 1) { 117 | const key = comptime Indexer.keyForIndex(i); 118 | const tag = @tagName(key); 119 | result.values[i] = @field(init_values, tag); 120 | } 121 | return result; 122 | } 123 | }; 124 | } 125 | }; 126 | return IndexedArray(EnumIndexer(E), V, mixin.EnumArrayExt); 127 | } 128 | const EnumField = lib.Type.EnumField; 129 | pub fn EnumIndexer(comptime E: type) type { 130 | if (!@typeInfo(E).Enum.is_exhaustive) { 131 | @compileError("Cannot create an enum indexer for a non-exhaustive enum."); 132 | } 133 | 134 | const const_fields = lib.fields(E); 135 | var fields = const_fields[0..const_fields.len].*; 136 | if (fields.len == 0) { 137 | return extern struct { 138 | pub const Key = E; 139 | pub const count: usize = 0; 140 | pub fn indexOf(e: E) usize { 141 | _ = e; 142 | unreachable; 143 | } 144 | pub fn keyForIndex(i: usize) E { 145 | _ = i; 146 | unreachable; 147 | } 148 | }; 149 | } 150 | lib.sort(lib.Type.EnumField, &fields, {}, ascByValue); 151 | const min = fields[0].value; 152 | const max = fields[fields.len - 1].value; 153 | const fields_len = fields.len; 154 | if (max - min == fields.len - 1) { 155 | return extern struct { 156 | pub const Key = E; 157 | pub const count = fields_len; 158 | pub fn indexOf(e: E) usize { 159 | return @intCast(usize, @enumToInt(e) - min); 160 | } 161 | pub fn keyForIndex(i: usize) E { 162 | // TODO fix addition semantics. This calculation 163 | // gives up some safety to avoid artificially limiting 164 | // the range of signed enum values to max_isize. 165 | const enum_value = if (min < 0) @bitCast(isize, i) +% min else i + min; 166 | return @intToEnum(E, @intCast(lib.Tag(E), enum_value)); 167 | } 168 | }; 169 | } 170 | 171 | const keys = valuesFromFields(E, &fields); 172 | 173 | return extern struct { 174 | pub const Key = E; 175 | pub const count = fields_len; 176 | pub fn indexOf(e: E) usize { 177 | for (keys, 0..) |k, i| { 178 | if (k == e) return i; 179 | } 180 | unreachable; 181 | } 182 | pub fn keyForIndex(i: usize) E { 183 | return keys[i]; 184 | } 185 | }; 186 | } 187 | pub fn EnumFieldStruct(comptime E: type, comptime Data: type, comptime field_default: ?Data) type { 188 | const StructField = lib.builtin.Type.StructField; 189 | var fields: []const StructField = &[_]StructField{}; 190 | for (lib.fields(E)) |field| { 191 | fields = fields ++ &[_]StructField{.{ 192 | .name = field.name, 193 | .type = Data, 194 | .default_value = if (field_default) |d| @ptrCast(?*const anyopaque, &d) else null, 195 | .is_comptime = false, 196 | .alignment = if (@sizeOf(Data) > 0) @alignOf(Data) else 0, 197 | }}; 198 | } 199 | return @Type(.{ .Struct = .{ 200 | .layout = .Extern, 201 | .fields = fields, 202 | .decls = &.{}, 203 | .is_tuple = false, 204 | } }); 205 | } 206 | 207 | fn ascByValue(ctx: void, comptime a: EnumField, comptime b: EnumField) bool { 208 | _ = ctx; 209 | return a.value < b.value; 210 | } 211 | pub fn valuesFromFields(comptime E: type, comptime fields: []const EnumField) []const E { 212 | comptime { 213 | var result: [fields.len]E = undefined; 214 | for (fields, 0..) |f, i| { 215 | result[i] = @field(E, f.name); 216 | } 217 | return &result; 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /src/lib/filesystem.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | pub const FAT32 = @import("filesystem/fat32.zig"); 3 | 4 | pub const Type = lib.FilesystemType; 5 | 6 | pub const ReadError = error{ 7 | unsupported, 8 | failed, 9 | }; 10 | 11 | pub const WriteError = error{ 12 | unsupported, 13 | failed, 14 | }; 15 | 16 | test { 17 | _ = FAT32; 18 | } 19 | -------------------------------------------------------------------------------- /src/lib/graphics.zig: -------------------------------------------------------------------------------- 1 | pub const Driver = @This(); 2 | 3 | const lib = @import("lib"); 4 | const assert = lib.assert; 5 | const log = lib.log.scoped(.Graphics); 6 | 7 | pub const Rectangle = @import("graphics/rectangle.zig"); 8 | pub const Rect = Rectangle.Rectangle; 9 | 10 | const Type = enum(u64) { 11 | limine = 0, 12 | virtio = 1, 13 | sdl_software_renderer_prototype = 2, 14 | }; 15 | 16 | const UpdateScreenFunction = fn (graphics: *Driver, drawing_area: DrawingArea, destination: Point) void; 17 | 18 | type: Type, 19 | frontbuffer: Framebuffer, 20 | backbuffer: DrawingArea, 21 | callback_update_screen: *const UpdateScreenFunction, 22 | 23 | pub const DrawingArea = struct { 24 | bytes: [*]u8 = undefined, 25 | width: u32 = 0, 26 | height: u32 = 0, 27 | stride: u32 = 0, 28 | }; 29 | 30 | pub const Point = struct { 31 | x: u32 = 0, 32 | y: u32 = 0, 33 | }; 34 | 35 | pub const Framebuffer = struct { 36 | area: DrawingArea = .{}, 37 | modified_region: Rect = Rectangle.zero(), 38 | 39 | pub fn get_pixel_count(framebuffer: Framebuffer) u32 { 40 | return framebuffer.area.width * framebuffer.area.height; 41 | } 42 | 43 | pub fn get_pointer(framebuffer: Framebuffer) [*]u32 { 44 | return @ptrCast([*]u32, @alignCast(@alignOf(u32), framebuffer.area.bytes)); 45 | } 46 | 47 | pub fn resize(framebuffer: *Framebuffer, allocator: lib.CustomAllocator, width: u32, height: u32) bool { 48 | // TODO: copy old bytes 49 | // TODO: free old bytes 50 | if (width == 0 or height == 0) return false; 51 | 52 | const old_width = framebuffer.area.width; 53 | const old_height = framebuffer.area.height; 54 | 55 | if (width == old_width and height == old_height) return true; 56 | 57 | // TODO: stop hardcoding the 4 58 | const new_buffer_memory = allocator.allocate_bytes(width * height * 4, 0x1000) catch unreachable; 59 | framebuffer.area = DrawingArea{ 60 | .bytes = @intToPtr([*]u8, new_buffer_memory.address), 61 | .width = width, 62 | .height = height, 63 | .stride = width * 4, 64 | }; 65 | 66 | // Clear it with white to debug it 67 | framebuffer.fill(0); 68 | 69 | return true; 70 | } 71 | 72 | pub fn fill(framebuffer: *Framebuffer, color: u32) void { 73 | assert(@divExact(framebuffer.area.stride, framebuffer.area.width) == @sizeOf(u32)); 74 | 75 | for (@ptrCast([*]u32, @alignCast(@alignOf(u32), framebuffer.area.bytes))[0..framebuffer.get_pixel_count()]) |*pixel| { 76 | pixel.* = color; 77 | } 78 | } 79 | 80 | pub fn copy(framebuffer: *Framebuffer, source: *Framebuffer, destination_point: Point, source_region: Rect, add_to_modified_region: bool) void { 81 | const destination_region = Rectangle.from_point_and_rectangle(destination_point, source_region); 82 | 83 | const surface_clip = Rectangle.from_area(framebuffer.area); 84 | 85 | if (add_to_modified_region) { 86 | framebuffer.update_modified_region(destination_region); 87 | } 88 | 89 | const source_ptr = @ptrCast([*]u32, @alignCast(@alignOf(u32), source.area.bytes + source.area.stride * Rectangle.top(source_region) + 4 * Rectangle.left(source_region))); 90 | framebuffer.draw_bitmap(surface_clip, destination_region, source_ptr, source.area.stride, .opaque_mode); 91 | } 92 | 93 | fn update_modified_region(framebuffer: *Framebuffer, destination_region: Rect) void { 94 | framebuffer.modified_region = Rectangle.bounding(destination_region, framebuffer.modified_region); 95 | framebuffer.modified_region = Rectangle.clip(framebuffer.modified_region, Rectangle.from_area(framebuffer.area)).intersection; 96 | } 97 | 98 | pub fn draw(framebuffer: *Framebuffer, source: *Framebuffer, destination_region: Rect, source_offset: Point, alpha: DrawBitmapMode) void { 99 | const surface_clip = Rectangle.from_area(framebuffer.area); 100 | framebuffer.update_modified_region(destination_region); 101 | const source_ptr = @ptrCast([*]u32, @alignCast(@alignOf(u32), source.area.bytes + source.area.stride * source_offset.y + 4 * source_offset.x)); 102 | framebuffer.draw_bitmap(surface_clip, destination_region, source_ptr, source.area.stride, alpha); 103 | } 104 | 105 | pub fn draw_bitmap(framebuffer: *Framebuffer, clip_area: Rect, region: Rect, source_ptr: [*]const u32, asked_source_stride: u32, mode: DrawBitmapMode) void { 106 | const result = Rectangle.clip(region, clip_area); 107 | if (result.clip) { 108 | const bounds = result.intersection; 109 | const source_stride = asked_source_stride / @sizeOf(u32); 110 | const stride = framebuffer.area.stride / @sizeOf(u32); 111 | const line_start_index = Rectangle.top(bounds) * stride + Rectangle.left(bounds); 112 | var line_start = @ptrCast([*]u32, @alignCast(@alignOf(u32), framebuffer.area.bytes)) + line_start_index; 113 | const source_line_start_index = Rectangle.left(bounds) - Rectangle.left(region) + source_stride * (Rectangle.top(bounds) - Rectangle.top(region)); 114 | var source_line_start = source_ptr + source_line_start_index; 115 | 116 | var i: u64 = 0; 117 | const bounds_width = Rectangle.width(bounds); 118 | const bounds_height = Rectangle.height(bounds); 119 | 120 | while (i < bounds_height) : ({ 121 | i += 1; 122 | line_start += stride; 123 | source_line_start += source_stride; 124 | }) { 125 | var destination = line_start; 126 | var source = source_line_start; 127 | 128 | var j = bounds_width; 129 | if (@enumToInt(mode) == 0xff) { 130 | while (true) { 131 | blend_pixel(&destination[0], source[0]); 132 | destination += 1; 133 | source += 1; 134 | j -= 1; 135 | if (j == 0) break; 136 | } 137 | } else if (@enumToInt(mode) <= 0xff) { 138 | @panic("todo: mode <= 0xff"); 139 | } else if (mode == .xor) { 140 | @panic("todo: mode xor"); 141 | } else if (mode == .opaque_mode) { 142 | // todo: refactor 143 | while (j > 0) : ({ 144 | destination += 1; 145 | source += 1; 146 | j -= 1; 147 | }) { 148 | destination[0] = 0xff_00_00_00 | source[0]; 149 | } 150 | } 151 | } 152 | } 153 | } 154 | }; 155 | 156 | // TODO: full alpha 157 | fn blend_pixel(destination_pixel: *u32, modified: u32) void { 158 | if (modified & 0xff_00_00_00 == 0xff_00_00_00) { 159 | destination_pixel.* = modified; 160 | return; 161 | } else if (modified & 0xff_00_00_00 == 0) { 162 | return; 163 | } 164 | 165 | const original = destination_pixel.*; 166 | 167 | const m1 = (modified & 0xff_00_00_00) >> 24; 168 | const m2 = 255 - m1; 169 | const a = 0xff_00_00_00; 170 | 171 | const r2 = m2 * (original & 0x00FF00FF); 172 | const g2 = m2 * (original & 0x0000FF00); 173 | const r1 = m1 * (modified & 0x00FF00FF); 174 | const g1 = m1 * (modified & 0x0000FF00); 175 | const result = a | (0x0000FF00 & ((g1 + g2) >> 8)) | (0x00FF00FF & ((r1 + r2) >> 8)); 176 | destination_pixel.* = result; 177 | } 178 | 179 | pub const DrawBitmapMode = enum(u16) { 180 | blend = 0, 181 | xor = 0xfffe, 182 | opaque_mode = 0xffff, 183 | _, 184 | }; 185 | -------------------------------------------------------------------------------- /src/lib/nls.zig: -------------------------------------------------------------------------------- 1 | pub const Error = error{ 2 | name_too_long, 3 | bad_value, 4 | }; 5 | 6 | pub const max_charset_size = 6; 7 | 8 | pub const Table = struct { 9 | character_set: []const u8, 10 | unicode_to_character: *const fn (wchar: u16, char_string: []u8) Error!void, 11 | character_to_unicode: *const fn (char_string: []u8) Error!u16, 12 | character_set_to_lower: []const u8, 13 | character_set_to_upper: []const u8, 14 | 15 | pub fn to_upper(table: *const Table, char: u8) u8 { 16 | const possible_result = table.character_set_to_upper[char]; 17 | return if (possible_result != 0) possible_result else char; 18 | } 19 | }; 20 | 21 | pub const ascii = @import("nls/ascii.zig"); 22 | -------------------------------------------------------------------------------- /src/lib/nls/ascii.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const NLS = lib.NLS; 3 | 4 | const charset_to_unicode: [256]u16 = [128]u16{ 5 | 0x0000, 0x0001, 0x0002, 0x0003, 6 | 0x0004, 0x0005, 0x0006, 0x0007, 7 | 0x0008, 0x0009, 0x000a, 0x000b, 8 | 0x000c, 0x000d, 0x000e, 0x000f, 9 | 0x0010, 0x0011, 0x0012, 0x0013, 10 | 0x0014, 0x0015, 0x0016, 0x0017, 11 | 0x0018, 0x0019, 0x001a, 0x001b, 12 | 0x001c, 0x001d, 0x001e, 0x001f, 13 | 0x0020, 0x0021, 0x0022, 0x0023, 14 | 0x0024, 0x0025, 0x0026, 0x0027, 15 | 0x0028, 0x0029, 0x002a, 0x002b, 16 | 0x002c, 0x002d, 0x002e, 0x002f, 17 | 0x0030, 0x0031, 0x0032, 0x0033, 18 | 0x0034, 0x0035, 0x0036, 0x0037, 19 | 0x0038, 0x0039, 0x003a, 0x003b, 20 | 0x003c, 0x003d, 0x003e, 0x003f, 21 | 0x0040, 0x0041, 0x0042, 0x0043, 22 | 0x0044, 0x0045, 0x0046, 0x0047, 23 | 0x0048, 0x0049, 0x004a, 0x004b, 24 | 0x004c, 0x004d, 0x004e, 0x004f, 25 | 0x0050, 0x0051, 0x0052, 0x0053, 26 | 0x0054, 0x0055, 0x0056, 0x0057, 27 | 0x0058, 0x0059, 0x005a, 0x005b, 28 | 0x005c, 0x005d, 0x005e, 0x005f, 29 | 0x0060, 0x0061, 0x0062, 0x0063, 30 | 0x0064, 0x0065, 0x0066, 0x0067, 31 | 0x0068, 0x0069, 0x006a, 0x006b, 32 | 0x006c, 0x006d, 0x006e, 0x006f, 33 | 0x0070, 0x0071, 0x0072, 0x0073, 34 | 0x0074, 0x0075, 0x0076, 0x0077, 35 | 0x0078, 0x0079, 0x007a, 0x007b, 36 | 0x007c, 0x007d, 0x007e, 0x007f, 37 | } ++ [1]u16{0} ** 128; 38 | 39 | const page00: [256]u8 = [128]u8{ 40 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 41 | 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 42 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 43 | 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 44 | 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 45 | 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 46 | 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 47 | 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 48 | 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 49 | 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 50 | 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 51 | 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 52 | 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 53 | 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 54 | 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 55 | 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 56 | } ++ [1]u8{0} ** 128; 57 | 58 | const page_unicode_to_charset = [_]*const [256]u8{ 59 | &page00, 60 | }; 61 | 62 | const charset_to_lower: [256]u8 = [128]u8{ 63 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 64 | 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 65 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 66 | 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 67 | 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 68 | 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 69 | 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 70 | 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 71 | 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 72 | 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 73 | 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 74 | 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 75 | 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 76 | 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 77 | 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 78 | 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 79 | } ++ [1]u8{0} ** 128; 80 | 81 | const charset_to_upper: [256]u8 = [128]u8{ 82 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 83 | 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 84 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 85 | 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 86 | 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 87 | 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 88 | 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 89 | 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 90 | 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 91 | 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 92 | 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 93 | 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 94 | 0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 95 | 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 96 | 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 97 | 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 98 | } ++ [1]u8{0} ** 128; 99 | 100 | fn unicode_to_character(wchar: u16, char_string: []u8) NLS.Error!void { 101 | //const unsigned char *uni2charset; 102 | const wchar_low = @truncate(u8, wchar); 103 | const wchar_high = @truncate(u8, wchar >> 8); 104 | 105 | if (char_string.len == 0) return NLS.Error.name_too_long; 106 | 107 | const unicode_to_charset = page_unicode_to_charset[wchar_high]; 108 | const possible_out = unicode_to_charset[wchar_low]; 109 | if (possible_out == 0) return NLS.Error.bad_value; 110 | char_string[0] = possible_out; 111 | } 112 | 113 | fn character_to_unicode(char_string: []const u8) NLS.Error!u16 { 114 | const unicode = charset_to_unicode[char_string[0]]; 115 | if (unicode == 0) return NLS.Error.bad_value; 116 | return unicode; 117 | } 118 | 119 | pub const table = NLS.Table{ 120 | .character_set = "ascii", 121 | .unicode_to_character = unicode_to_character, 122 | .character_to_unicode = character_to_unicode, 123 | .character_set_to_lower = &charset_to_lower, 124 | .character_set_to_upper = &charset_to_upper, 125 | }; 126 | -------------------------------------------------------------------------------- /src/lib/partition_table.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | 3 | pub const GPT = @import("partition_table/gpt.zig"); 4 | pub const MBR = @import("partition_table/mbr.zig"); 5 | 6 | test { 7 | _ = GPT; 8 | _ = MBR; 9 | } 10 | 11 | pub const Type = lib.PartitionTableType; 12 | -------------------------------------------------------------------------------- /src/lib/psf1.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | 3 | pub const Header = extern struct { 4 | magic: [2]u8, 5 | mode: u8, 6 | character_size: u8, 7 | 8 | pub const magic = .{ 0x36, 0x04 }; 9 | }; 10 | 11 | pub const Error = error{ 12 | invalid_magic, 13 | }; 14 | -------------------------------------------------------------------------------- /src/privileged.zig: -------------------------------------------------------------------------------- 1 | // This package provides of privileged data structures and routines to both kernel and bootloaders, for now 2 | 3 | const lib = @import("lib"); 4 | // const PhysicalAddress = lib.PhysicalAddress; 5 | // const VirtualAddress = lib.VirtualAddress; 6 | // const PhysicalMemoryRegion = lib.PhysicalMemoryRegion; 7 | // const VirtualMemoryRegion = lib.VirtualMemoryRegion; 8 | 9 | const assert = lib.assert; 10 | const log = lib.log; 11 | const maxInt = lib.maxInt; 12 | const Allocator = lib.Allocator; 13 | 14 | const bootloader = @import("bootloader"); 15 | 16 | pub const ACPI = @import("privileged/acpi.zig"); 17 | pub const arch = @import("privileged/arch.zig"); 18 | 19 | pub const writer = E9Writer{ .context = {} }; 20 | 21 | pub const E9WriterError = error{}; 22 | pub const E9Writer = lib.Writer(void, E9WriterError, writeToE9); 23 | 24 | fn writeToE9(_: void, bytes: []const u8) E9WriterError!usize { 25 | return arch.io.writeBytes(0xe9, bytes); 26 | } 27 | 28 | pub const default_stack_size = 0x4000; 29 | 30 | pub const ResourceOwner = enum(u2) { 31 | bootloader = 0, 32 | kernel = 1, 33 | user = 2, 34 | }; 35 | 36 | const panic_logger = lib.log.scoped(.PANIC); 37 | 38 | pub inline fn exitFromQEMU(exit_code: lib.QEMU.ExitCode) noreturn { 39 | comptime assert(@sizeOf(lib.QEMU.ExitCode) == @sizeOf(u32)); 40 | arch.io.write(u32, lib.QEMU.isa_debug_exit.io_base, @enumToInt(exit_code)); 41 | 42 | arch.stopCPU(); 43 | } 44 | 45 | pub const Mapping = extern struct { 46 | physical: lib.PhysicalAddress = lib.PhysicalAddress.invalid(), 47 | virtual: lib.VirtualAddress = .null, 48 | size: u64 = 0, 49 | flags: Flags = .{}, 50 | reserved: u32 = 0, 51 | 52 | pub const Flags = packed struct(u32) { 53 | write: bool = false, 54 | cache_disable: bool = false, 55 | global: bool = false, 56 | execute: bool = false, 57 | user: bool = false, 58 | secret: bool = false, 59 | reserved: u26 = 0, 60 | 61 | pub inline fn empty() Flags { 62 | return .{}; 63 | } 64 | 65 | pub inline fn toArchitectureSpecific(flags: Flags) arch.paging.MemoryFlags { 66 | return arch.paging.newFlags(flags); 67 | } 68 | }; 69 | }; 70 | 71 | pub const PageAllocator = struct { 72 | allocate: *const fn (context: ?*anyopaque, size: u64, alignment: u64, allocate_options: AllocateOptions) Allocator.Allocate.Error!lib.PhysicalMemoryRegion, 73 | context: ?*anyopaque, 74 | context_type: ContextType, 75 | reserved: u32 = 0, 76 | 77 | pub const AllocatePageTablesOptions = packed struct { 78 | count: u16 = 1, 79 | level: arch.paging.Level, 80 | user: bool, 81 | }; 82 | 83 | pub inline fn allocatePageTable(page_allocator: PageAllocator, options: AllocatePageTablesOptions) !lib.PhysicalMemoryRegion { 84 | const result = try page_allocator.allocate(page_allocator.context, arch.paging.page_table_size, arch.paging.page_table_alignment, .{ 85 | .count = options.count, 86 | .level = options.level, 87 | .level_valid = true, 88 | .user = options.user, 89 | }); 90 | return result; 91 | } 92 | 93 | pub const AllocateOptions = packed struct { 94 | count: u16 = 1, 95 | space_waste_allowed_to_guarantee_alignment: u8 = 0, 96 | level: arch.paging.Level = undefined, 97 | level_valid: bool = false, 98 | user: bool = false, 99 | }; 100 | 101 | const ContextType = enum(u32) { 102 | invalid = 0, 103 | bootloader = 1, 104 | cpu = 2, 105 | }; 106 | }; 107 | -------------------------------------------------------------------------------- /src/privileged/acpi.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const assert = lib.assert; 3 | const log = lib.log; 4 | 5 | pub const RSDP = extern struct { 6 | pub const Descriptor1 = extern struct { 7 | signature: [8]u8, 8 | checksum: u8, 9 | OEM_ID: [6]u8, 10 | revision: u8, 11 | RSDT_address: u32, 12 | 13 | comptime { 14 | assert(@sizeOf(Descriptor1) == 20); 15 | } 16 | 17 | const RSDPError = error{ 18 | version_corrupted, 19 | table_not_found, 20 | xsdt_32_bit, 21 | }; 22 | 23 | pub fn findTable(rsdp: *RSDP.Descriptor1, table_signature: Signature) !*align(1) const Header { 24 | switch (switch (rsdp.revision) { 25 | 0 => false, 26 | 2 => true, 27 | else => return RSDPError.version_corrupted, 28 | }) { 29 | inline else => |is_xsdt| { 30 | if (is_xsdt and lib.cpu.arch == .x86) return RSDPError.xsdt_32_bit; 31 | 32 | const root_table_address = switch (is_xsdt) { 33 | false => rsdp.RSDT_address, 34 | true => @fieldParentPtr(RSDP.Descriptor2, "descriptor1", rsdp).XSDT_address, 35 | }; 36 | 37 | const root_table_header = @intToPtr(*align(1) Header, root_table_address); 38 | const EntryType = switch (is_xsdt) { 39 | false => u32, 40 | true => u64, 41 | }; 42 | 43 | const entry_count = @divExact(root_table_header.length - @sizeOf(Header), @sizeOf(EntryType)); 44 | const entries = @intToPtr([*]align(1) const EntryType, @ptrToInt(root_table_header) + @sizeOf(Header))[0..entry_count]; 45 | for (entries) |entry| { 46 | const table_header = @intToPtr(*align(1) const Header, entry); 47 | if (table_signature == table_header.signature) { 48 | return table_header; 49 | } 50 | } 51 | 52 | return RSDPError.table_not_found; 53 | }, 54 | } 55 | } 56 | }; 57 | 58 | pub const Descriptor2 = extern struct { 59 | descriptor1: Descriptor1, 60 | length: u32, 61 | XSDT_address: u64 align(4), 62 | cheksum: u8, 63 | reserved: [3]u8, 64 | 65 | comptime { 66 | assert(@alignOf(Descriptor1) == 4); 67 | assert(@alignOf(Descriptor2) == 4); 68 | assert(@sizeOf(Descriptor2) == 36); 69 | } 70 | }; 71 | }; 72 | 73 | const Signature = enum(u32) { 74 | APIC = @ptrCast(*align(1) const u32, "APIC").*, 75 | FACP = @ptrCast(*align(1) const u32, "FACP").*, 76 | HPET = @ptrCast(*align(1) const u32, "HPET").*, 77 | MCFG = @ptrCast(*align(1) const u32, "MCFG").*, 78 | WAET = @ptrCast(*align(1) const u32, "WAET").*, 79 | BGRT = @ptrCast(*align(1) const u32, "BGRT").*, 80 | _, 81 | }; 82 | 83 | pub const Header = extern struct { 84 | signature: Signature, 85 | length: u32, 86 | revision: u8, 87 | checksum: u8, 88 | OEM_ID: [6]u8, 89 | OEM_table_ID: [8]u8, 90 | OEM_revision: u32, 91 | creator_ID: u32, 92 | creator_revision: u32, 93 | 94 | comptime { 95 | assert(@sizeOf(@This()) == 0x24); 96 | } 97 | }; 98 | 99 | pub const MADT = extern struct { 100 | header: Header, 101 | LAPIC_address: u32, 102 | flags: MADTFlags, 103 | 104 | pub const MADTFlags = packed struct(u32) { 105 | pcat_compatibility: bool, 106 | reserved: u31 = 0, 107 | }; 108 | 109 | comptime { 110 | assert(@sizeOf(@This()) == 0x2c); 111 | } 112 | 113 | pub fn getIterator(madt: *align(1) const MADT) Iterator { 114 | return .{ 115 | .madt = madt, 116 | }; 117 | } 118 | 119 | pub fn getCPUCount(madt: *align(1) const MADT) u32 { 120 | var cpu_count: u32 = 0; 121 | var iterator = madt.getIterator(); 122 | while (iterator.next()) |entry| { 123 | cpu_count += switch (entry.type) { 124 | .LAPIC => blk: { 125 | const lapic_entry = @fieldParentPtr(LAPIC, "record", entry); 126 | break :blk @boolToInt((lapic_entry.flags.enabled and !lapic_entry.flags.online_capable) or (lapic_entry.flags.online_capable and !lapic_entry.flags.enabled)); 127 | }, 128 | .x2APIC => @panic("x2apic not implemented"), 129 | else => continue, 130 | }; 131 | } 132 | 133 | return cpu_count; 134 | } 135 | 136 | pub const Record = extern struct { 137 | type: Type, 138 | length: u8, 139 | 140 | const Type = enum(u8) { 141 | LAPIC = 0, 142 | IO_APIC = 1, 143 | ISO = 2, 144 | NMI_source = 3, 145 | LAPIC_NMI = 4, 146 | LAPIC_address_override = 5, 147 | IO_SAPIC = 6, 148 | LSAPIC = 7, 149 | platform_interrupt_sources = 8, 150 | x2APIC = 9, 151 | x2APIC_NMI = 0xa, 152 | GIC_CPU_interface = 0xb, 153 | GIC_distributor = 0xc, 154 | GIC_MSI_frame = 0xd, 155 | GIC_redistributor = 0xe, 156 | GIC_interrupt_translation_service = 0xf, 157 | }; 158 | }; 159 | 160 | pub const Iterator = extern struct { 161 | madt: *align(1) const MADT, 162 | index: usize = 0, 163 | offset: usize = @sizeOf(MADT), 164 | 165 | pub fn next(iterator: *Iterator) ?*const Record { 166 | if (iterator.offset < iterator.madt.header.length) { 167 | const record = @intToPtr(*const Record, @ptrToInt(iterator.madt) + iterator.offset); 168 | iterator.offset += record.length; 169 | return record; 170 | } 171 | 172 | return null; 173 | } 174 | }; 175 | 176 | pub const LAPIC = extern struct { 177 | record: Record, 178 | ACPI_processor_UID: u8, 179 | APIC_ID: u8, 180 | flags: Flags, 181 | 182 | const Flags = packed struct(u32) { 183 | enabled: bool, 184 | online_capable: bool, 185 | reserved: u30 = 0, 186 | }; 187 | }; 188 | 189 | const IO_APIC = extern struct { 190 | record: Record, 191 | IO_APIC_ID: u8, 192 | reserved: u8, 193 | IO_APIC_address: u32, 194 | global_system_interrupt_base: u32, 195 | 196 | comptime { 197 | assert(@sizeOf(@This()) == @sizeOf(u64) + @sizeOf(u32)); 198 | } 199 | }; 200 | 201 | const InterruptSourceOverride = extern struct { 202 | record: Record, 203 | bus: u8, 204 | source: u8, 205 | global_system_interrupt: u32 align(2), 206 | flags: u16 align(2), 207 | 208 | comptime { 209 | assert(@sizeOf(@This()) == @sizeOf(u64) + @sizeOf(u16)); 210 | } 211 | }; 212 | 213 | const LAPIC_NMI = extern struct { 214 | record: Record, 215 | ACPI_processor_UID: u8, 216 | flags: u16 align(1), 217 | LAPIC_lint: u8, 218 | 219 | comptime { 220 | assert(@sizeOf(@This()) == @sizeOf(u32) + @sizeOf(u16)); 221 | } 222 | }; 223 | }; 224 | 225 | const MCFG = extern struct { 226 | header: Header, 227 | reserved: u64, 228 | 229 | fn getConfigurations(mcfg: *align(1) MCFG) []Configuration { 230 | const entry_count = (mcfg.header.length - @sizeOf(MCFG)) / @sizeOf(Configuration); 231 | const configuration_base = @ptrToInt(mcfg) + @sizeOf(MCFG); 232 | return @intToPtr([*]Configuration, configuration_base)[0..entry_count]; 233 | } 234 | 235 | comptime { 236 | assert(@sizeOf(MCFG) == @sizeOf(Header) + @sizeOf(u64)); 237 | assert(@sizeOf(Configuration) == 0x10); 238 | } 239 | 240 | const Configuration = extern struct { 241 | base_address: u64, 242 | segment_group_number: u16, 243 | start_bus: u8, 244 | end_bus: u8, 245 | reserved: u32, 246 | }; 247 | }; 248 | -------------------------------------------------------------------------------- /src/privileged/arch.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const privileged = @import("privileged"); 3 | 4 | pub const x86 = @import("arch/x86.zig"); 5 | pub const x86_64 = @import("arch/x86_64.zig"); 6 | 7 | pub const current = switch (lib.cpu.arch) { 8 | .x86 => x86, 9 | .x86_64 => x86_64, 10 | else => @compileError("Architecture not supported"), 11 | }; 12 | 13 | pub const CPUPageTables = current.paging.CPUPageTables; 14 | pub const stopCPU = current.stopCPU; 15 | pub const paging = current.paging; 16 | pub const Registers = current.Registers; 17 | pub const disableInterrupts = current.disableInterrupts; 18 | 19 | pub const dispatch_count = current.dispatch_count; 20 | pub var max_physical_address_bit: u6 = 40; 21 | 22 | pub const io = current.io; 23 | -------------------------------------------------------------------------------- /src/privileged/arch/x86.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const assert = lib.assert; 3 | const privileged = @import("privileged"); 4 | 5 | const x86 = @import("x86/common.zig"); 6 | pub usingnamespace x86; 7 | 8 | pub const io = @import("x86/32/io.zig"); 9 | 10 | /// Use x86_64 paging for VirtualAddressSpace 11 | pub const paging = privileged.arch.x86_64.paging; 12 | -------------------------------------------------------------------------------- /src/privileged/arch/x86/32/io.zig: -------------------------------------------------------------------------------- 1 | const privileged = @import("privileged"); 2 | pub inline fn writeBytes(port: u16, bytes: []const u8) usize { 3 | const bytes_left = asm volatile ( 4 | \\rep outsb 5 | : [ret] "={ecx}" (-> usize), 6 | : [dest] "{dx}" (port), 7 | [src] "{esi}" (bytes.ptr), 8 | [len] "{ecx}" (bytes.len), 9 | : "esi", "ecx" 10 | ); 11 | return bytes.len - bytes_left; 12 | } 13 | pub const read = privileged.arch.x86.read; 14 | pub const write = privileged.arch.x86.write; 15 | -------------------------------------------------------------------------------- /src/privileged/arch/x86/64/apic.zig: -------------------------------------------------------------------------------- 1 | const APIC = @This(); 2 | 3 | const lib = @import("lib"); 4 | const assert = lib.assert; 5 | const log = lib.log.scoped(.APIC); 6 | const cpuid = lib.arch.x86_64.cpuid; 7 | const maxInt = lib.maxInt; 8 | 9 | const privileged = @import("privileged"); 10 | const VirtualAddress = privileged.arch.VirtualAddress; 11 | 12 | const arch = privileged.arch; 13 | const x86_64 = privileged.arch.x86_64; 14 | const IA32_APIC_BASE = x86_64.registers.IA32_APIC_BASE; 15 | const io = x86_64.io; 16 | 17 | const ID = packed struct(u32) { 18 | reserved: u24, 19 | apic_id: u8, 20 | 21 | pub inline fn read() ID { 22 | return APIC.read(.id); 23 | } 24 | }; 25 | 26 | pub const TaskPriorityRegister = packed struct(u32) { 27 | subclass: u4 = 0, 28 | class: u4 = 0, 29 | reserved: u24 = 0, 30 | 31 | pub inline fn write(tpr: TaskPriorityRegister) void { 32 | APIC.write(.tpr, @bitCast(u32, tpr)); 33 | } 34 | }; 35 | 36 | pub const LVTTimer = packed struct(u32) { 37 | vector: u8 = 0xfa, 38 | reserved: u4 = 0, 39 | delivery_status: bool = false, 40 | reserved1: u3 = 0, 41 | mask: bool = true, 42 | mode: Mode = .oneshot, 43 | reserved2: u13 = 0, 44 | 45 | const Mode = enum(u2) { 46 | oneshot = 0, 47 | periodic = 1, 48 | tsc_deadline = 2, 49 | }; 50 | 51 | pub inline fn write(timer: LVTTimer) void { 52 | APIC.write(.lvt_timer, @bitCast(u32, timer)); 53 | } 54 | }; 55 | 56 | const DivideConfigurationRegister = packed struct(u32) { 57 | divide: Divide = .by_1, 58 | reserved1: u28 = 0, 59 | 60 | // Divide[bit 2] is always 0 61 | const Divide = enum(u4) { 62 | by_2 = 0b0000, 63 | by_4 = 0b0001, 64 | by_8 = 0b0010, 65 | by_16 = 0b0011, 66 | by_32 = 0b1000, 67 | by_64 = 0b1001, 68 | by_128 = 0b1010, 69 | by_1 = 0b1011, 70 | }; 71 | 72 | inline fn read() DivideConfigurationRegister { 73 | return APIC.read(.timer_div); 74 | } 75 | 76 | inline fn write(dcr: DivideConfigurationRegister) void { 77 | APIC.write(.timer_div, @bitCast(u32, dcr)); 78 | } 79 | }; 80 | 81 | pub inline fn access(register: Register) *volatile u32 { 82 | const physical_address = IA32_APIC_BASE.read().getAddress(); 83 | const virtual_address = switch (lib.cpu.arch) { 84 | .x86 => physical_address.toIdentityMappedVirtualAddress(), 85 | .x86_64 => switch (lib.os) { 86 | .freestanding => physical_address.toHigherHalfVirtualAddress(), 87 | .uefi => physical_address.toIdentityMappedVirtualAddress(), 88 | else => @compileError("Operating system not supported"), 89 | }, 90 | else => @compileError("Architecture not supported"), 91 | }; 92 | 93 | return virtual_address.offset(@enumToInt(register)).access(*volatile u32); 94 | } 95 | 96 | pub inline fn read(register: Register) u32 { 97 | return access(register).*; 98 | } 99 | 100 | pub inline fn write(register: Register, value: u32) void { 101 | access(register).* = value; 102 | } 103 | 104 | pub const Register = enum(u32) { 105 | id = 0x20, 106 | version = 0x30, 107 | tpr = 0x80, 108 | apr = 0x90, 109 | ppr = 0xa0, 110 | eoi = 0xB0, 111 | spurious = 0xF0, 112 | error_status_register = 0x280, 113 | icr_low = 0x300, 114 | icr_high = 0x310, 115 | lvt_timer = 0x320, 116 | timer_div = 0x3e0, 117 | timer_initcnt = 0x380, 118 | timer_current_count = 0x390, 119 | }; 120 | 121 | pub fn calibrateTimer() privileged.arch.x86_64.TicksPerMS { 122 | //calibrate_timer_with_rtc(apic_base); 123 | const timer_calibration_start = lib.arch.x86_64.readTimestamp(); 124 | var times_i: u64 = 0; 125 | const times = 8; 126 | 127 | APIC.write(.timer_initcnt, lib.maxInt(u32)); 128 | 129 | while (times_i < times) : (times_i += 1) { 130 | io.write(u8, io.Ports.PIT_command, 0x30); 131 | io.write(u8, io.Ports.PIT_data, 0xa9); 132 | io.write(u8, io.Ports.PIT_data, 0x04); 133 | 134 | while (true) { 135 | io.write(u8, io.Ports.PIT_command, 0xe2); 136 | if (io.read(u8, io.Ports.PIT_data) & (1 << 7) != 0) break; 137 | } 138 | } 139 | 140 | const ticks_per_ms = (maxInt(u32) - read(.timer_current_count)) >> 4; 141 | const timer_calibration_end = lib.arch.x86_64.readTimestamp(); 142 | const timestamp_ticks_per_ms = @intCast(u32, (timer_calibration_end - timer_calibration_start) >> 3); 143 | 144 | return .{ 145 | .tsc = timestamp_ticks_per_ms, 146 | .lapic = ticks_per_ms, 147 | }; 148 | } 149 | -------------------------------------------------------------------------------- /src/privileged/arch/x86/64/io.zig: -------------------------------------------------------------------------------- 1 | const common = @import("common"); 2 | const log = common.log.scoped(.IO); 3 | 4 | const privileged = @import("privileged"); 5 | 6 | pub const Ports = struct { 7 | pub const DMA1 = 0x0000; 8 | pub const PIC1 = 0x0020; 9 | pub const Cyrix_MSR = 0x0022; 10 | pub const PIT_data = 0x0040; 11 | pub const PIT_command = 0x0043; 12 | pub const PS2 = 0x0060; 13 | pub const CMOS_RTC = 0x0070; 14 | pub const DMA_page_registers = 0x0080; 15 | pub const A20 = 0x0092; 16 | pub const PIC2 = 0x00a0; 17 | pub const DMA2 = 0x00c0; 18 | pub const E9_hack = 0x00e9; 19 | pub const ATA2 = 0x0170; 20 | pub const ATA1 = 0x01f0; 21 | pub const parallel_port = 0x0278; 22 | pub const serial2 = 0x02f8; 23 | pub const IBM_VGA = 0x03b0; 24 | pub const floppy = 0x03f0; 25 | pub const serial1 = 0x03f8; 26 | pub const PCI_config = 0x0cf8; 27 | pub const PCI_data = 0x0cfc; 28 | }; 29 | 30 | pub inline fn writeBytes(port: u16, bytes: []const u8) usize { 31 | const bytes_left = asm volatile ( 32 | \\rep outsb 33 | : [ret] "={rcx}" (-> usize), 34 | : [dest] "{dx}" (port), 35 | [src] "{rsi}" (bytes.ptr), 36 | [len] "{rcx}" (bytes.len), 37 | : "rsi", "rcx" 38 | ); 39 | 40 | return bytes.len - bytes_left; 41 | } 42 | 43 | pub const read = privileged.arch.x86_64.read; 44 | pub const write = privileged.arch.x86_64.write; 45 | -------------------------------------------------------------------------------- /src/privileged/arch/x86/common.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const assert = lib.assert; 3 | 4 | pub const SegmentDescriptor = extern struct { 5 | limit: u16, 6 | address: u64 align(2), 7 | 8 | comptime { 9 | assert(@sizeOf(@This()) == 10); 10 | } 11 | }; 12 | 13 | pub inline fn stopCPU() noreturn { 14 | while (true) { 15 | asm volatile ( 16 | \\cli 17 | \\hlt 18 | \\pause 19 | ::: "memory"); 20 | } 21 | } 22 | 23 | pub inline fn disableInterrupts() void { 24 | asm volatile ("cli" ::: "memory"); 25 | } 26 | 27 | pub inline fn read(comptime T: type, port: u16) T { 28 | return switch (T) { 29 | u8 => asm volatile ("inb %[port], %[result]" 30 | : [result] "={al}" (-> u8), 31 | : [port] "N{dx}" (port), 32 | ), 33 | u16 => asm volatile ("inw %[port], %[result]" 34 | : [result] "={ax}" (-> u16), 35 | : [port] "N{dx}" (port), 36 | ), 37 | u32 => asm volatile ("inl %[port], %[result]" 38 | : [result] "={eax}" (-> u32), 39 | : [port] "N{dx}" (port), 40 | ), 41 | 42 | else => unreachable, 43 | }; 44 | } 45 | 46 | pub inline fn write(comptime T: type, port: u16, value: T) void { 47 | switch (T) { 48 | u8 => asm volatile ("outb %[value], %[port]" 49 | : 50 | : [value] "{al}" (value), 51 | [port] "N{dx}" (port), 52 | ), 53 | u16 => asm volatile ("outw %[value], %[port]" 54 | : 55 | : [value] "{ax}" (value), 56 | [port] "N{dx}" (port), 57 | ), 58 | u32 => asm volatile ("outl %[value], %[port]" 59 | : 60 | : [value] "{eax}" (value), 61 | [port] "N{dx}" (port), 62 | ), 63 | else => unreachable, 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/privileged/arch/x86_64.zig: -------------------------------------------------------------------------------- 1 | const x86 = @import("x86/common.zig"); 2 | pub usingnamespace x86; 3 | 4 | const lib = @import("lib"); 5 | const assert = lib.assert; 6 | const cpuid = lib.arch.x86_64.CPUID; 7 | 8 | const privileged = @import("privileged"); 9 | 10 | pub const APIC = @import("x86/64/apic.zig"); 11 | pub const io = @import("x86/64/io.zig"); 12 | pub const paging = @import("x86/64/paging.zig"); 13 | pub const registers = @import("x86/64/registers.zig"); 14 | 15 | pub const valid_page_sizes = privileged.arch.valid_page_sizes; 16 | pub const page_size = valid_page_sizes[0]; 17 | pub const reasonable_page_size = valid_page_sizes[1]; 18 | 19 | pub fn page_shifter(comptime asked_page_size: comptime_int) comptime_int { 20 | return @ctz(@as(u32, asked_page_size)); 21 | } 22 | 23 | /// Returns the maximum number bits a physical address is allowed to have in this CPU 24 | pub inline fn get_max_physical_address_bit() u6 { 25 | return @truncate(u6, cpuid(0x80000008).eax); 26 | } 27 | 28 | pub const GDT = extern struct { 29 | pub const Entry = packed struct(u64) { 30 | limit_low: u16, 31 | base_low: u16, 32 | base_mid: u8, 33 | access: packed struct(u8) { 34 | accessed: bool, 35 | read_write: bool, 36 | direction_conforming: bool, 37 | executable: bool, 38 | code_data_segment: bool, 39 | dpl: u2, 40 | present: bool, 41 | }, 42 | limit_high: u4, 43 | reserved: u1 = 0, 44 | long_mode: bool, 45 | size_flag: bool, 46 | granularity: bool, 47 | base_high: u8 = 0, 48 | 49 | pub const null_entry = Entry{ 50 | .limit_low = 0, 51 | .base_low = 0, 52 | .base_mid = 0, 53 | .access = .{ 54 | .accessed = false, 55 | .read_write = false, 56 | .direction_conforming = false, 57 | .executable = false, 58 | .code_data_segment = false, 59 | .dpl = 0, 60 | .present = false, 61 | }, 62 | .limit_high = 0, 63 | .long_mode = false, 64 | .size_flag = false, 65 | .granularity = false, 66 | }; 67 | 68 | pub const code_16 = Entry{ 69 | .limit_low = 0xffff, 70 | .base_low = 0, 71 | .base_mid = 0, 72 | .access = .{ 73 | .accessed = false, 74 | .read_write = true, 75 | .direction_conforming = false, 76 | .executable = true, 77 | .code_data_segment = true, 78 | .dpl = 0, 79 | .present = true, 80 | }, 81 | .limit_high = 0, 82 | .long_mode = false, 83 | .size_flag = false, 84 | .granularity = false, 85 | }; 86 | 87 | pub const data_16 = Entry{ 88 | .limit_low = 0xffff, 89 | .base_low = 0, 90 | .base_mid = 0, 91 | .access = .{ 92 | .accessed = false, 93 | .read_write = true, 94 | .direction_conforming = false, 95 | .executable = false, 96 | .code_data_segment = true, 97 | .dpl = 0, 98 | .present = true, 99 | }, 100 | .limit_high = 0, 101 | .long_mode = false, 102 | .size_flag = false, 103 | .granularity = false, 104 | }; 105 | 106 | pub const code_32 = Entry{ 107 | .limit_low = 0xffff, 108 | .base_low = 0, 109 | .base_mid = 0, 110 | .access = .{ 111 | .accessed = false, 112 | .read_write = true, 113 | .direction_conforming = false, 114 | .executable = true, 115 | .code_data_segment = true, 116 | .dpl = 0, 117 | .present = true, 118 | }, 119 | .limit_high = 0xf, 120 | .long_mode = false, 121 | .size_flag = true, 122 | .granularity = true, 123 | }; 124 | 125 | pub const data_32 = Entry{ 126 | .limit_low = 0xffff, 127 | .base_low = 0, 128 | .base_mid = 0, 129 | .access = .{ 130 | .accessed = false, 131 | .read_write = true, 132 | .direction_conforming = false, 133 | .executable = false, 134 | .code_data_segment = true, 135 | .dpl = 0, 136 | .present = true, 137 | }, 138 | .limit_high = 0xf, 139 | .long_mode = false, 140 | .size_flag = true, 141 | .granularity = true, 142 | }; 143 | 144 | pub const code_64 = Entry{ 145 | .limit_low = 0xffff, 146 | .base_low = 0, 147 | .base_mid = 0, 148 | .access = .{ 149 | .accessed = false, 150 | .read_write = true, 151 | .direction_conforming = false, 152 | .executable = true, 153 | .code_data_segment = true, 154 | .dpl = 0, 155 | .present = true, 156 | }, 157 | .limit_high = 0xf, 158 | .long_mode = true, 159 | .size_flag = false, 160 | .granularity = false, 161 | }; 162 | 163 | pub const data_64 = Entry{ 164 | .limit_low = 0xffff, 165 | .base_low = 0, 166 | .base_mid = 0, 167 | .access = .{ 168 | .accessed = false, 169 | .read_write = true, 170 | .direction_conforming = false, 171 | .executable = false, 172 | .code_data_segment = true, 173 | .dpl = 0, 174 | .present = true, 175 | }, 176 | .limit_high = 0xf, 177 | .long_mode = false, 178 | .size_flag = false, 179 | .granularity = false, 180 | }; 181 | 182 | pub const user_data_64 = Entry{ 183 | .limit_low = 0xffff, 184 | .base_low = 0, 185 | .base_mid = 0, 186 | .access = .{ 187 | .accessed = false, 188 | .read_write = true, 189 | .direction_conforming = false, 190 | .executable = false, 191 | .code_data_segment = true, 192 | .dpl = 3, 193 | .present = true, 194 | }, 195 | .limit_high = 0xf, 196 | .long_mode = false, 197 | .size_flag = false, 198 | .granularity = true, 199 | }; 200 | 201 | pub const user_code_64 = Entry{ 202 | .limit_low = 0xffff, 203 | .base_low = 0, 204 | .base_mid = 0, 205 | .access = .{ 206 | .accessed = false, 207 | .read_write = true, 208 | .direction_conforming = false, 209 | .executable = true, 210 | .code_data_segment = true, 211 | .dpl = 3, 212 | .present = true, 213 | }, 214 | .limit_high = 0xf, 215 | .long_mode = true, 216 | .size_flag = false, 217 | .granularity = true, 218 | }; 219 | }; 220 | 221 | pub const Descriptor = x86.SegmentDescriptor; 222 | }; 223 | 224 | pub const TicksPerMS = extern struct { 225 | tsc: u32, 226 | lapic: u32, 227 | }; 228 | -------------------------------------------------------------------------------- /src/rise.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | 3 | pub const arch = @import("rise/arch.zig"); 4 | pub const capabilities = @import("rise/capabilities.zig"); 5 | pub const syscall = @import("rise/syscall.zig"); 6 | 7 | /// This struct is the shared part that the user and the cpu see 8 | pub const UserScheduler = extern struct { 9 | self: *UserScheduler, 10 | disabled: bool, 11 | has_work: bool, 12 | core_id: u32, 13 | 14 | pub inline fn architectureSpecific(user_scheduler: *UserScheduler) *arch.UserScheduler { 15 | return @fieldParentPtr(arch.UserScheduler, "generic", user_scheduler); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /src/rise/arch.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | pub usingnamespace switch (lib.cpu.arch) { 3 | .x86_64 => x86_64, 4 | else => @compileError("Architecture not supported"), 5 | }; 6 | 7 | const x86_64 = @import("arch/x64_64.zig"); 8 | -------------------------------------------------------------------------------- /src/rise/arch/x64_64.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const assert = lib.assert; 3 | const rise = @import("rise"); 4 | 5 | pub const UserScheduler = extern struct { 6 | generic: rise.UserScheduler, 7 | disabled_save_area: RegisterArena, 8 | }; 9 | 10 | pub const RegisterArena = extern struct { 11 | fpu: FPU align(lib.arch.stack_alignment), 12 | registers: rise.arch.Registers, 13 | 14 | pub fn contextSwitch(register_arena: *align(lib.arch.stack_alignment) const RegisterArena) noreturn { 15 | assert(lib.isAligned(@ptrToInt(register_arena), lib.arch.stack_alignment)); 16 | //lib.log.debug("ASDASD: {}", .{register_arena}); 17 | register_arena.fpu.load(); 18 | register_arena.registers.restore(); 19 | } 20 | }; 21 | 22 | pub const Registers = extern struct { 23 | r15: u64, 24 | r14: u64, 25 | r13: u64, 26 | r12: u64, 27 | rbp: u64, 28 | rbx: u64, 29 | r11: u64, 30 | r10: u64, 31 | r9: u64, 32 | r8: u64, 33 | rax: u64, 34 | rcx: u64, 35 | rdx: u64, 36 | rsi: u64, 37 | rdi: u64, 38 | rip: u64, 39 | rflags: lib.arch.x86_64.registers.RFLAGS, 40 | rsp: u64, 41 | 42 | pub fn restore(registers: *const Registers) noreturn { 43 | const fmt = lib.comptimePrint; 44 | asm volatile (fmt( 45 | "mov {}(%[registers]), %r15\n\t" ++ 46 | "mov {}(%[registers]), %r14\n\t" ++ 47 | "mov {}(%[registers]), %r13\n\t" ++ 48 | "mov {}(%[registers]), %r12\n\t" ++ 49 | "mov {}(%[registers]), %rbp\n\t" ++ 50 | "mov {}(%[registers]), %rbx\n\t" ++ 51 | "mov {}(%[registers]), %r11\n\t" ++ 52 | "mov {}(%[registers]), %r10\n\t" ++ 53 | "mov {}(%[registers]), %r9\n\t" ++ 54 | "mov {}(%[registers]), %r8\n\t" ++ 55 | "mov {}(%[registers]), %rax\n\t" ++ 56 | "mov {}(%[registers]), %rcx\n\t" ++ 57 | "mov {}(%[registers]), %rdx\n\t" ++ 58 | "mov {}(%[registers]), %rsi\n\t" ++ 59 | "pushq %[ss]\n\t" ++ 60 | "pushq {}(%[registers])\n\t" ++ 61 | "pushq {}(%[registers])\n\t" ++ 62 | "pushq %[cs]\n\t" ++ 63 | "pushq {}(%[registers])\n\t" ++ 64 | "mov {}(%[registers]), %rdi\n\t" ++ 65 | "iretq\n\t" ++ 66 | "1: jmp 1b", 67 | 68 | .{ 69 | @offsetOf(Registers, "r15"), 70 | @offsetOf(Registers, "r14"), 71 | @offsetOf(Registers, "r13"), 72 | @offsetOf(Registers, "r12"), 73 | @offsetOf(Registers, "rbp"), 74 | @offsetOf(Registers, "rbx"), 75 | @offsetOf(Registers, "r11"), 76 | @offsetOf(Registers, "r10"), 77 | @offsetOf(Registers, "r9"), 78 | @offsetOf(Registers, "r8"), 79 | @offsetOf(Registers, "rax"), 80 | @offsetOf(Registers, "rcx"), 81 | @offsetOf(Registers, "rdx"), 82 | @offsetOf(Registers, "rsi"), 83 | @offsetOf(Registers, "rsp"), 84 | @offsetOf(Registers, "rflags"), 85 | @offsetOf(Registers, "rip"), 86 | @offsetOf(Registers, "rdi"), 87 | }, 88 | ) 89 | : 90 | : [registers] "{rdi}" (registers), 91 | [ss] "i" (rise.arch.user_data_selector), 92 | [cs] "i" (rise.arch.user_code_selector), 93 | : "memory" 94 | ); 95 | 96 | unreachable; 97 | } 98 | }; 99 | 100 | pub const FPU = extern struct { 101 | fcw: u16, 102 | fsw: u16, 103 | ftw: u8, 104 | reserved: u8 = 0, 105 | fop: u16, 106 | fpu_ip1: u32, 107 | fpu_ip2: u16, 108 | reserved0: u16 = 0, 109 | fpu_dp1: u32, 110 | fpu_dp2: u16, 111 | reserved1: u16 = 0, 112 | mxcsr: u32, 113 | mxcsr_mask: u32, 114 | st: [8][2]u64, 115 | xmm: [16][2]u64, 116 | reserved2: [12]u64 = .{0} ** 12, 117 | 118 | pub inline fn load(fpu: *align(lib.arch.stack_alignment) const FPU) void { 119 | assert(@ptrToInt(fpu) % lib.arch.stack_alignment == 0); 120 | asm volatile ( 121 | \\fxrstor %[fpu] 122 | : 123 | : [fpu] "*p" (fpu), 124 | : "memory" 125 | ); 126 | } 127 | }; 128 | 129 | pub const user_code_selector = 0x43; 130 | pub const user_data_selector = 0x3b; 131 | 132 | pub inline fn syscall(options: rise.syscall.Options, arguments: rise.syscall.Arguments) rise.syscall.Result { 133 | var first: rise.syscall.Result.Rise.First = undefined; 134 | var second: rise.syscall.Result.Rise.Second = undefined; 135 | asm volatile ( 136 | \\syscall 137 | : [rax] "={rax}" (first), 138 | [rdx] "={rdx}" (second), 139 | : [options] "{rax}" (options), 140 | [arg0] "{rdi}" (arguments[0]), 141 | [arg1] "{rsi}" (arguments[1]), 142 | [arg2] "{rdx}" (arguments[2]), 143 | [arg3] "{r10}" (arguments[3]), 144 | [arg4] "{r8}" (arguments[4]), 145 | [arg5] "{r9}" (arguments[5]), 146 | : "rcx", "r11", "rsp", "memory" 147 | ); 148 | 149 | return .{ 150 | .rise = .{ 151 | .first = first, 152 | .second = second, 153 | }, 154 | }; 155 | } 156 | -------------------------------------------------------------------------------- /src/rise/syscall.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const assert = lib.assert; 3 | const log = lib.log.scoped(.Syscall); 4 | 5 | const rise = @import("rise"); 6 | const capabilities = rise.capabilities; 7 | 8 | pub const argument_count = 6; 9 | pub const Arguments = [argument_count]usize; 10 | 11 | pub const Convention = enum(u1) { 12 | linux = 0, 13 | rise = 1, 14 | }; 15 | 16 | pub const Options = extern union { 17 | general: General, 18 | rise: Rise, 19 | linux: Linux, 20 | 21 | pub const General = packed struct(u64) { 22 | number: Number, 23 | convention: Convention, 24 | 25 | pub const Number = lib.IntType(.unsigned, union_space_bits); 26 | 27 | comptime { 28 | assertSize(@This()); 29 | } 30 | 31 | pub inline fn getNumberInteger(general: General, comptime convention: Convention) NumberIntegerType(convention) { 32 | const options_integer = @bitCast(u64, general); 33 | return @truncate(NumberIntegerType(convention), options_integer); 34 | } 35 | 36 | pub fn NumberIntegerType(comptime convention: Convention) type { 37 | return switch (convention) { 38 | .rise => Rise.IDInteger, 39 | .linux => u64, 40 | }; 41 | } 42 | }; 43 | 44 | pub const Rise = packed struct(u64) { 45 | type: capabilities.Type, 46 | command: capabilities.Subtype, 47 | reserved: lib.IntType(.unsigned, @bitSizeOf(u64) - @bitSizeOf(capabilities.Type) - @bitSizeOf(capabilities.Subtype) - @bitSizeOf(Convention)) = 0, 48 | convention: Convention = .rise, 49 | 50 | comptime { 51 | Options.assertSize(@This()); 52 | } 53 | 54 | const IDInteger = u16; 55 | pub const ID = enum(IDInteger) { 56 | qemu_exit = 0, 57 | print = 1, 58 | }; 59 | }; 60 | 61 | pub const Linux = enum(u64) { 62 | _, 63 | comptime { 64 | Options.assertSize(@This()); 65 | } 66 | }; 67 | 68 | pub const union_space_bits = @bitSizeOf(u64) - @bitSizeOf(Convention); 69 | 70 | fn assertSize(comptime T: type) void { 71 | assert(@sizeOf(T) == @sizeOf(u64)); 72 | assert(@bitSizeOf(T) == @bitSizeOf(u64)); 73 | } 74 | 75 | comptime { 76 | assertSize(@This()); 77 | } 78 | }; 79 | 80 | pub const Result = extern union { 81 | general: General, 82 | rise: Rise, 83 | linux: Linux, 84 | 85 | pub const General = extern struct { 86 | first: packed struct(u64) { 87 | argument: u63, 88 | convention: Convention, 89 | }, 90 | second: u64, 91 | }; 92 | 93 | pub const Rise = extern struct { 94 | first: First, 95 | second: Second, 96 | 97 | pub const First = packed struct(u64) { 98 | padding1: u32 = 0, 99 | @"error": u16 = 0, 100 | padding2: u8 = 0, 101 | padding3: u7 = 0, 102 | convention: Convention = .rise, 103 | }; 104 | 105 | pub const Second = u64; 106 | 107 | }; 108 | 109 | pub const Linux = extern struct { 110 | result: u64, 111 | reserved: u64 = 0, 112 | }; 113 | 114 | fn assertSize(comptime T: type) void { 115 | assert(@sizeOf(T) == @sizeOf(u64)); 116 | assert(@bitSizeOf(T) == @bitSizeOf(u64)); 117 | } 118 | }; 119 | -------------------------------------------------------------------------------- /src/user.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const assert = lib.assert; 3 | const ExecutionMode = lib.Syscall.ExecutionMode; 4 | 5 | const rise = @import("rise"); 6 | const capabilities = rise.capabilities; 7 | pub const Syscall = rise.capabilities.Syscall; 8 | 9 | pub const arch = @import("user/arch.zig"); 10 | pub const thread = @import("user/thread.zig"); 11 | 12 | comptime { 13 | @export(arch._start, .{ .linkage = .Strong, .name = "_start" }); 14 | } 15 | 16 | pub const Scheduler = extern struct { 17 | time_slice: u32, 18 | core_id: u32, 19 | }; 20 | 21 | const Writer = extern struct { 22 | const syscall = Syscall(.io, .log); 23 | const Error = Writer.syscall.ErrorSet.Error; 24 | 25 | fn write(_: void, bytes: []const u8) Error!usize { 26 | const result = try Writer.syscall.blocking(bytes); 27 | return result; 28 | } 29 | }; 30 | 31 | pub const std_options = struct { 32 | pub fn logFn(comptime level: lib.std.log.Level, comptime scope: @TypeOf(.EnumLiteral), comptime format: []const u8, args: anytype) void { 33 | lib.format(writer, format, args) catch unreachable; 34 | writer.writeByte('\n') catch unreachable; 35 | _ = scope; 36 | _ = level; 37 | } 38 | }; 39 | 40 | pub const writer = lib.Writer(void, Writer.Error, Writer.write){ .context = {} }; 41 | 42 | pub fn zigPanic(message: []const u8, _: ?*lib.StackTrace, _: ?usize) noreturn { 43 | @call(.always_inline, panic, .{ "{s}", .{message} }); 44 | } 45 | 46 | pub fn panic(comptime format: []const u8, arguments: anytype) noreturn { 47 | lib.log.scoped(.PANIC).err(format, arguments); 48 | while (true) { 49 | asm volatile ("pause" ::: "memory"); 50 | } 51 | } 52 | 53 | pub inline fn currentScheduler() *Scheduler { 54 | return arch.currentScheduler(); 55 | } 56 | 57 | fn schedulerInitDisabled(scheduler: *arch.Scheduler) void { 58 | assert(scheduler.common.generic.disabled); 59 | // Architecture-specific initialization 60 | scheduler.initDisabled(); 61 | scheduler.generic.time_slice = 1; 62 | // TODO: capabilities 63 | } 64 | 65 | pub var is_init = false; 66 | 67 | export fn riseInitializeDisabled(scheduler: *arch.Scheduler, arg_init: bool) callconv(.C) noreturn { 68 | // TODO: delete when this code is unnecessary. In the meanwhile it counts as a sanity check 69 | assert(arg_init); 70 | is_init = arg_init; 71 | schedulerInitDisabled(scheduler); 72 | thread.initDisabled(scheduler); 73 | } 74 | 75 | pub const VirtualAddress = enum(usize) { 76 | _, 77 | 78 | pub inline fn new(address: anytype) VirtualAddress { 79 | const T = @TypeOf(address); 80 | return switch (T) { 81 | usize => @intToEnum(VirtualAddress, address), 82 | else => switch (@typeInfo(T)) { 83 | .Fn => @intToEnum(VirtualAddress, @ptrToInt(&address)), 84 | .Pointer => @intToEnum(VirtualAddress, @ptrToInt(address)), 85 | else => { 86 | @compileLog(T); 87 | @compileError("HA!"); 88 | }, 89 | }, 90 | }; 91 | } 92 | 93 | pub inline fn value(va: VirtualAddress) usize { 94 | return @enumToInt(va); 95 | } 96 | 97 | pub inline fn sub(va: *VirtualAddress, substraction: usize) void { 98 | @ptrCast(*usize, va).* -= substraction; 99 | } 100 | }; 101 | -------------------------------------------------------------------------------- /src/user/arch.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | 3 | comptime { 4 | if (lib.os != .freestanding) @compileError("OS not supported"); 5 | } 6 | 7 | pub const x86_64 = @import("arch/x86_64.zig"); 8 | 9 | const current = switch (lib.cpu.arch) { 10 | .x86_64 => x86_64, 11 | else => @compileError("Architecture not supported"), 12 | }; 13 | 14 | pub usingnamespace current; 15 | 16 | pub const _start = current._start; 17 | -------------------------------------------------------------------------------- /src/user/arch/x86_64.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const assert = lib.assert; 3 | const rise = @import("rise"); 4 | const user = @import("user"); 5 | 6 | const FPU = rise.arch.FPU; 7 | const Registers = rise.arch.Registers; 8 | const RegisterArena = rise.arch.RegisterArena; 9 | const Thread = user.Thread; 10 | const VirtualAddress = user.VirtualAddress; 11 | 12 | pub const Scheduler = extern struct { 13 | common: rise.arch.UserScheduler, 14 | generic: user.Scheduler, 15 | 16 | pub fn initDisabled(scheduler: *Scheduler) void { 17 | _ = scheduler; 18 | // TODO: 19 | // *set entry points? 20 | // *set tls registers? 21 | } 22 | 23 | pub noinline fn restore(scheduler: *Scheduler, register_arena: *const RegisterArena) noreturn { 24 | assert(scheduler.common.generic.disabled); 25 | assert(scheduler.common.generic.has_work); 26 | 27 | assert(register_arena.registers.rip > lib.arch.valid_page_sizes[0]); 28 | assert(register_arena.registers.rflags.IF and register_arena.registers.rflags.reserved0); 29 | 30 | register_arena.contextSwitch(); 31 | } 32 | }; 33 | 34 | // CRT0 35 | pub fn _start() callconv(.Naked) noreturn { 36 | asm volatile ( 37 | \\mov %rdi, %rsp 38 | \\lea 0x4000(%rdi), %rsp 39 | \\push %rbp 40 | \\jmp riseInitializeDisabled 41 | ); 42 | 43 | unreachable; 44 | } 45 | 46 | pub inline fn setInitialState(register_arena: *RegisterArena, entry: VirtualAddress, stack: VirtualAddress, arguments: rise.syscall.Arguments) void { 47 | assert(stack.value() > lib.arch.valid_page_sizes[0]); 48 | assert(lib.isAligned(stack.value(), lib.arch.stack_alignment)); 49 | var stack_address = stack; 50 | // x86_64 ABI 51 | stack_address.sub(@sizeOf(usize)); 52 | 53 | register_arena.registers.rip = entry.value(); 54 | register_arena.registers.rsp = stack_address.value(); 55 | register_arena.registers.rflags = .{ .IF = true }; 56 | register_arena.registers.rdi = arguments[0]; 57 | register_arena.registers.rsi = arguments[1]; 58 | register_arena.registers.rdx = arguments[2]; 59 | register_arena.registers.rcx = arguments[3]; 60 | register_arena.registers.r8 = arguments[4]; 61 | register_arena.registers.r9 = arguments[5]; 62 | 63 | register_arena.fpu = lib.zeroes(FPU); 64 | register_arena.fpu.fcw = 0x037f; 65 | register_arena.fpu.fcw = 0x1f80; 66 | } 67 | 68 | pub fn maybeCurrentScheduler() ?*user.Scheduler { 69 | return asm volatile ( 70 | \\mov %fs:0, %[user_scheduler] 71 | : [user_scheduler] "=r" (-> ?*user.Scheduler), 72 | : 73 | : "memory" 74 | ); 75 | } 76 | 77 | pub inline fn currentScheduler() *user.Scheduler { 78 | const result = maybeCurrentScheduler().?; 79 | return result; 80 | } 81 | -------------------------------------------------------------------------------- /src/user/arch/x86_64/linker_script.ld: -------------------------------------------------------------------------------- 1 | PHDRS { 2 | none PT_NULL FLAGS(0); 3 | text PT_LOAD FLAGS((1 << 2) | (1 << 0) /* Readable | Executable */); 4 | rodata PT_LOAD FLAGS((1 << 2) /* Readable */); 5 | data PT_LOAD FLAGS((1 << 2) | (1 << 1) /* Readable | Writeable */); 6 | } 7 | 8 | SECTIONS { 9 | . = 0x600000; 10 | . = ALIGN(4K); 11 | .text . : { 12 | *(.text*) 13 | }:text 14 | . = ALIGN(4K); 15 | .rodata . : { 16 | *(.rodata*) 17 | }:rodata 18 | . = ALIGN(4K); 19 | .data . : { 20 | *(.data*) 21 | *(.bss*) 22 | }:data 23 | . = ALIGN(4K); 24 | } 25 | -------------------------------------------------------------------------------- /src/user/programs/device_manager/main.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const log = lib.log; 3 | const user = @import("user"); 4 | const Syscall = user.Syscall; 5 | 6 | pub const panic = user.zigPanic; 7 | pub const std_options = user.std_options; 8 | 9 | export var core_id: u32 = 0; 10 | 11 | pub fn main() !noreturn { 12 | core_id = try Syscall(.cpu, .get_core_id).blocking({}); 13 | user.currentScheduler().core_id = core_id; 14 | log.debug("Hello world! User space initialization from core #{}", .{core_id}); 15 | const allocation = try Syscall(.cpu_memory, .allocate).blocking(0x1000); 16 | log.debug("Look allocation successful at 0x{x}", .{allocation.value()}); 17 | try Syscall(.cpu, .shutdown).blocking({}); 18 | } 19 | -------------------------------------------------------------------------------- /src/user/programs/device_manager/module.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "zig_exe", 3 | "dependencies": [] 4 | } 5 | -------------------------------------------------------------------------------- /src/user/programs/device_manager/test.zig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidgmbb/birth/a8d3984edb43e95696dc0361429e518524462f70/src/user/programs/device_manager/test.zig -------------------------------------------------------------------------------- /src/user/programs/init/main.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const log = lib.log; 3 | const user = @import("user"); 4 | const syscall = user.Syscall; 5 | 6 | pub const panic = user.zigPanic; 7 | pub const std_options = user.std_options; 8 | 9 | export var core_id: u32 = 0; 10 | 11 | pub fn main() !noreturn { 12 | core_id = try syscall(.cpu, .get_core_id).blocking({}); 13 | user.currentScheduler().core_id = core_id; 14 | log.debug("Hello world! User space initialization from core #{}", .{core_id}); 15 | const bundle_file_list_size = try syscall(.boot, .get_bundle_file_list_size).blocking({}); 16 | log.debug("Bundle file list size: {}", .{bundle_file_list_size}); 17 | const bundle_size = try syscall(.boot, .get_bundle_size).blocking({}); 18 | log.debug("Bundle size: {}", .{bundle_size}); 19 | const allocation = try syscall(.cpu_memory, .allocate).blocking(0x1000); 20 | log.debug("Look allocation successful at 0x{x}", .{allocation.value()}); 21 | try syscall(.cpu, .shutdown).blocking({}); 22 | } 23 | -------------------------------------------------------------------------------- /src/user/programs/init/module.json: -------------------------------------------------------------------------------- 1 | { 2 | "kind": "zig_exe", 3 | "dependencies": [] 4 | } 5 | -------------------------------------------------------------------------------- /src/user/programs/init/test.zig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidgmbb/birth/a8d3984edb43e95696dc0361429e518524462f70/src/user/programs/init/test.zig -------------------------------------------------------------------------------- /src/user/thread.zig: -------------------------------------------------------------------------------- 1 | const lib = @import("lib"); 2 | const log = lib.log.scoped(.thread); 3 | const user = @import("user"); 4 | const rise = @import("rise"); 5 | 6 | const VirtualAddress = user.VirtualAddress; 7 | 8 | pub const Thread = extern struct { 9 | self: *Thread, 10 | previous: ?*Thread, 11 | next: ?*Thread, 12 | stack: [*]u8, 13 | stack_top: [*]align(lib.arch.stack_alignment) u8, 14 | register_arena: rise.arch.RegisterArena align(lib.arch.stack_alignment), 15 | core_id: u32, 16 | 17 | pub fn init(thread: *Thread, scheduler: *user.arch.Scheduler) void { 18 | thread.self = thread; 19 | thread.previous = null; 20 | thread.next = null; 21 | thread.core_id = scheduler.generic.core_id; 22 | } 23 | }; 24 | 25 | pub const Mutex = extern struct { 26 | locked: bool = false, 27 | 28 | pub inline fn internalLock(mutex: *volatile Mutex) void { 29 | mutex.locked = true; 30 | } 31 | }; 32 | 33 | var static_stack: [0x10000]u8 align(lib.arch.stack_alignment) = undefined; 34 | var static_thread: Thread = undefined; 35 | var static_thread_lock = Mutex{}; 36 | 37 | pub fn initDisabled(scheduler: *user.arch.Scheduler) noreturn { 38 | const thread = &static_thread; 39 | static_thread_lock.internalLock(); 40 | thread.stack = &static_stack; 41 | thread.stack_top = static_stack[static_stack.len..]; 42 | thread.init(scheduler); 43 | 44 | // TODO: use RAX as parameter? 45 | 46 | user.arch.setInitialState(&thread.register_arena, VirtualAddress.new(bootstrapThread), VirtualAddress.new(thread.stack_top), .{0} ** 6); 47 | 48 | scheduler.common.generic.has_work = true; 49 | 50 | scheduler.restore(&thread.register_arena); 51 | } 52 | 53 | fn bootstrapThread(parameters: ?*anyopaque) callconv(.C) noreturn { 54 | // TODO: Implement libc glue code 55 | // TODO: Implement rise glue code 56 | if (user.is_init) { 57 | // No allocation path 58 | mainThread(parameters); 59 | } else { 60 | // Do allocations 61 | while (true) {} 62 | } 63 | } 64 | 65 | fn mainThread(parameters: ?*anyopaque) noreturn { 66 | // TODO: parameters 67 | _ = parameters; 68 | const root = @import("root"); 69 | if (@hasDecl(root, "main")) { 70 | const result = switch (@typeInfo(@typeInfo(@TypeOf(root.main)).Fn.return_type.?)) { 71 | .NoReturn => root.main(), 72 | .Void => blk: { 73 | root.main(); 74 | break :blk 0; 75 | }, 76 | .Int => root.main(), 77 | .ErrorUnion => blk: { 78 | const result = root.main() catch { 79 | // TODO: log 80 | break :blk 1; 81 | }; 82 | 83 | switch (@typeInfo(@TypeOf(result))) { 84 | .Void => break :blk 0, 85 | .Int => break :blk result, 86 | else => @compileError("Unexpected return type"), 87 | } 88 | }, 89 | else => @compileError("Unexpected return type"), 90 | }; 91 | _ = result; 92 | @panic("ASdasd"); 93 | } else { 94 | const result = _main(); 95 | _ = result; 96 | } 97 | } 98 | 99 | export fn _main() i32 { 100 | // global constructors 101 | // array 102 | return 0; 103 | } 104 | -------------------------------------------------------------------------------- /tools/OVMF_CODE-pure-efi.fd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidgmbb/birth/a8d3984edb43e95696dc0361429e518524462f70/tools/OVMF_CODE-pure-efi.fd -------------------------------------------------------------------------------- /tools/format_loopback_fat32.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # $1 Loopback device 3 | set -e 4 | sudo mkfs.fat -F 32 `cat $1`p1 1>2 5 | -------------------------------------------------------------------------------- /tools/loopback_end.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # $1 = Loopback device 3 | set -e 4 | sudo losetup -d `cat $1` 5 | -------------------------------------------------------------------------------- /tools/loopback_mount.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # $1 = Loopback device 3 | # $2 = Mount directory 4 | set -e 5 | #echo "Mounting loopback device $1 in directory $2..." 6 | sudo mount `cat $1`p1 $2 7 | -------------------------------------------------------------------------------- /tools/loopback_start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # $1 = Disk image 3 | # $2 = Loopback device 4 | set -e 5 | #echo "Starting loopback device $2 with image $1..." 6 | sudo losetup -Pf --show $1 > $2 7 | --------------------------------------------------------------------------------