├── .editorconfig ├── .gitignore ├── GNUmakefile ├── LICENSE ├── README.txt ├── base-files ├── bin ├── etc │ ├── group │ ├── passwd │ ├── profile │ └── shells ├── home │ └── zigux │ │ └── .keep ├── lib ├── lib64 ├── root │ └── .keep ├── run │ └── .keep ├── sbin ├── tmp │ └── .keep ├── usr │ ├── lib64 │ └── sbin └── var │ ├── log │ └── .keep │ └── run ├── bochsrc ├── host-recipes ├── autoconf ├── autoconf-2.69 ├── automake ├── binutils ├── gcc ├── gnulib ├── libgcc-binaries ├── libtool ├── limine ├── pkg-config └── zig ├── init ├── meson.build └── src │ └── main.c ├── jinx-config ├── kernel ├── build.zig ├── build.zig.zon ├── linker.ld └── src │ ├── abi.zig │ ├── acpi.zig │ ├── apic.zig │ ├── arch.zig │ ├── containers │ ├── bit_fields.zig │ └── ring_buffer.zig │ ├── debug.zig │ ├── drivers.zig │ ├── drivers │ ├── ahci.zig │ ├── input.zig │ ├── ps2.zig │ ├── usb.zig │ └── xhci.zig │ ├── hpet.zig │ ├── interrupts.zig │ ├── lock.zig │ ├── main.zig │ ├── pci.zig │ ├── per_cpu.zig │ ├── phys.zig │ ├── process.zig │ ├── scheduler.zig │ ├── tar.zig │ ├── time.zig │ ├── uacpi.zig │ ├── utils.zig │ ├── vfs.zig │ ├── vfs │ ├── dev_fs.zig │ └── ram_fs.zig │ └── virt.zig ├── misc ├── create-image.sh ├── cross_file.txt └── run-emulator.sh ├── patches ├── autoconf │ └── jinx-working-patch.patch ├── binutils │ └── jinx-working-patch.patch ├── gcc-host │ └── jinx-working-patch.patch ├── libtool │ └── jinx-working-patch.patch ├── mlibc │ └── jinx-working-patch.patch ├── ncurses │ └── jinx-working-patch.patch ├── readline │ └── jinx-working-patch.patch └── zig │ └── jinx-working-patch.patch ├── recipes ├── base-files ├── bash ├── core-libs ├── coreutils ├── cxxshim ├── frigg ├── init ├── kernel ├── libgcc ├── libgcc-binaries ├── libiconv ├── libintl ├── libstdc++ ├── libxcrypt ├── linux-headers ├── mlibc ├── mlibc-headers ├── nano ├── ncurses ├── readline └── tzdata └── source-recipes ├── autoconf ├── autoconf-2.69 ├── automake ├── binutils ├── gcc-host ├── gnulib ├── libgcc-binaries ├── libtool ├── limine ├── pkg-config └── zig /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [Makefile] 4 | indent_style = tab 5 | 6 | [{host-recipes,recipes,source-recipes}/*] 7 | indent_style = space 8 | indent_size = 2 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .jinx-cache 2 | .vscode 3 | 4 | builds 5 | host-builds 6 | host-pkgs 7 | iso_root 8 | pkgs 9 | sources 10 | sysroot 11 | zig-cache 12 | zig-out 13 | 14 | bochsout.log 15 | initramfs.tar 16 | jinx 17 | zigux.iso 18 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | override QEMUFLAGS += -M q35 -m 2G -debugcon stdio -smp 1 \ 2 | -device qemu-xhci,id=xhci \ 3 | -device usb-kbd,bus=xhci.0,port=1,pcap=keyboard.pcap \ 4 | -device usb-mouse,bus=xhci.0,port=2,pcap=mouse.pcap \ 5 | -device usb-net,bus=xhci.0,port=3,pcap=net.pcap \ 6 | # -trace usb_xhci* 7 | 8 | .PHONY: all 9 | all: 10 | rm -f zigux.iso 11 | $(MAKE) zigux.iso 12 | 13 | .PHONY: kernel 14 | kernel: jinx 15 | rm -f builds/kernel.built builds/kernel.packaged 16 | ./jinx build kernel 17 | 18 | .PHONY: distro-full 19 | distro-full: jinx 20 | ./jinx build-all 21 | 22 | .PHONY: distro-base 23 | distro-base: jinx 24 | ./jinx build base-files kernel init bash coreutils 25 | 26 | .PHONY: run 27 | run: zigux.iso 28 | qemu-system-x86_64 -cdrom $< $(QEMUFLAGS) 29 | 30 | .PHONY: run-kvm 31 | run-kvm: zigux.iso 32 | qemu-system-x86_64 -cdrom $< -enable-kvm $(QEMUFLAGS) 33 | 34 | jinx: 35 | curl -o $@ https://raw.githubusercontent.com/mintsuki/jinx/802082d0389d0b73afed5f52875a204e9134a3fe/jinx 36 | chmod +x $@ 37 | 38 | zigux.iso: jinx 39 | $(MAKE) distro-base kernel 40 | ./misc/create-image.sh 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2022 by czapek1337@gmail.com 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 6 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | Zigux is an attempt to write a UNIX-like kernel in Zig. 2 | In order to build Zigux you need an x86_64 based Linux system and following dependencies: 3 | 4 | - GNU coreutils 5 | - GNU make 6 | - wget 7 | - xorriso 8 | - qemu 9 | 10 | Running `make all` will generate an ISO file in the current directory. You can emulate 11 | Zigux by running `make run` (or `make run-kvm` if your host supports KVM). 12 | 13 | Building packages and kernel itself is handled by Jinx (https://github.com/mintsuki/jinx), 14 | the recipes were greatly inspired by Lyre (https://github.com/lyre-os/lyre). 15 | -------------------------------------------------------------------------------- /base-files/bin: -------------------------------------------------------------------------------- 1 | usr/bin -------------------------------------------------------------------------------- /base-files/etc/group: -------------------------------------------------------------------------------- 1 | root:x:0: 2 | bin:x:1:daemon 3 | tty:x:5: 4 | disk:x:6: 5 | daemon:x:8: 6 | lp:x:9: 7 | kmem:x:15: 8 | lpadmin:x:19: 9 | dialout:x:20: 10 | cdrom:x:24: 11 | tape:x:26: 12 | audio:x:29: 13 | video:x:44: 14 | nogroup:x:99: 15 | input:x:108: 16 | zigux:x:1000: 17 | -------------------------------------------------------------------------------- /base-files/etc/passwd: -------------------------------------------------------------------------------- 1 | root:x:0:0:root:/root:/bin/bash 2 | bin:x:1:1:bin:/dev/null:/bin/false 3 | daemon:x:8:8:Daemon user:/dev/null:/bin/false 4 | lp:x:9:9:Print Service user:/var/spool/cups:/bin/false 5 | nobody:x:99:99:Unprivileged user:/dev/null:/bin/false 6 | anon:x:1000:1000:User:/anon:/bin/bash 7 | -------------------------------------------------------------------------------- /base-files/etc/profile: -------------------------------------------------------------------------------- 1 | # /etc/profile 2 | 3 | umask 022 4 | 5 | append_path () { 6 | case ":$PATH:" in 7 | *:"$1":*) 8 | ;; 9 | *) 10 | PATH="${PATH:+$PATH:}$1" 11 | esac 12 | } 13 | 14 | append_path "/usr/bin" 15 | append_path "/usr/local/bin" 16 | append_path "/usr/local/sbin" 17 | 18 | export PATH 19 | export PS1="\[\e[1;32m\]\u@\h\[\e[0m\] \[\e[1;34m\]\w\[\e[0m\]\$ " 20 | 21 | unset -f append_path 22 | unset TERMCAP 23 | unset MANPATH 24 | -------------------------------------------------------------------------------- /base-files/etc/shells: -------------------------------------------------------------------------------- 1 | /bin/sh 2 | /bin/bash 3 | -------------------------------------------------------------------------------- /base-files/home/zigux/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/48cf/zigux/ad2f32b6ef9767af8ced34368b8aa2578f9a14a2/base-files/home/zigux/.keep -------------------------------------------------------------------------------- /base-files/lib: -------------------------------------------------------------------------------- 1 | usr/lib -------------------------------------------------------------------------------- /base-files/lib64: -------------------------------------------------------------------------------- 1 | usr/lib -------------------------------------------------------------------------------- /base-files/root/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/48cf/zigux/ad2f32b6ef9767af8ced34368b8aa2578f9a14a2/base-files/root/.keep -------------------------------------------------------------------------------- /base-files/run/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/48cf/zigux/ad2f32b6ef9767af8ced34368b8aa2578f9a14a2/base-files/run/.keep -------------------------------------------------------------------------------- /base-files/sbin: -------------------------------------------------------------------------------- 1 | usr/bin -------------------------------------------------------------------------------- /base-files/tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/48cf/zigux/ad2f32b6ef9767af8ced34368b8aa2578f9a14a2/base-files/tmp/.keep -------------------------------------------------------------------------------- /base-files/usr/lib64: -------------------------------------------------------------------------------- 1 | lib -------------------------------------------------------------------------------- /base-files/usr/sbin: -------------------------------------------------------------------------------- 1 | bin -------------------------------------------------------------------------------- /base-files/var/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/48cf/zigux/ad2f32b6ef9767af8ced34368b8aa2578f9a14a2/base-files/var/log/.keep -------------------------------------------------------------------------------- /base-files/var/run: -------------------------------------------------------------------------------- 1 | ../run -------------------------------------------------------------------------------- /bochsrc: -------------------------------------------------------------------------------- 1 | log: bochsout.log 2 | memory: guest=2048, host=2048 3 | cpu: count=1, ips=50000000, reset_on_triple_fault=1 4 | cpuid: fsgsbase=1 5 | romimage: file="/usr/share/bochs/BIOS-bochs-latest", address=0xfffe0000 6 | vgaromimage: file="/usr/share/bochs/VGABIOS-lgpl-latest" 7 | ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 8 | ata0-slave: type=cdrom, path="zigux.iso", status=inserted 9 | boot: cdrom 10 | -------------------------------------------------------------------------------- /host-recipes/autoconf: -------------------------------------------------------------------------------- 1 | name=autoconf 2 | from_source=autoconf 3 | revision=1 4 | 5 | build() { 6 | "${source_dir}/configure" \ 7 | --prefix="${prefix}" 8 | 9 | make -j${parallelism} 10 | } 11 | 12 | package() { 13 | DESTDIR="${dest_dir}" make install 14 | 15 | strip_command=strip \ 16 | post_package_strip 17 | } 18 | -------------------------------------------------------------------------------- /host-recipes/autoconf-2.69: -------------------------------------------------------------------------------- 1 | name=autoconf-2.69 2 | from_source=autoconf-2.69 3 | revision=1 4 | 5 | build() { 6 | "${source_dir}/configure" \ 7 | --prefix="${prefix}" 8 | 9 | make -j${parallelism} 10 | } 11 | 12 | package() { 13 | DESTDIR="${dest_dir}" make install 14 | 15 | strip_command=strip \ 16 | post_package_strip 17 | } 18 | -------------------------------------------------------------------------------- /host-recipes/automake: -------------------------------------------------------------------------------- 1 | name=automake 2 | from_source=automake 3 | revision=1 4 | hostdeps="autoconf" 5 | imagedeps="gcc" 6 | 7 | build() { 8 | "${source_dir}/configure" \ 9 | --prefix="${prefix}" 10 | 11 | make -j${parallelism} 12 | } 13 | 14 | package() { 15 | DESTDIR="${dest_dir}" make install 16 | 17 | cp -pv /usr/local/share/autoconf/build-aux/config.sub "${dest_dir}${prefix}/share/automake-1.16/" 18 | cp -pv /usr/local/share/autoconf/build-aux/config.guess "${dest_dir}${prefix}/share/automake-1.16/" 19 | 20 | strip_command=strip \ 21 | post_package_strip 22 | } 23 | -------------------------------------------------------------------------------- /host-recipes/binutils: -------------------------------------------------------------------------------- 1 | name=binutils 2 | from_source=binutils 3 | revision=1 4 | imagedeps="gcc" 5 | hostdeps="autoconf-2.69 automake libtool pkg-config" 6 | 7 | build() { 8 | "${source_dir}/configure" \ 9 | --prefix="${prefix}" \ 10 | --target=${OS_TRIPLET} \ 11 | --with-sysroot="${sysroot_dir}" \ 12 | --disable-nls \ 13 | --disable-werror \ 14 | --disable-dependency-tracking 15 | 16 | make -j${parallelism} all 17 | } 18 | 19 | package() { 20 | DESTDIR="${dest_dir}" make install 21 | 22 | strip_command=strip \ 23 | post_package_strip 24 | } 25 | -------------------------------------------------------------------------------- /host-recipes/gcc: -------------------------------------------------------------------------------- 1 | name=gcc 2 | from_source=gcc-host 3 | revision=1 4 | imagedeps="gcc" 5 | hostdeps="autoconf-2.69 automake libtool pkg-config" 6 | hostrundeps="binutils" 7 | deps="mlibc-headers" 8 | 9 | build() { 10 | cp -rp "${source_dir}"/. ./ 11 | mkdir build && cd build 12 | 13 | CXXFLAGS_FOR_TARGET="$CFLAGS" \ 14 | CFLAGS_FOR_TARGET="$CFLAGS" \ 15 | ../configure \ 16 | --prefix="${prefix}" \ 17 | --target=${OS_TRIPLET} \ 18 | --with-sysroot="${sysroot_dir}" \ 19 | --disable-nls \ 20 | --enable-languages=c,c++,lto \ 21 | --disable-multilib \ 22 | --enable-initfini-array \ 23 | --enable-shared \ 24 | --enable-host-shared 25 | 26 | make -j${parallelism} all-gcc 27 | } 28 | 29 | package() { 30 | DESTDIR="${dest_dir}" make -C build install-gcc 31 | 32 | ln -s "${OS_TRIPLET}-gcc" "${dest_dir}${prefix}/bin/${OS_TRIPLET}-cc" 33 | 34 | strip_command=strip \ 35 | post_package_strip 36 | } 37 | -------------------------------------------------------------------------------- /host-recipes/gnulib: -------------------------------------------------------------------------------- 1 | name=gnulib 2 | from_source=gnulib 3 | revision=1 4 | -------------------------------------------------------------------------------- /host-recipes/libgcc-binaries: -------------------------------------------------------------------------------- 1 | name=libgcc-binaries 2 | from_source=libgcc-binaries 3 | revision=1 4 | 5 | package() { 6 | mkdir -p "${dest_dir}${prefix}/libgcc-binaries" 7 | cp -rv "${source_dir}"/. "${dest_dir}${prefix}/libgcc-binaries/" 8 | } 9 | -------------------------------------------------------------------------------- /host-recipes/libtool: -------------------------------------------------------------------------------- 1 | name=libtool 2 | from_source=libtool 3 | revision=1 4 | hostdeps="autoconf automake" 5 | imagedeps="help2man gcc" 6 | 7 | build() { 8 | cp -rp "${source_dir}"/. ./ 9 | 10 | ./configure \ 11 | --prefix="${prefix}" 12 | 13 | make -j${parallelism} 14 | } 15 | 16 | package() { 17 | DESTDIR="${dest_dir}" make install 18 | 19 | cp -pv /usr/local/share/autoconf/build-aux/config.sub "${dest_dir}${prefix}/share/libtool/build-aux/" 20 | cp -pv /usr/local/share/autoconf/build-aux/config.guess "${dest_dir}${prefix}/share/libtool/build-aux/" 21 | 22 | strip_command=strip \ 23 | post_package_strip 24 | } 25 | -------------------------------------------------------------------------------- /host-recipes/limine: -------------------------------------------------------------------------------- 1 | name=limine 2 | from_source=limine 3 | revision=1 4 | imagedeps="nasm gcc mtools" 5 | 6 | build() { 7 | LDFLAGS="-static" \ 8 | "${source_dir}/configure" \ 9 | --prefix="${prefix}" \ 10 | --enable-uefi-ia32 \ 11 | --enable-uefi-x86-64 \ 12 | --enable-uefi-cd \ 13 | --enable-bios \ 14 | --enable-bios-cd \ 15 | --enable-bios-pxe 16 | 17 | make -j${parallelism} 18 | } 19 | 20 | package() { 21 | DESTDIR="${dest_dir}" make install 22 | 23 | strip_command=strip \ 24 | post_package_strip 25 | } 26 | -------------------------------------------------------------------------------- /host-recipes/pkg-config: -------------------------------------------------------------------------------- 1 | name=pkg-config 2 | from_source=pkg-config 3 | revision=1 4 | imagedeps="gcc" 5 | hostdeps="automake autoconf libtool" 6 | 7 | build() { 8 | "${source_dir}/configure" \ 9 | --prefix="${prefix}" 10 | 11 | make -j${parallelism} 12 | } 13 | 14 | package() { 15 | DESTDIR="${dest_dir}" make install 16 | 17 | ln -svf pkgconf "${dest_dir}${prefix}/bin/${OS_TRIPLET}-pkg-config" 18 | 19 | mkdir -pv "${dest_dir}${prefix}/share/pkgconfig/personality.d" 20 | cat >"${dest_dir}${prefix}/share/pkgconfig/personality.d/${OS_TRIPLET}.personality" < 2 | #include 3 | #include 4 | #include 5 | 6 | int main() { 7 | while (1) { 8 | printf("\x1b[2J\x1b[H"); 9 | printf("Welcome to zigux!\n"); 10 | printf("You can find the source code at " 11 | "https://github.com/czapek1337/zigux\n\n"); 12 | 13 | int pid = fork(); 14 | 15 | if (pid == 0) { 16 | char *argv[] = {"/usr/bin/bash", NULL}; 17 | char *envp[] = {"TERM=xterm", "HOME=/root", 18 | "PATH=/bin:/usr/bin:/usr/local/bin", NULL}; 19 | 20 | execve("/usr/bin/bash", argv, envp); 21 | } else { 22 | int status; 23 | 24 | waitpid(pid, &status, 0); 25 | } 26 | } 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /jinx-config: -------------------------------------------------------------------------------- 1 | JINX_MAJOR_VER=0.2 2 | 3 | export CFLAGS="-O2 -pipe -march=x86-64 -mtune=generic" 4 | export CXXFLAGS="${CFLAGS}" 5 | 6 | OS_TRIPLET="x86_64-zigux" 7 | 8 | autotools_recursive_regen() { 9 | for f in $(find . -name configure.ac); do 10 | echo "* autotools regen in '$(dirname $f)'..." 11 | ( cd "$(dirname "$f")" && autoreconf -fvi "$@" ) 12 | done 13 | } 14 | 15 | post_package_strip() { 16 | if [ -z "$strip_command" ]; then 17 | strip_command="${OS_TRIPLET}-strip" 18 | fi 19 | 20 | for f in $(find "${dest_dir}"); do 21 | if file "$f" | grep 'not stripped' >/dev/null; then 22 | echo "* stripping '$f'..." 23 | stripped_file="$(mktemp)" 24 | ${strip_command} "$f" -o "$stripped_file" 25 | chmod --reference="$f" "$stripped_file" 26 | mv -f "$stripped_file" "$f" 27 | fi 28 | done 29 | } 30 | 31 | autotools_configure() { 32 | if [ -z "${configure_script_path}" ]; then 33 | configure_script_path="${source_dir}/configure" 34 | fi 35 | 36 | ${configure_script_path} \ 37 | --host=${OS_TRIPLET} \ 38 | --with-sysroot=${sysroot_dir} \ 39 | --prefix=${prefix} \ 40 | --sysconfdir=/etc \ 41 | --localstatedir=/var \ 42 | --bindir=${prefix}/bin \ 43 | --sbindir=${prefix}/bin \ 44 | --libdir=${prefix}/lib \ 45 | --disable-static \ 46 | --enable-shared \ 47 | --disable-malloc0returnsnull \ 48 | "$@" 49 | } 50 | 51 | meson_configure() { 52 | if [ -z "${meson_source_dir}" ]; then 53 | meson_source_dir="${source_dir}" 54 | fi 55 | 56 | meson setup "${meson_source_dir}" \ 57 | --cross-file "${base_dir}/misc/cross_file.txt" \ 58 | --prefix=${prefix} \ 59 | --sysconfdir=/etc \ 60 | --libdir=lib \ 61 | --sbindir=bin \ 62 | --buildtype=release \ 63 | -Ddefault_library=shared \ 64 | "$@" 65 | } 66 | -------------------------------------------------------------------------------- /kernel/build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | const target = blk: { 4 | var tgt = std.zig.CrossTarget{ 5 | .cpu_arch = .x86_64, 6 | .os_tag = .freestanding, 7 | .abi = .none, 8 | }; 9 | 10 | const Features = std.Target.x86.Feature; 11 | 12 | tgt.cpu_features_sub.addFeature(@intFromEnum(Features.mmx)); 13 | tgt.cpu_features_sub.addFeature(@intFromEnum(Features.sse)); 14 | tgt.cpu_features_sub.addFeature(@intFromEnum(Features.sse2)); 15 | tgt.cpu_features_sub.addFeature(@intFromEnum(Features.avx)); 16 | tgt.cpu_features_sub.addFeature(@intFromEnum(Features.avx2)); 17 | tgt.cpu_features_add.addFeature(@intFromEnum(Features.soft_float)); 18 | 19 | break :blk tgt; 20 | }; 21 | 22 | pub fn build(b: *std.Build) !void { 23 | // Get dependencies 24 | const flanterm = b.dependency("flanterm", .{}); 25 | const limine = b.dependency("limine", .{}); 26 | const uacpi = b.dependency("uacpi", .{}); 27 | 28 | // Build the kernel 29 | const optimize = b.standardOptimizeOption(.{}); 30 | const kernel = b.addExecutable(.{ 31 | .name = "zigux", 32 | .root_source_file = .{ .path = "src/main.zig" }, 33 | .target = b.resolveTargetQuery(target), 34 | .optimize = optimize, 35 | .code_model = .kernel, 36 | .omit_frame_pointer = false, 37 | .pic = true, 38 | }); 39 | 40 | kernel.setLinkerScriptPath(.{ .path = "linker.ld" }); 41 | kernel.root_module.addImport("limine", limine.module("limine")); 42 | kernel.root_module.red_zone = false; 43 | kernel.root_module.stack_check = false; 44 | kernel.want_lto = false; 45 | 46 | // Install the kernel as an artifact 47 | b.installArtifact(kernel); 48 | 49 | // Add mlibc include paths 50 | kernel.addIncludePath(.{ .path = "../pkgs/mlibc-headers/usr/include" }); 51 | kernel.addIncludePath(.{ .path = "../pkgs/linux-headers/usr/include" }); 52 | 53 | // Get dependency paths 54 | const flanterm_path = flanterm.builder.build_root.path.?; 55 | const uacpi_path = uacpi.builder.build_root.path.?; 56 | 57 | // Add flanterm include path and source files 58 | kernel.addIncludePath(.{ .path = flanterm_path }); 59 | kernel.addCSourceFiles(.{ 60 | .root = .{ .path = flanterm_path }, 61 | .files = &.{ "flanterm.c", "backends/fb.c" }, 62 | .flags = &.{"-fno-sanitize=undefined"}, 63 | }); 64 | 65 | // Add uACPI include path and source files 66 | kernel.addIncludePath(.{ 67 | .path = try std.fs.path.join(b.allocator, &.{ uacpi_path, "include" }), 68 | }); 69 | 70 | kernel.addCSourceFiles(.{ 71 | .root = .{ .path = uacpi_path }, 72 | .files = &.{ 73 | "source/tables.c", 74 | "source/types.c", 75 | "source/uacpi.c", 76 | "source/utilities.c", 77 | "source/interpreter.c", 78 | "source/opcodes.c", 79 | "source/namespace.c", 80 | "source/stdlib.c", 81 | "source/shareable.c", 82 | "source/opregion.c", 83 | "source/default_handlers.c", 84 | "source/io.c", 85 | "source/notify.c", 86 | "source/sleep.c", 87 | "source/registers.c", 88 | "source/resources.c", 89 | "source/event.c", 90 | }, 91 | }); 92 | 93 | // Enable sized frees in uACPI 94 | kernel.defineCMacro("UACPI_SIZED_FREES", "1"); 95 | } 96 | -------------------------------------------------------------------------------- /kernel/build.zig.zon: -------------------------------------------------------------------------------- 1 | .{ 2 | .name = "zigux", 3 | .version = "0.0.0", 4 | .paths = .{""}, 5 | .dependencies = .{ 6 | .flanterm = .{ 7 | .url = "https://github.com/mintsuki/flanterm/archive/347d9cf1e3fced64aa20ab278e2fdcc834086035.tar.gz", 8 | .hash = "1220c0a75eb766b1ba49ddfe978a7f2fbc02fb797ebda63a117c7959beccb10d4556", 9 | }, 10 | .limine = .{ 11 | .url = "https://github.com/limine-bootloader/limine-zig/archive/c94d35f1f116e9e8a7943f30712901760c3d7d96.tar.gz", 12 | .hash = "1220889cec612d653c31a4063d20daa9ec382ac23f76e9cc60a778b0ad17377bafa1", 13 | }, 14 | .uacpi = .{ 15 | .url = "https://github.com/UltraOS/uACPI/archive/edc5e219eb7ccefcfc8d0618018fabd51e83444f.tar.gz", 16 | .hash = "122047c9a1f3df967b94ef86a426b2207774dc63bb941f00f70a2a5400b2df898b3f", 17 | }, 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /kernel/linker.ld: -------------------------------------------------------------------------------- 1 | /* Tell the linker that we want an x86_64 ELF64 output file */ 2 | OUTPUT_FORMAT(elf64-x86-64) 3 | OUTPUT_ARCH(i386:x86-64) 4 | 5 | /* We want the symbol _start to be our entry point */ 6 | ENTRY(platformMain) 7 | 8 | /* Define the program headers we want so the bootloader gives us the right */ 9 | /* MMU permissions */ 10 | PHDRS 11 | { 12 | text PT_LOAD FLAGS((1 << 0) | (1 << 2)) ; /* Execute + Read */ 13 | rodata PT_LOAD FLAGS((1 << 2)) ; /* Read only */ 14 | data PT_LOAD FLAGS((1 << 1) | (1 << 2)) ; /* Write + Read */ 15 | } 16 | 17 | SECTIONS 18 | { 19 | /* We wanna be placed in the topmost 2GiB of the address space, for optimisations */ 20 | /* and because that is what the Limine spec mandates. */ 21 | /* Any address in this region will do, but often 0xffffffff80000000 is chosen as */ 22 | /* that is the beginning of the region. */ 23 | . = 0xffffffff80000000; 24 | 25 | text_begin = .; 26 | 27 | .text : { 28 | *(.text .text.*) 29 | } :text 30 | 31 | text_end = .; 32 | 33 | /* Move to the next memory page for .rodata */ 34 | . += CONSTANT(MAXPAGESIZE); 35 | 36 | rodata_begin = .; 37 | 38 | PROVIDE(symbol_table = 1); 39 | 40 | .rodata : { 41 | *(.rodata .rodata.*) 42 | } :rodata 43 | 44 | rodata_end = .; 45 | 46 | /* Move to the next memory page for .data */ 47 | . += CONSTANT(MAXPAGESIZE); 48 | 49 | data_begin = .; 50 | 51 | .data : { 52 | *(.data .data.*) 53 | } :data 54 | 55 | .bss : { 56 | *(COMMON) 57 | *(.bss .bss.*) 58 | } :data 59 | 60 | data_end = .; 61 | 62 | /* Discard .note.* and .eh_frame since they may cause issues on some hosts. */ 63 | /DISCARD/ : { 64 | *(.eh_frame) 65 | *(.note .note.*) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /kernel/src/abi.zig: -------------------------------------------------------------------------------- 1 | const C = @cImport({ 2 | @cInclude("abi-bits/errno.h"); 3 | @cInclude("abi-bits/seek-whence.h"); 4 | @cInclude("abi-bits/stat.h"); 5 | @cInclude("abi-bits/vm-flags.h"); 6 | @cInclude("asm/ioctls.h"); 7 | @cInclude("zigux/syscall.h"); 8 | @cInclude("dirent.h"); 9 | @cInclude("fcntl.h"); 10 | @cInclude("termios.h"); 11 | }); 12 | 13 | pub usingnamespace C; 14 | -------------------------------------------------------------------------------- /kernel/src/acpi.zig: -------------------------------------------------------------------------------- 1 | const logger = std.log.scoped(.acpi); 2 | 3 | const limine = @import("limine"); 4 | const root = @import("root"); 5 | const std = @import("std"); 6 | 7 | const apic = @import("./apic.zig"); 8 | const arch = @import("./arch.zig"); 9 | const virt = @import("./virt.zig"); 10 | const pci = @import("./pci.zig"); 11 | const uacpi = @import("./uacpi.zig"); 12 | const ps2 = @import("./drivers/ps2.zig"); 13 | 14 | pub const AcpiTable = struct { 15 | uacpi_table: *uacpi.uacpi_table, 16 | 17 | pub fn signature(self: *const @This()) [4]u8 { 18 | return self.uacpi_table.unnamed_0.hdr[0].signature; 19 | } 20 | 21 | pub fn getData(self: *const @This()) []const u8 { 22 | const hdr: *uacpi.acpi_sdt_hdr = @ptrCast(self.uacpi_table.unnamed_0.hdr); 23 | const data: [*]const u8 = @ptrCast(hdr); 24 | return data[0..hdr.length]; 25 | } 26 | }; 27 | 28 | pub fn findTable(signature: *const [4]u8) ?AcpiTable { 29 | var table: ?*uacpi.uacpi_table = null; 30 | if (uacpi.uacpi_table_find_by_signature(signature, &table) != uacpi.UACPI_STATUS_OK) { 31 | return null; 32 | } 33 | return .{ .uacpi_table = table.? }; 34 | } 35 | 36 | pub fn init(rsdp_res: *limine.RsdpResponse) !void { 37 | try uacpi.initTables(rsdp_res); 38 | 39 | const madt_table = findTable(uacpi.ACPI_MADT_SIGNATURE) orelse { 40 | logger.err("Could not find the APIC table", .{}); 41 | return error.TableNotFound; 42 | }; 43 | 44 | var data = madt_table.getData()[@sizeOf(uacpi.acpi_madt)..]; 45 | while (data.len >= @sizeOf(uacpi.acpi_entry_hdr)) { 46 | const entry: *const uacpi.acpi_entry_hdr = @ptrCast(data.ptr); 47 | switch (entry.type) { 48 | uacpi.ACPI_MADT_ENTRY_TYPE_IOAPIC => { 49 | const ioapic: *const uacpi.acpi_madt_ioapic = @ptrCast(entry); 50 | apic.handleIOAPIC(ioapic.address, ioapic.gsi_base); 51 | }, 52 | uacpi.ACPI_MADT_ENTRY_TYPE_INTERRUPT_SOURCE_OVERRIDE => { 53 | const iso: *const uacpi.acpi_madt_interrupt_source_override = @ptrCast(entry); 54 | apic.handleIOAPICISO(iso.bus, iso.source, iso.gsi, iso.flags); 55 | }, 56 | else => logger.warn("Unhandled MADT entry {d} of length {d}", .{ entry.type, entry.length }), 57 | } 58 | data = data[entry.length..]; 59 | } 60 | 61 | try uacpi.init(); 62 | } 63 | 64 | pub fn enumerateDevices() !void { 65 | const sb = uacpi.uacpi_namespace_get_predefined(uacpi.UACPI_PREDEFINED_NAMESPACE_SB); 66 | if (sb == null) { 67 | logger.err("Failed to get predefined namespace SB", .{}); 68 | return error.NamespaceNotFound; 69 | } 70 | 71 | uacpi.uacpi_namespace_for_each_node_depth_first(sb, iterationCallback, null); 72 | } 73 | 74 | fn handlePciHostBridge(node: ?*uacpi.uacpi_namespace_node) void { 75 | const path = uacpi.uacpi_namespace_node_generate_absolute_path(node); 76 | defer uacpi.uacpi_free_absolute_path(path); 77 | 78 | var status: uacpi.uacpi_status = undefined; 79 | var segment: u64 = 0; 80 | var bus: u64 = 0; 81 | 82 | status = uacpi.uacpi_eval_integer(node, "_SEG", null, &segment); 83 | if (status != uacpi.UACPI_STATUS_OK and status != uacpi.UACPI_STATUS_NOT_FOUND) { 84 | logger.err("Failed to evaluate _SEG for {s}: {s}", .{ path, uacpi.uacpi_status_to_string(status) }); 85 | return; 86 | } 87 | 88 | status = uacpi.uacpi_eval_integer(node, "_BBN", null, &bus); 89 | if (status != uacpi.UACPI_STATUS_OK and status != uacpi.UACPI_STATUS_NOT_FOUND) { 90 | logger.err("Failed to evaluate _BBN for {s}: {s}", .{ path, uacpi.uacpi_status_to_string(status) }); 91 | return; 92 | } 93 | 94 | logger.info("Found PCI(e) host bridge {s}, segment {d}, bus {d}", .{ path, segment, bus }); 95 | pci.scanBus(@intCast(segment), @intCast(bus)) catch |err| { 96 | logger.err("An error occurred during host bridge scan {s}: {any}", .{ path, err }); 97 | }; 98 | } 99 | 100 | fn iterationCallback( 101 | _: ?*anyopaque, 102 | node: ?*uacpi.uacpi_namespace_node, 103 | ) callconv(.C) uacpi.uacpi_ns_iteration_decision { 104 | const path = uacpi.uacpi_namespace_node_generate_absolute_path(node); 105 | defer uacpi.uacpi_free_absolute_path(path); 106 | 107 | var node_info: ?*uacpi.x_uacpi_namespace_node_info = null; 108 | if (uacpi.uacpi_get_namespace_node_info(node, @ptrCast(&node_info)) != uacpi.UACPI_STATUS_OK) { 109 | logger.warn("Failed to get information for node {s}", .{path}); 110 | return uacpi.UACPI_NS_ITERATION_DECISION_CONTINUE; 111 | } 112 | 113 | const info = node_info.?; 114 | for (info.cid.ids()[0..info.cid.num_ids]) |cid_str| { 115 | const cid = cid_str.value[0 .. cid_str.size - 1]; 116 | if (std.mem.eql(u8, cid, "PNP0A03") or std.mem.eql(u8, cid, "PNP0A08")) { 117 | handlePciHostBridge(node); 118 | } else if (std.mem.eql(u8, cid, "PNP0303")) { 119 | ps2.init(node) catch |err| { 120 | logger.err("An error occurred during PS/2 keyboard initialization: {any}", .{err}); 121 | }; 122 | } 123 | } 124 | 125 | return uacpi.UACPI_NS_ITERATION_DECISION_CONTINUE; 126 | } 127 | -------------------------------------------------------------------------------- /kernel/src/apic.zig: -------------------------------------------------------------------------------- 1 | const logger = std.log.scoped(.apic); 2 | 3 | const std = @import("std"); 4 | 5 | const hpet = @import("./hpet.zig"); 6 | const interrupts = @import("./interrupts.zig"); 7 | const per_cpu = @import("./per_cpu.zig"); 8 | const scheduler = @import("./scheduler.zig"); 9 | const time = @import("./time.zig"); 10 | const uacpi = @import("./uacpi.zig"); 11 | const virt = @import("./virt.zig"); 12 | 13 | const APICRegister = enum(usize) { 14 | lapic_id = 0x20, 15 | eoi = 0xB0, 16 | spurious_vector = 0xF0, 17 | icr0 = 0x300, 18 | icr1 = 0x310, 19 | lvt_timer = 0x320, 20 | timer_initial_count = 0x380, 21 | timer_current_count = 0x390, 22 | timer_divide = 0x3E0, 23 | }; 24 | 25 | fn getLAPICRegister(reg: APICRegister) *volatile u32 { 26 | return @as(*volatile u32, @ptrFromInt(per_cpu.get().lapic_base + @intFromEnum(reg))); 27 | } 28 | 29 | const IOAPIC = struct { 30 | ioregsel: *volatile u32, 31 | iowin: *volatile u32, 32 | 33 | base_gsi: u32, 34 | gsi_count: u32, 35 | 36 | fn read(self: *const @This(), offset: u32) u32 { 37 | self.ioregsel.* = offset; 38 | return self.iowin.*; 39 | } 40 | 41 | fn write(self: *const @This(), offset: u32, value: u32) void { 42 | self.ioregsel.* = offset; 43 | self.iowin.* = value; 44 | } 45 | 46 | const RouteFlags = struct { 47 | delivery_mode: enum { fixed, lowest_priority, smi, nmi, init, ext_int }, 48 | pin_polarity: enum { high, low }, 49 | trigger_mode: enum { edge, level }, 50 | masked: bool, 51 | }; 52 | 53 | fn route(self: *const @This(), gsi: u32, lapic_id: u32, vector: u8, flags: RouteFlags) void { 54 | const delivery_mode: u3 = switch (flags.delivery_mode) { 55 | .fixed => 0b000, 56 | .lowest_priority => 0b001, 57 | .smi => 0b010, 58 | .nmi => 0b100, 59 | .init => 0b101, 60 | .ext_int => 0b111, 61 | }; 62 | 63 | const offset = 0x10 + (gsi - self.base_gsi) * 2; 64 | const entry: u64 = vector | 65 | @as(u64, delivery_mode) << 8 | 66 | @as(u64, @intFromBool(flags.pin_polarity == .low)) << 13 | 67 | @as(u64, @intFromBool(flags.trigger_mode == .level)) << 15 | 68 | @as(u64, @intFromBool(flags.masked)) << 16 | 69 | @as(u64, @intCast(lapic_id)) << 56; 70 | 71 | self.write(offset, @truncate(entry)); 72 | self.write(offset + 1, @intCast(entry >> 32)); 73 | } 74 | }; 75 | 76 | const InterruptSourceOverride = struct { 77 | bus: u8, 78 | gsi: u8, 79 | flags: u16, 80 | }; 81 | 82 | var timer_ticks_per_ms: u64 = 0; 83 | var ioapics: std.BoundedArray(IOAPIC, 16) = .{}; 84 | var isos = [1]?InterruptSourceOverride{null} ** 16; 85 | 86 | fn timerHandler(frame: *interrupts.InterruptFrame) void { 87 | const increment: time.Timespec = .{ .seconds = 0, .nanoseconds = std.time.ns_per_ms }; 88 | time.setClock(.monotonic, time.getClock(.monotonic).add(increment)); 89 | time.setClock(.realtime, time.getClock(.realtime).add(increment)); 90 | scheduler.reschedule(frame); 91 | eoi(); 92 | } 93 | 94 | pub fn init() void { 95 | getLAPICRegister(.spurious_vector).* = @as(u32, 0xFF) | 0x100; 96 | 97 | // Calibrate the LAPIC timer using the HPET 98 | getLAPICRegister(.lvt_timer).* = (1 << 16); // Masked, one shot 99 | getLAPICRegister(.timer_divide).* = 0b0001; // Divide by 4 100 | 101 | const initial_count = 0x1000_0000; 102 | getLAPICRegister(.timer_initial_count).* = initial_count; 103 | 104 | hpet.sleep(std.time.ns_per_ms * 10, false); 105 | const count = getLAPICRegister(.timer_current_count).*; 106 | std.debug.assert(count != 0); 107 | 108 | timer_ticks_per_ms = (initial_count - count) / 10; 109 | logger.info("LAPIC timer ticks at {d} ticks/ms", .{timer_ticks_per_ms}); 110 | 111 | const timer_vector = interrupts.allocateVector(); 112 | interrupts.registerHandler(timer_vector, timerHandler); 113 | 114 | getLAPICRegister(.lvt_timer).* = @as(u32, timer_vector) | (1 << 17); // Periodic mode 115 | getLAPICRegister(.timer_initial_count).* = @intCast(timer_ticks_per_ms); 116 | } 117 | 118 | pub fn eoi() void { 119 | getLAPICRegister(.eoi).* = 0; 120 | } 121 | 122 | pub fn getLapicID() u32 { 123 | return getLAPICRegister(.lapic_id).* >> 24; 124 | } 125 | 126 | pub fn routeISAIRQ(irq: u8, lapic_id: u32, vector: u8, masked: bool) bool { 127 | var gsi = irq; 128 | var route_flags: IOAPIC.RouteFlags = .{ 129 | .delivery_mode = .fixed, 130 | .pin_polarity = .high, 131 | .trigger_mode = .edge, 132 | .masked = masked, 133 | }; 134 | 135 | if (isos[irq]) |iso| { 136 | gsi = @intCast(iso.gsi); 137 | switch (iso.flags & uacpi.ACPI_MADT_POLARITY_MASK) { 138 | uacpi.ACPI_MADT_POLARITY_ACTIVE_LOW => route_flags.pin_polarity = .low, 139 | uacpi.ACPI_MADT_POLARITY_ACTIVE_HIGH => route_flags.pin_polarity = .high, 140 | else => {}, 141 | } 142 | switch (iso.flags & uacpi.ACPI_MADT_TRIGGERING_MASK) { 143 | uacpi.ACPI_MADT_TRIGGERING_EDGE => route_flags.trigger_mode = .edge, 144 | uacpi.ACPI_MADT_TRIGGERING_LEVEL => route_flags.trigger_mode = .level, 145 | else => {}, 146 | } 147 | } 148 | 149 | var responsible_ioapic: ?*const IOAPIC = null; 150 | for (ioapics.constSlice()) |*it| { 151 | if (it.base_gsi <= irq and it.base_gsi + it.gsi_count > irq) { 152 | responsible_ioapic = it; 153 | break; 154 | } 155 | } 156 | 157 | if (responsible_ioapic) |ioapic| { 158 | ioapic.route(gsi, lapic_id, vector, route_flags); 159 | return true; 160 | } 161 | 162 | logger.warn("Failed to route ISA IRQ {d}, could not find responsible IOAPIC", .{irq}); 163 | return false; 164 | } 165 | 166 | pub fn handleIOAPIC(address: u32, base_gsi: u32) void { 167 | var ioapic: IOAPIC = .{ 168 | .ioregsel = virt.asHigherHalfUncached(*volatile u32, address), 169 | .iowin = virt.asHigherHalfUncached(*volatile u32, address + 0x10), 170 | .base_gsi = base_gsi, 171 | .gsi_count = undefined, 172 | }; 173 | 174 | ioapic.gsi_count = (ioapic.read(0x1) >> 16) & 0xFF; 175 | ioapics.append(ioapic) catch @panic("Exceeded maximum number of IOAPICs"); 176 | } 177 | 178 | pub fn handleIOAPICISO(bus: u8, irq: u8, gsi: u32, flags: u16) void { 179 | std.debug.assert(isos[irq] == null); 180 | isos[irq] = .{ .bus = bus, .gsi = @intCast(gsi), .flags = flags }; 181 | } 182 | -------------------------------------------------------------------------------- /kernel/src/arch.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | const interrupts = @import("./interrupts.zig"); 4 | 5 | pub inline fn readEFlags() u64 { 6 | return asm volatile ( 7 | \\pushf 8 | \\pop %[result] 9 | : [result] "=r" (-> u64), 10 | ); 11 | } 12 | 13 | pub inline fn hlt() void { 14 | asm volatile ("hlt"); 15 | } 16 | 17 | pub inline fn out(comptime T: type, port: u16, value: T) void { 18 | switch (T) { 19 | u8 => asm volatile ("outb %[val], %[port]" 20 | : 21 | : [val] "{al}" (value), 22 | [port] "N{dx}" (port), 23 | ), 24 | u16 => asm volatile ("outw %[val], %[port]" 25 | : 26 | : [val] "{ax}" (value), 27 | [port] "N{dx}" (port), 28 | ), 29 | u32 => asm volatile ("outl %[val], %[port]" 30 | : 31 | : [val] "{eax}" (value), 32 | [port] "N{dx}" (port), 33 | ), 34 | else => @compileError("No port out instruction is available for type " ++ @typeName(T)), 35 | } 36 | } 37 | 38 | pub inline fn in(comptime T: type, port: u16) T { 39 | return switch (T) { 40 | u8 => asm volatile ("inb %[port], %[result]" 41 | : [result] "={al}" (-> T), 42 | : [port] "N{dx}" (port), 43 | ), 44 | u16 => asm volatile ("inw %[port], %[result]" 45 | : [result] "={ax}" (-> T), 46 | : [port] "N{dx}" (port), 47 | ), 48 | u32 => asm volatile ("inl %[port], %[result]" 49 | : [result] "={eax}" (-> T), 50 | : [port] "N{dx}" (port), 51 | ), 52 | else => @compileError("No port in instruction is available for type " ++ @typeName(T)), 53 | }; 54 | } 55 | 56 | pub const MSR = enum(u32) { 57 | IA32_APIC_BASE = 0x1B, 58 | IA32_FS_BASE = 0xC0000100, 59 | IA32_GS_BASE = 0xC0000101, 60 | IA32_KERNEL_GS_BASE = 0xC0000102, 61 | _, 62 | }; 63 | 64 | pub inline fn rdmsr(msr: MSR) u64 { 65 | var low: u32 = undefined; 66 | var high: u32 = undefined; 67 | asm volatile ("rdmsr" 68 | : [_] "={eax}" (low), 69 | [_] "={edx}" (high), 70 | : [_] "{ecx}" (@intFromEnum(msr)), 71 | ); 72 | return @as(u64, low) | (@as(u64, high) << 32); 73 | } 74 | 75 | pub inline fn wrmsr(msr: MSR, value: u64) void { 76 | asm volatile ("wrmsr" 77 | : 78 | : [_] "{eax}" (value & 0xFFFFFFFF), 79 | [_] "{edx}" (value >> 32), 80 | [_] "{ecx}" (@intFromEnum(msr)), 81 | ); 82 | } 83 | 84 | const TableRegister = extern struct { 85 | limit: u16, 86 | base: u64 align(2), 87 | }; 88 | 89 | pub const TSS = extern struct { 90 | reserved: u32, 91 | rsp: [3]u64 align(4), 92 | reserved0: u64 align(4), 93 | ist: [7]u64 align(4), 94 | reserved1: u32, 95 | reserved2: u32, 96 | reserved3: u16, 97 | iopb_offset: u16, 98 | }; 99 | 100 | pub const IDT = struct { 101 | const Entry = packed struct(u128) { 102 | offset_low: u16, 103 | selector: u16, 104 | ist: u8, 105 | flags: u8, 106 | offset_mid: u16, 107 | offset_high: u32, 108 | reserved: u32, 109 | 110 | pub fn init(offset: u64, ist: u8, flags: u8) Entry { 111 | return .{ 112 | .offset_low = @truncate(offset), 113 | .selector = 0x28, 114 | .ist = ist, 115 | .flags = flags, 116 | .offset_mid = @truncate(offset >> 16), 117 | .offset_high = @intCast(offset >> 32), 118 | .reserved = 0, 119 | }; 120 | } 121 | }; 122 | 123 | entries: [256]Entry = undefined, 124 | 125 | pub fn load(self: *@This(), has_tss: bool) void { 126 | const idtr: TableRegister = .{ .limit = @sizeOf(IDT) - 1, .base = @intFromPtr(self) }; 127 | for (interrupts.getInterruptHandlers(), 0..) |handler, i| { 128 | const flags: u8 = 0x8E | if (i == interrupts.sched_call_vector) @as(u8, 3 << 5) else 0; 129 | const ist: u8 = if (has_tss) switch (i) { 130 | 0xE => 3, 131 | interrupts.sched_call_vector => 2, 132 | else => 1, 133 | } else 0; 134 | self.entries[i] = Entry.init(@intFromPtr(handler), ist, flags); 135 | } 136 | asm volatile ("lidt (%[idtr])" 137 | : 138 | : [idtr] "r" (&idtr), 139 | ); 140 | } 141 | }; 142 | 143 | pub const GDT = struct { 144 | const Entry = packed struct(u64) { 145 | limit: u16, 146 | base_low: u16, 147 | base_mid: u8, 148 | flags: u8, 149 | granularity: u8, 150 | base_high: u8, 151 | }; 152 | 153 | const SystemEntry = packed struct(u128) { 154 | lower: Entry, 155 | base_high_ex: u32, 156 | reserved: u32, 157 | }; 158 | 159 | entries: [11]u64 = .{ 160 | 0x0000000000000000, // null 161 | 0x00009A000000FFFF, // 16-bit code 162 | 0x000093000000FFFF, // 16-bit data 163 | 0x00CF9A000000FFFF, // 32-bit code 164 | 0x00CF93000000FFFF, // 32-bit data 165 | 0x00AF9B000000FFFF, // 64-bit code 166 | 0x00AF93000000FFFF, // 64-bit data 167 | 0x00AFFB000000FFFF, // usermode 64-bit code 168 | 0x00AFF3000000FFFF, // usermode 64-bit data 169 | 0x0000000000000000, // tss low 170 | 0x0000000000000000, // tss high 171 | }, 172 | 173 | pub fn load(self: *@This()) void { 174 | const gdtr: TableRegister = .{ .limit = @sizeOf(GDT) - 1, .base = @intFromPtr(self) }; 175 | asm volatile ( 176 | \\lgdt (%[gdtr]) 177 | \\push $0x28 178 | \\lea 1f(%%rip), %%rax 179 | \\push %%rax 180 | \\lretq 181 | \\1: 182 | \\mov $0x30, %%ax 183 | \\mov %%ax, %%ds 184 | \\mov %%ax, %%es 185 | \\mov %%ax, %%ss 186 | : 187 | : [gdtr] "r" (&gdtr), 188 | ); 189 | } 190 | 191 | pub fn loadTSS(self: *@This(), tss: *TSS) void { 192 | const tss_entry = @as(*SystemEntry, @ptrCast(&self.entries[9])); 193 | const tss_address = @intFromPtr(tss); 194 | tss_entry.* = .{ 195 | .lower = .{ 196 | .limit = @sizeOf(TSS) - 1, 197 | .base_low = @truncate(tss_address), 198 | .base_mid = @truncate(tss_address >> 16), 199 | .flags = 0b10001001, 200 | .granularity = 0, 201 | .base_high = @truncate(tss_address >> 24), 202 | }, 203 | .base_high_ex = @intCast(tss_address >> 32), 204 | .reserved = 0, 205 | }; 206 | asm volatile ( 207 | \\mov $0x48, %%ax 208 | \\ltr %%ax 209 | ); 210 | } 211 | }; 212 | -------------------------------------------------------------------------------- /kernel/src/containers/bit_fields.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | fn PtrCastPreserveCV(comptime T: type, comptime PtrToT: type, comptime NewT: type) type { 4 | return switch (PtrToT) { 5 | *T => *NewT, 6 | *const T => *const NewT, 7 | *volatile T => *volatile NewT, 8 | *const volatile T => *const volatile NewT, 9 | 10 | else => @compileError("wtf you doing"), 11 | }; 12 | } 13 | 14 | fn BitType(comptime FieldType: type, comptime ValueType: type, comptime shamt: usize) type { 15 | const self_bit: FieldType = (1 << shamt); 16 | 17 | return struct { 18 | bits: BitField(FieldType, shamt, 1), 19 | 20 | pub fn set(self: anytype) void { 21 | self.bits.field().* |= self_bit; 22 | } 23 | 24 | pub fn unset(self: anytype) void { 25 | self.bits.field().* &= ~self_bit; 26 | } 27 | 28 | pub fn read(self: anytype) ValueType { 29 | return @bitCast(ValueType, @truncate(u1, self.bits.field().* >> shamt)); 30 | } 31 | 32 | // Since these are mostly used with MMIO, I want to avoid 33 | // reading the memory just to write it again, also races 34 | pub fn write(self: anytype, val: ValueType) void { 35 | if (@bitCast(bool, val)) { 36 | self.set(); 37 | } else { 38 | self.unset(); 39 | } 40 | } 41 | }; 42 | } 43 | 44 | pub fn Bit(comptime FieldType: type, comptime shamt: usize) type { 45 | return BitType(FieldType, u1, shamt); 46 | } 47 | 48 | pub fn Boolean(comptime FieldType: type, comptime shamt: usize) type { 49 | return BitType(FieldType, bool, shamt); 50 | } 51 | 52 | pub fn BitField(comptime FieldType: type, comptime shamt: usize, comptime num_bits: usize) type { 53 | if (shamt + num_bits > @bitSizeOf(FieldType)) { 54 | @compileError("bit field doesn't fit"); 55 | } 56 | 57 | const self_mask: FieldType = ((1 << num_bits) - 1) << shamt; 58 | 59 | const ValueType = std.meta.Int(.unsigned, num_bits); 60 | 61 | return struct { 62 | dummy: FieldType, 63 | 64 | fn field(self: anytype) PtrCastPreserveCV(@This(), @TypeOf(self), FieldType) { 65 | return @ptrCast(PtrCastPreserveCV(@This(), @TypeOf(self), FieldType), self); 66 | } 67 | 68 | pub fn write(self: anytype, val: ValueType) void { 69 | self.field().* &= ~self_mask; 70 | self.field().* |= @intCast(FieldType, val) << shamt; 71 | } 72 | 73 | pub fn read(self: anytype) ValueType { 74 | const val: FieldType = self.field().*; 75 | return @intCast(ValueType, (val & self_mask) >> shamt); 76 | } 77 | }; 78 | } 79 | -------------------------------------------------------------------------------- /kernel/src/containers/ring_buffer.zig: -------------------------------------------------------------------------------- 1 | // https://github.com/FlorenceOS/Florence/blob/master/lib/containers/ring_buffer.zig 2 | 3 | const scheduler = @import("../scheduler.zig"); 4 | 5 | pub fn RingBuffer(comptime T: type, comptime max_size: usize) type { 6 | if (@popCount(max_size) != 1) { 7 | // Required both for mask() and `written`/`read` overflowing 8 | @compileError("Size must be a power of 2!"); 9 | } 10 | 11 | return struct { 12 | written: usize = 0, 13 | read: usize = 0, 14 | 15 | elements: [max_size]T = undefined, 16 | 17 | fn mask(value: usize) usize { 18 | return value & (max_size - 1); 19 | } 20 | 21 | pub fn size(self: @This()) usize { 22 | return self.written - self.read; 23 | } 24 | 25 | pub fn empty(self: @This()) bool { 26 | return self.size() == 0; 27 | } 28 | 29 | pub fn full(self: @This()) bool { 30 | return self.size() == max_size; 31 | } 32 | 33 | // Peek at the next writable slot, call send() when done writing to 34 | // If you don't want to write, no further action is needed 35 | pub fn peekWrite(self: *@This()) ?*T { 36 | if (self.full()) { 37 | return null; 38 | } else { 39 | return &self.elements[mask(self.written)]; 40 | } 41 | } 42 | 43 | pub fn send(self: *@This()) void { 44 | // Overflowing here is fine, max size is power of 2 45 | self.written +%= 1; 46 | } 47 | 48 | pub fn push(self: *@This(), value: T) bool { 49 | if (self.peekWrite()) |elem| { 50 | elem.* = value; 51 | self.send(); 52 | return true; 53 | } else { 54 | return false; 55 | } 56 | } 57 | 58 | pub fn peek(self: *@This()) ?*T { 59 | if (self.empty()) { 60 | return null; 61 | } else { 62 | return &self.elements[mask(self.read)]; 63 | } 64 | } 65 | 66 | pub fn drop(self: *@This()) void { 67 | // Overflowing here is fine, max size is power of 2 68 | self.read +%= 1; 69 | } 70 | 71 | pub fn pop(self: *@This()) ?T { 72 | if (self.peek()) |value| { 73 | const v = value.*; 74 | self.drop(); 75 | return v; 76 | } else { 77 | return null; 78 | } 79 | } 80 | }; 81 | } 82 | 83 | // A RingBuffer that you can wait on and that also tracks the number of dropped elements in case the buffer is full when pushed to. 84 | // Pushing can be done from an interrupt context, but popping cannot. 85 | pub fn RingWaitQueue(comptime T: type, comptime max_size: usize) type { 86 | return struct { 87 | num_dropped: usize = 0, 88 | semaphore: scheduler.Semaphore = scheduler.Semaphore.init(0), 89 | buffer: RingBuffer(T, max_size) = .{}, 90 | 91 | pub fn push(self: *@This(), value: T) bool { 92 | if (self.buffer.push(value)) { 93 | self.semaphore.release(1); 94 | return true; 95 | } else { 96 | _ = @atomicRmw(usize, &self.num_dropped, .Add, 1, .acq_rel); 97 | return false; 98 | } 99 | } 100 | 101 | pub fn get(self: *@This()) T { 102 | while (true) { 103 | if (self.buffer.pop()) |p| { 104 | return p; 105 | } 106 | 107 | self.semaphore.acquire(1); 108 | } 109 | } 110 | 111 | // Set the number of dropped elements to 0 and return old value 112 | pub fn dropped(self: *@This()) usize { 113 | return @atomicRmw(usize, &self.num_dropped, .Xchg, 0, .acq_rel); 114 | } 115 | }; 116 | } 117 | -------------------------------------------------------------------------------- /kernel/src/debug.zig: -------------------------------------------------------------------------------- 1 | const logger = std.log.scoped(.debug); 2 | 3 | const root = @import("root"); 4 | const std = @import("std"); 5 | const limine = @import("limine"); 6 | const builtin = @import("builtin"); 7 | 8 | const arch = @import("arch.zig"); 9 | const virt = @import("virt.zig"); 10 | 11 | var debug_allocator_bytes: [16 * 1024 * 1024]u8 = undefined; 12 | var debug_allocator = std.heap.FixedBufferAllocator.init(debug_allocator_bytes[0..]); 13 | var debug_info: ?std.dwarf.DwarfInfo = null; 14 | 15 | pub fn printStackIterator(stack_iter: std.debug.StackIterator) void { 16 | logger.err("Stack backtrace:", .{}); 17 | var iter = stack_iter; 18 | while (iter.next()) |addr| { 19 | printSymbol(addr); 20 | } 21 | } 22 | 23 | pub fn printStackTrace(stack_trace: *std.builtin.StackTrace) void { 24 | logger.err("Stack backtrace:", .{}); 25 | 26 | var frame_index: usize = 0; 27 | var frames_left: usize = @min(stack_trace.index, stack_trace.instruction_addresses.len); 28 | 29 | while (frames_left != 0) : ({ 30 | frames_left -= 1; 31 | frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len; 32 | }) { 33 | const return_address = stack_trace.instruction_addresses[frame_index]; 34 | printSymbol(return_address); 35 | } 36 | } 37 | 38 | pub fn debugPrint(string: []const u8) void { 39 | for (string) |byte| { 40 | arch.out(u8, 0xE9, byte); 41 | } 42 | } 43 | 44 | pub fn init(kernel_file_res: *limine.KernelFileResponse) !void { 45 | if (debug_info != null) { 46 | return; 47 | } 48 | 49 | errdefer debug_info = null; 50 | 51 | const kernel_file = kernel_file_res.kernel_file; 52 | var sections = std.dwarf.DwarfInfo.null_section_array; 53 | sections[@intFromEnum(std.dwarf.DwarfSection.debug_info)] = try getSectionSlice(kernel_file.address, ".debug_info"); 54 | sections[@intFromEnum(std.dwarf.DwarfSection.debug_abbrev)] = try getSectionSlice(kernel_file.address, ".debug_abbrev"); 55 | sections[@intFromEnum(std.dwarf.DwarfSection.debug_str)] = try getSectionSlice(kernel_file.address, ".debug_str"); 56 | sections[@intFromEnum(std.dwarf.DwarfSection.debug_line)] = try getSectionSlice(kernel_file.address, ".debug_line"); 57 | sections[@intFromEnum(std.dwarf.DwarfSection.debug_ranges)] = try getSectionSlice(kernel_file.address, ".debug_ranges"); 58 | 59 | debug_info = .{ 60 | .endian = .little, 61 | .is_macho = false, 62 | .sections = sections, 63 | }; 64 | 65 | try std.dwarf.openDwarfDebugInfo(&debug_info.?, debug_allocator.allocator()); 66 | } 67 | 68 | fn printInfo(address: u64, symbol_name: []const u8, file_name_: []const u8, line: usize) void { 69 | var file_name = file_name_; 70 | 71 | if (std.mem.startsWith(u8, file_name, "/base_dir")) { 72 | while (!std.mem.startsWith(u8, file_name, "kernel/")) { 73 | const idx = std.mem.indexOf(u8, file_name, "/") orelse break; 74 | file_name = file_name[idx + 1 ..]; 75 | } 76 | } 77 | 78 | logger.err(" 0x{X:0>16}: {s} at {s}:{d}", .{ address, symbol_name, file_name, line }); 79 | } 80 | 81 | fn printSymbol(address: u64) void { 82 | var symbol_name: []const u8 = ""; 83 | 84 | if (debug_info) |*info| brk: { 85 | if (info.getSymbolName(address)) |name| { 86 | symbol_name = name; 87 | } 88 | 89 | const compile_unit = info.findCompileUnit(address) catch break :brk; 90 | const line_info = info.getLineNumberInfo(debug_allocator.allocator(), compile_unit.*, address) catch break :brk; 91 | 92 | return printInfo(address, symbol_name, line_info.file_name, line_info.line); 93 | } 94 | 95 | printInfo(address, symbol_name, "??", 0); 96 | } 97 | 98 | fn getSectionData(elf: [*]const u8, shdr: []const u8) []const u8 { 99 | const offset = @as(usize, @intCast(std.mem.readInt(u64, shdr[24..][0..8], .little))); 100 | const size = @as(usize, @intCast(std.mem.readInt(u64, shdr[32..][0..8], .little))); 101 | 102 | return elf[offset .. offset + size]; 103 | } 104 | 105 | fn getSectionName(names: []const u8, shdr: []const u8) ?[]const u8 { 106 | const offset = @as(usize, @intCast(std.mem.readInt(u32, shdr[0..][0..4], .little))); 107 | const len = std.mem.indexOf(u8, names[offset..], "\x00") orelse return null; 108 | 109 | return names[offset .. offset + len]; 110 | } 111 | 112 | fn getShdr(elf: [*]const u8, idx: u16) []const u8 { 113 | const sh_offset = std.mem.readInt(u64, elf[40 .. 40 + 8], .little); 114 | const sh_entsize = std.mem.readInt(u16, elf[58 .. 58 + 2], .little); 115 | const off = sh_offset + sh_entsize * @as(usize, @intCast(idx)); 116 | 117 | return elf[off .. off + sh_entsize]; 118 | } 119 | 120 | fn getSectionSlice(elf: [*]const u8, section_name: []const u8) !std.dwarf.DwarfInfo.Section { 121 | const sh_strndx = std.mem.readInt(u16, elf[62 .. 62 + 2], .little); 122 | const sh_num = std.mem.readInt(u16, elf[60 .. 60 + 2], .little); 123 | 124 | if (sh_strndx > sh_num) { 125 | return error.ShstrndxOutOfRange; 126 | } 127 | 128 | const section_names = getSectionData(elf, getShdr(elf, sh_strndx)); 129 | 130 | var i: u16 = 0; 131 | 132 | while (i < sh_num) : (i += 1) { 133 | const header = getShdr(elf, i); 134 | 135 | if (std.mem.eql(u8, getSectionName(section_names, header) orelse continue, section_name)) { 136 | const data = getSectionData(elf, header); 137 | return .{ .data = data, .owned = false }; 138 | } 139 | } 140 | 141 | return error.SectionNotFound; 142 | } 143 | -------------------------------------------------------------------------------- /kernel/src/drivers.zig: -------------------------------------------------------------------------------- 1 | pub const pci_drivers = .{ 2 | // @import("drivers/ahci.zig").pci_driver, 3 | @import("drivers/xhci.zig").pci_driver, 4 | }; 5 | 6 | pub const PCIDriverDiscovery = union(enum) { 7 | all, 8 | id: struct { 9 | vendor: u16, 10 | device: u16, 11 | }, 12 | class: struct { 13 | class_id: u8, 14 | subclass_id: u8, 15 | prog_if: ?u8, 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /kernel/src/drivers/input.zig: -------------------------------------------------------------------------------- 1 | const logger = std.log.scoped(.input); 2 | 3 | const std = @import("std"); 4 | 5 | // zig fmt: off 6 | pub const KeyLocation = enum { 7 | Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, 8 | F13, F14, F15, F16, F17, F18, F19, F20, F21, F22, F23, F24, 9 | 10 | // Number row 11 | LeftOf1, Number1, Number2, Number3, Number4, Number5, 12 | Number6, Number7, Number8, Number9, Number0, RightOf0, 13 | LeftOfBackspace, Backspace, 14 | 15 | // First QWERTY row 16 | Tab, Line1n1, Line1n2, Line1n3, Line1n4, Line1n5, 17 | Line1n6, Line1n7, Line1n8, Line1n9, Line1n10, Line1n11, 18 | Line1n12, Line1n13, 19 | 20 | // Second QWERTY row 21 | CapsLock, Line2n1, Line2n2, Line2n3, Line2n4, Line2n5, 22 | Line2n6, Line2n7, Line2n8, Line2n9, Line2n10, Line2n11, 23 | NonUsPound, Enter, 24 | 25 | // Third QWERTY row 26 | LeftShift, NonUsBackslash, Line3n1, Line3n2, Line3n3, 27 | Line3n4, Line3n5, Line3n6, Line3n7, Line3n8, Line3n9, 28 | Line3n10, RightShift, 29 | 30 | // Bottom row 31 | LeftCtrl, LeftSuper, LeftAlt, Spacebar, RightAlt, 32 | RightSuper, MenuKey, RightCtrl, 33 | 34 | // Directional keys 35 | ArrowUp, ArrowLeft, ArrowDown, ArrowRight, 36 | 37 | // Group above directional keys 38 | PrintScreen, PauseBreak, ScrollLock, Insert, Home, PageUp, 39 | Delete, End, PageDown, 40 | 41 | // Numpad 42 | Numlock, NumpadDiv, NumpadMul, Numpad7, Numpad8, Numpad9, 43 | NumpadSub, Numpad4, Numpad5, Numpad6, NumpadAdd, Numpad1, 44 | Numpad2, Numpad3, Numpad0, NumpadPoint, NumpadEnter, 45 | 46 | // Multimedia keys 47 | MediaStop, MediaRewind, MediaPausePlay, MediaForward, 48 | MediaMute, MediaVolumeUp, MediaVolumeDown, 49 | }; 50 | // zig fmt: on 51 | 52 | pub const KeyboardEvent = struct { 53 | location: KeyLocation, 54 | pressed: bool, 55 | }; 56 | 57 | pub const KeyboardState = struct { 58 | const State = std.PackedIntArray(bool, @typeInfo(KeyLocation).Enum.fields.len); 59 | 60 | is_pressed: State = blk: { 61 | @setEvalBranchQuota(9999999); 62 | break :blk State.initAllTo(false); 63 | }, 64 | 65 | pub fn isPressed(self: *KeyboardState, location: KeyLocation) bool { 66 | return self.is_pressed.get(@intFromEnum(location)); 67 | } 68 | 69 | pub fn isCtrlPressed(self: *KeyboardState) bool { 70 | return self.isPressed(.LeftCtrl) or self.isPressed(.RightCtrl); 71 | } 72 | 73 | pub fn isAltPressed(self: *KeyboardState) bool { 74 | return self.isPressed(.LeftAlt) or self.isPressed(.RightAlt); 75 | } 76 | 77 | pub fn isShiftPressed(self: *KeyboardState) bool { 78 | return self.isPressed(.LeftShift) or self.isPressed(.RightShift); 79 | } 80 | 81 | pub fn isSuperPressed(self: *KeyboardState) bool { 82 | return self.isPressed(.LeftSuper) or self.isPressed(.RightSuper); 83 | } 84 | 85 | fn processEvent(self: *KeyboardState, event: KeyboardEvent) void { 86 | self.is_pressed.set(@intFromEnum(event.location), event.pressed); 87 | } 88 | }; 89 | 90 | const RingWaitQueue = @import("../containers/ring_buffer.zig").RingWaitQueue; 91 | 92 | var keyboard_queue: RingWaitQueue(KeyboardEvent, 128) = .{}; 93 | 94 | pub var keyboard_state: KeyboardState = .{}; 95 | 96 | pub fn enqueueKeyboardEvent(event: KeyboardEvent) bool { 97 | return keyboard_queue.push(event); 98 | } 99 | 100 | pub fn dequeueKeyboardEvent() KeyboardEvent { 101 | const event = keyboard_queue.get(); 102 | const dropped = keyboard_queue.dropped(); 103 | 104 | if (dropped > 0) { 105 | logger.warn("Dropped {} keyboard events", .{dropped}); 106 | } 107 | 108 | keyboard_state.processEvent(event); 109 | return event; 110 | } 111 | -------------------------------------------------------------------------------- /kernel/src/drivers/ps2.zig: -------------------------------------------------------------------------------- 1 | const logger = std.log.scoped(.ps2); 2 | 3 | const root = @import("root"); 4 | const std = @import("std"); 5 | 6 | const arch = @import("../arch.zig"); 7 | const apic = @import("../apic.zig"); 8 | const interrupts = @import("../interrupts.zig"); 9 | const scheduler = @import("../scheduler.zig"); 10 | const uacpi = @import("../uacpi.zig"); 11 | const input = @import("./input.zig"); 12 | 13 | var keyboard_buffer: std.BoundedArray(u8, 8) = .{}; 14 | 15 | fn finishSequence(offset: usize, sequence: []const u8) bool { 16 | const buffer = keyboard_buffer.slice()[offset..]; 17 | 18 | if (buffer.len >= sequence.len) { 19 | if (std.mem.eql(u8, buffer, sequence)) { 20 | keyboard_buffer.resize(0) catch unreachable; 21 | return true; 22 | } else { 23 | std.debug.panic("Unexpected scancode sequence: {any}, expected: {any}", .{ buffer, sequence }); 24 | } 25 | } 26 | 27 | return false; 28 | } 29 | 30 | fn keyLocation(extended: bool, scan_code: u8) ?input.KeyLocation { 31 | if (extended) { 32 | return switch (scan_code) { 33 | 0x10 => .MediaRewind, 34 | 0x19 => .MediaForward, 35 | 0x1C => .NumpadEnter, 36 | 0x1D => .RightCtrl, 37 | 0x20 => .MediaMute, 38 | 0x22 => .MediaPausePlay, 39 | 0x24 => .MediaStop, 40 | 0x2E => .MediaVolumeDown, 41 | 0x30 => .MediaVolumeUp, 42 | 0x35 => .NumpadDiv, 43 | 0x38 => .RightAlt, 44 | 0x47 => .Home, 45 | 0x48 => .ArrowUp, 46 | 0x49 => .PageUp, 47 | 0x4B => .ArrowLeft, 48 | 0x4D => .ArrowRight, 49 | 0x4F => .End, 50 | 0x50 => .ArrowDown, 51 | 0x51 => .PageDown, 52 | 0x52 => .Insert, 53 | 0x53 => .Delete, 54 | 0x5B => .LeftSuper, 55 | 0x5C => .RightSuper, 56 | 0x5D => .MenuKey, 57 | else => return null, 58 | }; 59 | } else { 60 | return switch (scan_code) { 61 | 0x01 => .Escape, 62 | 0x02 => .Number1, 63 | 0x03 => .Number2, 64 | 0x04 => .Number3, 65 | 0x05 => .Number4, 66 | 0x06 => .Number5, 67 | 0x07 => .Number6, 68 | 0x08 => .Number7, 69 | 0x09 => .Number8, 70 | 0x0A => .Number9, 71 | 0x0B => .Number0, 72 | 0x0C => .RightOf0, 73 | 0x0D => .LeftOfBackspace, 74 | 0x0E => .Backspace, 75 | 0x0F => .Tab, 76 | 0x10 => .Line1n1, 77 | 0x11 => .Line1n2, 78 | 0x12 => .Line1n3, 79 | 0x13 => .Line1n4, 80 | 0x14 => .Line1n5, 81 | 0x15 => .Line1n6, 82 | 0x16 => .Line1n7, 83 | 0x17 => .Line1n8, 84 | 0x18 => .Line1n9, 85 | 0x19 => .Line1n10, 86 | 0x1A => .Line1n11, 87 | 0x1B => .Line1n12, 88 | 0x2B => .Line1n13, 89 | 0x1C => .Enter, 90 | 0x1D => .LeftCtrl, 91 | 0x1E => .Line2n1, 92 | 0x1F => .Line2n2, 93 | 0x20 => .Line2n3, 94 | 0x21 => .Line2n4, 95 | 0x22 => .Line2n5, 96 | 0x23 => .Line2n6, 97 | 0x24 => .Line2n7, 98 | 0x25 => .Line2n8, 99 | 0x26 => .Line2n9, 100 | 0x27 => .Line2n10, 101 | 0x28 => .Line2n11, 102 | 0x29 => .LeftOf1, 103 | 0x2A => .LeftShift, 104 | 0x2C => .Line3n1, 105 | 0x2D => .Line3n2, 106 | 0x2E => .Line3n3, 107 | 0x2F => .Line3n4, 108 | 0x30 => .Line3n5, 109 | 0x31 => .Line3n6, 110 | 0x32 => .Line3n7, 111 | 0x33 => .Line3n8, 112 | 0x34 => .Line3n9, 113 | 0x35 => .Line3n10, 114 | 0x36 => .RightShift, 115 | 0x37 => .NumpadMul, 116 | 0x38 => .LeftAlt, 117 | 0x39 => .Spacebar, 118 | 0x3A => .CapsLock, 119 | 0x3B => .F1, 120 | 0x3C => .F2, 121 | 0x3D => .F3, 122 | 0x3E => .F4, 123 | 0x3F => .F5, 124 | 0x40 => .F6, 125 | 0x41 => .F7, 126 | 0x42 => .F8, 127 | 0x43 => .F9, 128 | 0x44 => .F10, 129 | 0x45 => .Numlock, 130 | 0x46 => .ScrollLock, 131 | 0x47 => .Numpad7, 132 | 0x48 => .Numpad8, 133 | 0x49 => .Numpad9, 134 | 0x4A => .NumpadSub, 135 | 0x4B => .Numpad4, 136 | 0x4C => .Numpad5, 137 | 0x4D => .Numpad6, 138 | 0x4E => .NumpadAdd, 139 | 0x4F => .Numpad1, 140 | 0x50 => .Numpad2, 141 | 0x51 => .Numpad3, 142 | 0x52 => .Numpad0, 143 | 0x53 => .NumpadPoint, 144 | // ... 145 | 0x56 => .NonUsBackslash, 146 | 0x57 => .F11, 147 | 0x58 => .F12, 148 | else => return null, 149 | }; 150 | } 151 | } 152 | 153 | fn standardKey(extended: bool, scan_code: u8) void { 154 | defer keyboard_buffer.resize(0) catch unreachable; 155 | const location = keyLocation(extended, scan_code & 0x7F) orelse { 156 | const kind: []const u8 = if (extended) "extended scancode" else "scancode"; 157 | logger.warn("Unknown {s}: 0x{X}", .{ kind, scan_code & 0x7F }); 158 | return; 159 | }; 160 | _ = input.enqueueKeyboardEvent(.{ .location = location, .pressed = scan_code & 0x80 == 0 }); 161 | } 162 | 163 | fn generateEvent() void { 164 | const buffer = keyboard_buffer.slice(); 165 | switch (buffer[0]) { 166 | 0xE1 => if (finishSequence(1, "\x1D\x45\xE1\x9D\xC5")) { 167 | _ = input.enqueueKeyboardEvent(.{ .location = .PauseBreak, .pressed = true }); 168 | _ = input.enqueueKeyboardEvent(.{ .location = .PauseBreak, .pressed = false }); 169 | }, 170 | 0xE0 => if (keyboard_buffer.len >= 2) { 171 | switch (buffer[1]) { 172 | 0x2A => if (finishSequence(2, &.{ 0xE0, 0x37 })) { 173 | _ = input.enqueueKeyboardEvent(.{ .location = .PrintScreen, .pressed = true }); 174 | }, 175 | 0xB7 => if (finishSequence(2, &.{ 0xE0, 0xAA })) { 176 | _ = input.enqueueKeyboardEvent(.{ .location = .PrintScreen, .pressed = false }); 177 | }, 178 | else => |byte| standardKey(true, byte), 179 | } 180 | }, 181 | else => |byte| standardKey(false, byte), 182 | } 183 | } 184 | 185 | const Ps2KeyboardContext = struct { 186 | path: [*:0]const u8, 187 | has_data_port: bool = false, 188 | data_port: u16 = 0x60, 189 | command_port: u16 = 0x64, 190 | irq: u8 = 0x1, 191 | }; 192 | 193 | fn keyboardHandler(context: interrupts.InterruptContext) void { 194 | const ps2_ctx: *Ps2KeyboardContext = @ptrCast(@alignCast(context.?)); 195 | keyboard_buffer.append(arch.in(u8, ps2_ctx.data_port)) catch {}; 196 | generateEvent(); 197 | apic.eoi(); 198 | } 199 | 200 | fn resourceIterationCallback( 201 | context: ?*anyopaque, 202 | resource: ?*uacpi.uacpi_resource, 203 | ) callconv(.C) uacpi.uacpi_resource_iteration_decision { 204 | const ps2_ctx: *Ps2KeyboardContext = @ptrCast(@alignCast(context.?)); 205 | switch (resource.?.type) { 206 | uacpi.UACPI_RESOURCE_TYPE_IRQ => { 207 | const irq_resource = &resource.?.unnamed_0.irq; 208 | std.debug.assert(irq_resource.num_irqs == 1); 209 | ps2_ctx.irq = irq_resource.irqs()[0]; 210 | }, 211 | uacpi.UACPI_RESOURCE_TYPE_IO => if (!ps2_ctx.has_data_port) { 212 | // Assume first IO resource is the data port 213 | ps2_ctx.data_port = resource.?.unnamed_0.io.minimum; 214 | ps2_ctx.has_data_port = true; 215 | } else { 216 | ps2_ctx.command_port = resource.?.unnamed_0.io.minimum; 217 | }, 218 | else => {}, 219 | } 220 | return uacpi.UACPI_RESOURCE_ITERATION_CONTINUE; 221 | } 222 | 223 | pub fn init(acpi_node: ?*uacpi.uacpi_namespace_node) !void { 224 | const path = uacpi.uacpi_namespace_node_generate_absolute_path(acpi_node); 225 | logger.info("Found PS/2 keyboard at {s}", .{path}); 226 | 227 | const context = try root.allocator.create(Ps2KeyboardContext); 228 | context.* = .{ .path = path }; 229 | 230 | var status: uacpi.uacpi_status = undefined; 231 | var resources: ?*uacpi.uacpi_resources = null; 232 | defer uacpi.uacpi_free_resources(resources); 233 | 234 | status = uacpi.uacpi_get_current_resources(acpi_node, &resources); 235 | if (status != uacpi.UACPI_STATUS_OK) { 236 | logger.err( 237 | "Failed to get resources for PS/2 keyboard {s}: {s}", 238 | .{ path, uacpi.uacpi_status_to_string(status) }, 239 | ); 240 | return; 241 | } 242 | 243 | status = uacpi.uacpi_for_each_resource(resources, resourceIterationCallback, context); 244 | if (status != uacpi.UACPI_STATUS_OK) { 245 | logger.err( 246 | "Failed to iterate resources for PS/2 keyboard {s}: {s}", 247 | .{ path, uacpi.uacpi_status_to_string(status) }, 248 | ); 249 | return; 250 | } 251 | 252 | logger.debug("PS/2 keyboard IRQ: {d}", .{context.irq}); 253 | logger.debug("PS/2 keyboard data port: 0x{X}, command port: 0x{X}", .{ context.data_port, context.command_port }); 254 | 255 | const keyboard_vector = interrupts.allocateVector(); 256 | interrupts.registerHandlerWithContext(keyboard_vector, keyboardHandler, context); 257 | std.debug.assert(apic.routeISAIRQ(context.irq, 0, keyboard_vector, false)); 258 | 259 | // Flush the keyboard buffer 260 | while (arch.in(u8, context.command_port) & (1 << 0) != 0) { 261 | _ = arch.in(u8, context.data_port); 262 | } 263 | 264 | logger.info("PS/2 keyboard initialized", .{}); 265 | } 266 | -------------------------------------------------------------------------------- /kernel/src/drivers/usb.zig: -------------------------------------------------------------------------------- 1 | const logger = std.log.scoped(.usb); 2 | 3 | const std = @import("std"); 4 | 5 | const xhci = @import("./xhci.zig"); 6 | const input = @import("./input.zig"); 7 | const phys = @import("../phys.zig"); 8 | 9 | fn keyboardUsageToKeyLocation(scancode: usize) ?input.KeyLocation { 10 | return switch (scancode) { 11 | 0x4 => .Line2n1, // Keyboard A 12 | 0x5 => .Line3n5, // Keyboard B 13 | 0x6 => .Line3n3, // Keyboard C 14 | 0x7 => .Line2n3, // Keyboard D 15 | 0x8 => .Line1n3, // Keyboard E 16 | 0x9 => .Line2n4, // Keyboard F 17 | 0xA => .Line2n5, // Keyboard G 18 | 0xB => .Line2n6, // Keyboard H 19 | 0xC => .Line1n8, // Keyboard I 20 | 0xD => .Line2n7, // Keyboard J 21 | 0xE => .Line2n8, // Keyboard K 22 | 0xF => .Line2n9, // Keyboard L 23 | 0x10 => .Line3n7, // Keyboard M 24 | 0x11 => .Line3n6, // Keyboard N 25 | 0x12 => .Line1n9, // Keyboard O 26 | 0x13 => .Line1n10, // Keyboard P 27 | 0x14 => .Line1n1, // Keyboard Q 28 | 0x15 => .Line1n4, // Keyboard R 29 | 0x16 => .Line2n2, // Keyboard S 30 | 0x17 => .Line1n5, // Keyboard T 31 | 0x18 => .Line1n7, // Keyboard U 32 | 0x19 => .Line3n4, // Keyboard V 33 | 0x1A => .Line1n2, // Keyboard W 34 | 0x1B => .Line3n2, // Keyboard X 35 | 0x1C => .Line1n6, // Keyboard Y 36 | 0x1D => .Line3n1, // Keyboard Z 37 | 0x1E => .Number1, // Keyboard 1 38 | 0x1F => .Number2, // Keyboard 2 39 | 0x20 => .Number3, // Keyboard 3 40 | 0x21 => .Number4, // Keyboard 4 41 | 0x22 => .Number5, // Keyboard 5 42 | 0x23 => .Number6, // Keyboard 6 43 | 0x24 => .Number7, // Keyboard 7 44 | 0x25 => .Number8, // Keyboard 8 45 | 0x26 => .Number9, // Keyboard 9 46 | 0x27 => .Number0, // Keyboard 0 47 | 0x28 => .Enter, // Keyboard Return (Enter) 48 | 0x29 => .Escape, // Keyboard Escape 49 | 0x2A => .Backspace, // Keyboard Delete (Backspace) 50 | 0x2B => .Tab, // Keyboard Tab 51 | 0x2C => .Spacebar, // Keyboard Spacebar 52 | 0x2D => .RightOf0, // Keyboard - and (underscore) 53 | 0x2E => .LeftOfBackspace, // Keyboard = and + 54 | 0x2F => .Line1n11, // Keyboard [ and { 55 | 0x30 => .Line1n12, // Keyboard ] and } 56 | 0x31 => .Line1n13, // Keyboard \ and | 57 | 0x32 => .NonUsPound, // Keyboard Non-US # and ~ 58 | 0x33 => .Line2n10, // Keyboard ; and : 59 | 0x34 => .Line2n11, // Keyboard ' and " 60 | 0x35 => .LeftOf1, // Keyboard ` and ~ 61 | 0x36 => .Line3n8, // Keyboard , and < 62 | 0x37 => .Line3n9, // Keyboard . and > 63 | 0x38 => .Line3n10, // Keyboard / and ? 64 | 0x39 => .CapsLock, // Keyboard Caps Lock 65 | 0x3A => .F1, // Keyboard F1 66 | 0x3B => .F2, // Keyboard F2 67 | 0x3C => .F3, // Keyboard F3 68 | 0x3D => .F4, // Keyboard F4 69 | 0x3E => .F5, // Keyboard F5 70 | 0x3F => .F6, // Keyboard F6 71 | 0x40 => .F7, // Keyboard F7 72 | 0x41 => .F8, // Keyboard F8 73 | 0x42 => .F9, // Keyboard F9 74 | 0x43 => .F10, // Keyboard F10 75 | 0x44 => .F11, // Keyboard F11 76 | 0x45 => .F12, // Keyboard F12 77 | 0x46 => .PrintScreen, // Keyboard PrintScreen 78 | 0x47 => .ScrollLock, // Keyboard Scroll Lock 79 | 0x48 => .PauseBreak, // Keyboard Pause 80 | 0x49 => .Insert, // Keyboard Insert 81 | 0x4A => .Home, // Keyboard Home 82 | 0x4B => .PageUp, // Keyboard PageUp 83 | 0x4C => .Delete, // Keyboard Delete Forward 84 | 0x4D => .End, // Keyboard End 85 | 0x4E => .PageDown, // Keyboard PageDown 86 | 0x4F => .ArrowRight, // Keyboard RightArrow 87 | 0x50 => .ArrowLeft, // Keyboard LeftArrow 88 | 0x51 => .ArrowDown, // Keyboard DownArrow 89 | 0x52 => .ArrowUp, // Keyboard UpArrow 90 | 0x53 => .Numlock, // Keyboard Num Lock and Clear 91 | 0x54 => .NumpadDiv, // Keypad / 92 | 0x55 => .NumpadMul, // Keypad * 93 | 0x56 => .NumpadSub, // Keypad - 94 | 0x57 => .NumpadAdd, // Keypad + 95 | 0x58 => .NumpadEnter, // Keypad Enter 96 | 0x59 => .Numpad1, // Keypad 1 and End 97 | 0x5A => .Numpad2, // Keypad 2 and Down Arrow 98 | 0x5B => .Numpad3, // Keypad 3 and PageDn 99 | 0x5C => .Numpad4, // Keypad 4 and Left Arrow 100 | 0x5D => .Numpad5, // Keypad 5 101 | 0x5E => .Numpad6, // Keypad 6 and Right Arrow 102 | 0x5F => .Numpad7, // Keypad 7 and Home 103 | 0x60 => .Numpad8, // Keypad 8 and Up Arrow 104 | 0x61 => .Numpad9, // Keypad 9 and PageUp 105 | 0x62 => .Numpad0, // Keypad 0 and Insert 106 | 0x63 => .NumpadPoint, // Keypad . and Delete 107 | 0x64 => .NonUsBackslash, // Keyboard Non-US \ and | 108 | // 0x65 Keyboard Application 109 | // 0x66 Keyboard Power 110 | // 0x67 Keypad = 111 | 0x68 => .F13, // Keyboard F13 112 | 0x69 => .F14, // Keyboard F14 113 | 0x6A => .F15, // Keyboard F15 114 | 0x6B => .F16, // Keyboard F16 115 | 0x6C => .F17, // Keyboard F17 116 | 0x6D => .F18, // Keyboard F18 117 | 0x6E => .F19, // Keyboard F19 118 | 0x6F => .F20, // Keyboard F20 119 | 0x70 => .F21, // Keyboard F21 120 | 0x71 => .F22, // Keyboard F22 121 | 0x72 => .F23, // Keyboard F23 122 | 0x73 => .F24, // Keyboard F24 123 | // ... 124 | 0xE0 => .LeftCtrl, // Keyboard LeftControl 125 | 0xE1 => .LeftShift, // Keyboard LeftShift 126 | 0xE2 => .LeftAlt, // Keyboard LeftAlt 127 | 0xE3 => .LeftSuper, // Keyboard Left GUI 128 | 0xE4 => .RightCtrl, // Keyboard RightControl 129 | 0xE5 => .RightShift, // Keyboard RightShift 130 | 0xE6 => .RightAlt, // Keyboard RightAlt 131 | 0xE7 => .RightSuper, // Keyboard Right GUI 132 | else => null, 133 | }; 134 | } 135 | 136 | const HidKeyboardDevice = struct { 137 | const KeyState = std.PackedIntArray(bool, 256); 138 | 139 | poll_ep: Endpoint = undefined, 140 | report_buffer: u64 = 0, 141 | key_state: KeyState = blk: { 142 | @setEvalBranchQuota(9999999); 143 | break :blk KeyState.initAllTo(false); 144 | }, 145 | 146 | fn configure(self: *@This(), device: *Device, controller: *xhci.Controller, slot_id: u8) void { 147 | controller.sendControlTransfer(slot_id, 0, 0x21, 0xB, 0, device.interface_id, 0, 0, false); 148 | controller.sendControlTransfer(slot_id, 0, 0x21, 0xA, 0, device.interface_id, 0, 0, false); 149 | controller.enableEndpoint(slot_id, self.poll_ep); 150 | self.report_buffer = phys.allocate(1, .dma).?; 151 | } 152 | 153 | fn onConfigured(self: *@This(), device: *Device, controller: *xhci.Controller, slot_id: u8) void { 154 | _ = device; 155 | controller.sendDataTransfer(slot_id, self.poll_ep.address, 8, self.report_buffer, true); 156 | } 157 | 158 | fn onDataTransferComplete( 159 | self: *@This(), 160 | device: *Device, 161 | controller: *xhci.Controller, 162 | endpoint_addr: u8, 163 | slot_id: u8, 164 | data: []const u8, 165 | ) bool { 166 | _ = device; 167 | if (endpoint_addr == self.poll_ep.address) { 168 | defer controller.sendDataTransfer(slot_id, self.poll_ep.address, 8, self.report_buffer, true); 169 | logger.debug("Keyboard report data: {any}", .{std.fmt.fmtSliceHexUpper(data)}); 170 | 171 | const modifiers = data[0]; 172 | const scancodes = data[2..]; 173 | 174 | if (scancodes[0] == 0x1) { 175 | return true; 176 | } 177 | 178 | for (0..8, 0xE0..) |i, scancode| { 179 | const state = modifiers & (@as(u8, 1) << @intCast(i)) != 0; 180 | const previous_state = self.key_state.get(scancode); 181 | if (state != previous_state) { 182 | self.key_state.set(scancode, state); 183 | _ = input.enqueueKeyboardEvent(.{ 184 | .location = keyboardUsageToKeyLocation(scancode).?, 185 | .pressed = state, 186 | }); 187 | } 188 | } 189 | 190 | for (0x0..0xE0) |scancode| { 191 | if (!self.key_state.get(scancode)) { 192 | continue; 193 | } 194 | 195 | if (std.mem.indexOfScalar(u8, scancodes, @intCast(scancode)) == null) { 196 | self.key_state.set(scancode, false); 197 | _ = input.enqueueKeyboardEvent(.{ 198 | .location = keyboardUsageToKeyLocation(scancode) orelse continue, 199 | .pressed = false, 200 | }); 201 | } 202 | } 203 | 204 | for (scancodes) |scancode| { 205 | if (scancode != 0 and !self.key_state.get(scancode)) { 206 | self.key_state.set(scancode, true); 207 | _ = input.enqueueKeyboardEvent(.{ 208 | .location = keyboardUsageToKeyLocation(scancode) orelse { 209 | logger.warn("Unhandled scancode: {X}", .{scancode}); 210 | continue; 211 | }, 212 | .pressed = true, 213 | }); 214 | } 215 | } 216 | 217 | return true; 218 | } 219 | 220 | return false; 221 | } 222 | }; 223 | 224 | const HidMouseDevice = struct { 225 | poll_ep: Endpoint = undefined, 226 | report_buffer: u64 = 0, 227 | 228 | fn configure(self: *@This(), device: *Device, controller: *xhci.Controller, slot_id: u8) void { 229 | controller.sendControlTransfer(slot_id, 0, 0x21, 0xB, 0, device.interface_id, 0, 0, false); 230 | controller.sendControlTransfer(slot_id, 0, 0x21, 0xA, 0, device.interface_id, 0, 0, false); 231 | controller.enableEndpoint(slot_id, self.poll_ep); 232 | self.report_buffer = phys.allocate(1, .dma).?; 233 | } 234 | 235 | fn onConfigured(self: *@This(), device: *Device, controller: *xhci.Controller, slot_id: u8) void { 236 | _ = device; 237 | controller.sendDataTransfer(slot_id, self.poll_ep.address, 32, self.report_buffer, true); 238 | } 239 | 240 | fn onDataTransferComplete( 241 | self: *@This(), 242 | device: *Device, 243 | controller: *xhci.Controller, 244 | endpoint_addr: u8, 245 | slot_id: u8, 246 | data: []const u8, 247 | ) bool { 248 | _ = device; 249 | if (endpoint_addr == self.poll_ep.address) { 250 | defer controller.sendDataTransfer(slot_id, self.poll_ep.address, 8, self.report_buffer, true); 251 | logger.debug("Mouse report data: {any}", .{std.fmt.fmtSliceHexUpper(data)}); 252 | return true; 253 | } 254 | 255 | return false; 256 | } 257 | }; 258 | 259 | pub const Endpoint = extern struct { 260 | length: u8, 261 | descriptor_type: u8, 262 | address: u8, 263 | attributes: u8, 264 | packet_size: u16 align(1), 265 | interval: u8, 266 | }; 267 | 268 | pub const Device = struct { 269 | interface_id: u8 = undefined, 270 | impl: union(enum) { 271 | hid_keyboard: HidKeyboardDevice, 272 | hid_mouse: HidMouseDevice, 273 | }, 274 | 275 | pub fn configure(self: *@This(), controller: *xhci.Controller, slot_id: u8) void { 276 | switch (self.impl) { 277 | .hid_keyboard => |*hid| hid.configure(self, controller, slot_id), 278 | .hid_mouse => |*hid| hid.configure(self, controller, slot_id), 279 | } 280 | } 281 | 282 | pub fn onConfigured(self: *@This(), controller: *xhci.Controller, slot_id: u8) void { 283 | switch (self.impl) { 284 | .hid_keyboard => |*hid| hid.onConfigured(self, controller, slot_id), 285 | .hid_mouse => |*hid| hid.onConfigured(self, controller, slot_id), 286 | } 287 | } 288 | 289 | pub fn onDataTransferComplete( 290 | self: *@This(), 291 | controller: *xhci.Controller, 292 | endpoint_addr: u8, 293 | slot_id: u8, 294 | data: []const u8, 295 | ) bool { 296 | switch (self.impl) { 297 | .hid_keyboard => |*hid| return hid.onDataTransferComplete(self, controller, endpoint_addr, slot_id, data), 298 | .hid_mouse => |*hid| return hid.onDataTransferComplete(self, controller, endpoint_addr, slot_id, data), 299 | } 300 | 301 | return false; 302 | } 303 | 304 | fn handleDescriptor(self: *@This(), descriptor: []const u8) void { 305 | const descriptor_type = descriptor[1]; 306 | switch (descriptor_type) { 307 | 5 => { 308 | const endpoint = std.mem.bytesToValue(Endpoint, descriptor[0..7]); 309 | switch (self.impl) { 310 | .hid_keyboard => |*hid| { 311 | if ((endpoint.attributes & 0b11) == 0b11 and (endpoint.address & 0x80) != 0) { 312 | logger.debug("Found HID keyboard polling endpoint {any}", .{endpoint}); 313 | hid.poll_ep = endpoint; 314 | } 315 | }, 316 | .hid_mouse => |*hid| { 317 | if ((endpoint.attributes & 0b11) == 0b11 and (endpoint.address & 0x80) != 0) { 318 | logger.debug("Found HID mouse polling endpoint {any}", .{endpoint}); 319 | hid.poll_ep = endpoint; 320 | } 321 | }, 322 | } 323 | }, 324 | else => logger.warn( 325 | "Unhandled USB descriptor type {d}: {any}", 326 | .{ descriptor_type, std.fmt.fmtSliceHexUpper(descriptor) }, 327 | ), 328 | } 329 | } 330 | }; 331 | 332 | pub fn identifyDevice(config_descriptor: []const u8) !std.BoundedArray(Device, 8) { 333 | var devices = std.BoundedArray(Device, 8){}; 334 | var offset = @as(usize, config_descriptor[0]); // bLength 335 | var current_device: ?*Device = null; 336 | while (offset + 2 <= config_descriptor.len) { 337 | const length = config_descriptor[offset]; 338 | const descriptor_type = config_descriptor[offset + 1]; 339 | switch (descriptor_type) { 340 | 4 => { 341 | const interface_class = config_descriptor[offset + 5]; 342 | const interface_subclass = config_descriptor[offset + 6]; 343 | const interface_protocol = config_descriptor[offset + 7]; 344 | 345 | if (interface_class == 0x3) { 346 | switch (interface_subclass) { 347 | 0x1 => switch (interface_protocol) { 348 | 0x1 => { 349 | current_device = try devices.addOne(); 350 | current_device.?.* = .{ .impl = .{ .hid_keyboard = .{} } }; 351 | }, 352 | 0x2 => { 353 | current_device = try devices.addOne(); 354 | current_device.?.* = .{ .impl = .{ .hid_mouse = .{} } }; 355 | }, 356 | else => logger.warn("Unhandled HID boot protocol {d}", .{interface_protocol}), 357 | }, 358 | else => logger.warn("Unhandled HID subclass {d}", .{interface_subclass}), 359 | } 360 | } else { 361 | logger.warn( 362 | "Unhandled interface class {d} (subclass {d}, protocol {d})", 363 | .{ interface_class, interface_subclass, interface_protocol }, 364 | ); 365 | } 366 | 367 | if (current_device) |device| { 368 | device.interface_id = config_descriptor[offset + 2]; 369 | } 370 | }, 371 | else => if (current_device) |device| { 372 | device.handleDescriptor(config_descriptor[offset..][0..length]); 373 | }, 374 | } 375 | offset += length; 376 | } 377 | return devices; 378 | } 379 | -------------------------------------------------------------------------------- /kernel/src/hpet.zig: -------------------------------------------------------------------------------- 1 | const logger = std.log.scoped(.hpet); 2 | 3 | const std = @import("std"); 4 | 5 | const acpi = @import("./acpi.zig"); 6 | const scheduler = @import("./scheduler.zig"); 7 | const uacpi = @import("./uacpi.zig"); 8 | const virt = @import("./virt.zig"); 9 | 10 | var hpet_address: ?*anyopaque = null; 11 | var counter_period: u64 = 0; 12 | 13 | const Register = enum(usize) { 14 | capabilities = 0x0, 15 | configuration = 0x10, 16 | main_counter = 0xF0, 17 | }; 18 | 19 | fn getRegister(reg: Register) *volatile u64 { 20 | return @ptrFromInt(@intFromPtr(hpet_address.?) + @intFromEnum(reg)); 21 | } 22 | 23 | fn ensureIsSane() bool { 24 | if (getRegister(.capabilities).* & (1 << 13) == 0) { 25 | logger.err("HPET main counter is not 64-bits wide", .{}); 26 | return false; 27 | } 28 | 29 | return true; 30 | } 31 | 32 | fn tryInit() bool { 33 | const hpet_table = acpi.findTable(uacpi.ACPI_HPET_SIGNATURE) orelse { 34 | logger.err("HPET table not found", .{}); 35 | return false; 36 | }; 37 | 38 | const hpet: *uacpi.acpi_hpet = @ptrCast(hpet_table.uacpi_table.unnamed_0.hdr); 39 | if (hpet.address.address_space_id != uacpi.UACPI_ADDRESS_SPACE_SYSTEM_MEMORY) { 40 | logger.err("HPET address space is not system memory", .{}); 41 | return false; 42 | } 43 | 44 | hpet_address = virt.asHigherHalfUncached(*anyopaque, hpet.address.address); 45 | logger.debug("HPET base address is 0x{X}", .{hpet.address.address}); 46 | 47 | if (!ensureIsSane()) { 48 | hpet_address = null; 49 | return false; 50 | } 51 | 52 | return true; 53 | } 54 | 55 | fn finishInit() void { 56 | counter_period = getRegister(.capabilities).* >> 32; 57 | logger.info("HPET counter period is {d}fs", .{counter_period}); 58 | 59 | getRegister(.configuration).* &= ~@as(u64, 1 << 0); 60 | getRegister(.main_counter).* = 0; 61 | getRegister(.configuration).* |= (1 << 0); 62 | } 63 | 64 | pub fn init(force_init: bool) !void { 65 | defer { 66 | if (hpet_address != null) { 67 | finishInit(); 68 | } 69 | } 70 | 71 | if (tryInit()) { 72 | return; 73 | } 74 | 75 | if (force_init) { 76 | logger.warn("Could not find an HPET table, assuming the memory location", .{}); 77 | hpet_address = virt.asHigherHalfUncached(*anyopaque, 0xFED00000); 78 | 79 | if (ensureIsSane()) { 80 | return; 81 | } 82 | } 83 | 84 | hpet_address = null; 85 | return error.Unsupported; 86 | } 87 | 88 | pub fn sample() u64 { 89 | return getRegister(.main_counter).*; 90 | } 91 | 92 | pub fn sleep(ns: u64, preempt: bool) void { 93 | const start = sample(); 94 | const end = start + (ns * 1000000) / counter_period; 95 | 96 | if (preempt) { 97 | while (sample() < end) { 98 | scheduler.yield(); 99 | } 100 | } else { 101 | while (sample() < end) { 102 | std.atomic.spinLoopHint(); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /kernel/src/interrupts.zig: -------------------------------------------------------------------------------- 1 | const logger = std.log.scoped(.interrupts); 2 | 3 | const std = @import("std"); 4 | 5 | const arch = @import("arch.zig"); 6 | const debug = @import("debug.zig"); 7 | const scheduler = @import("scheduler.zig"); 8 | const per_cpu = @import("per_cpu.zig"); 9 | const virt = @import("virt.zig"); 10 | 11 | var next_vector: usize = 32; 12 | var handlers = [1]InterruptHandler{exceptionHandler} ** 32 ++ [1]InterruptHandler{unhandledInterruptHandler} ** 224; 13 | var handler_contexts: [256]InterruptContext = undefined; 14 | 15 | pub const syscall_vector: u8 = 0xFD; 16 | pub const sched_call_vector: u8 = 0xFE; 17 | pub const spurious_vector: u8 = 0xFF; 18 | 19 | pub const InterruptContext = ?*anyopaque; 20 | 21 | const InterruptStub = *const fn () callconv(.Naked) void; 22 | const InterruptHandler = *const fn (*InterruptFrame) void; 23 | const InterruptHandlerWithContext = *const fn (InterruptContext) void; 24 | 25 | const IretFrame = extern struct { 26 | err: u64, 27 | rip: u64, 28 | cs: u64, 29 | rflags: u64, 30 | rsp: u64, 31 | ss: u64, 32 | }; 33 | 34 | pub const InterruptFrame = extern struct { 35 | es: u64, 36 | ds: u64, 37 | r15: u64, 38 | r14: u64, 39 | r13: u64, 40 | r12: u64, 41 | r11: u64, 42 | r10: u64, 43 | r9: u64, 44 | r8: u64, 45 | rsi: u64, 46 | rdi: u64, 47 | rbp: u64, 48 | rdx: u64, 49 | rcx: u64, 50 | rbx: u64, 51 | rax: u64, 52 | vector: u64, 53 | iret: IretFrame, 54 | }; 55 | 56 | const interrupt_stubs = blk: { 57 | var result: [256]InterruptStub = undefined; 58 | for (&result, 0..) |*it, i| { 59 | it.* = makeHandler(i); 60 | } 61 | break :blk result; 62 | }; 63 | 64 | pub fn getInterruptHandlers() [256]InterruptStub { 65 | return interrupt_stubs; 66 | } 67 | 68 | pub fn allocateVector() u8 { 69 | const result = @atomicRmw(usize, &next_vector, .Add, 1, .acq_rel); 70 | if (result >= 256 - 16) { 71 | @panic("No more interrupt vectors left to allocate"); 72 | } 73 | return @intCast(result); 74 | } 75 | 76 | pub fn registerHandler(vector: u8, handler: InterruptHandler) void { 77 | handlers[vector] = handler; 78 | } 79 | 80 | pub fn registerHandlerWithContext( 81 | vector: u8, 82 | comptime handler_: InterruptHandlerWithContext, 83 | context: InterruptContext, 84 | ) void { 85 | handler_contexts[vector] = context; 86 | handlers[vector] = struct { 87 | fn handler(frame: *InterruptFrame) void { 88 | const handler_context = handler_contexts[frame.vector & 0xFF]; 89 | @call(.always_inline, handler_, .{handler_context}); 90 | } 91 | }.handler; 92 | } 93 | 94 | fn printRegisters(frame: *InterruptFrame) void { 95 | const cr2 = asm volatile ("mov %%cr2, %[result]" 96 | : [result] "=r" (-> u64), 97 | ); 98 | 99 | const cr3 = asm volatile ("mov %%cr3, %[result]" 100 | : [result] "=r" (-> u64), 101 | ); 102 | 103 | if (per_cpu.get().thread) |thread| { 104 | logger.err("Faulting process: {}:{}", .{ thread.parent.pid, thread.tid }); 105 | } 106 | 107 | logger.err("Registers:", .{}); 108 | logger.err(" rax={X:0>16} rbx={X:0>16} rcx={X:0>16} rdx={X:0>16}", .{ frame.rax, frame.rbx, frame.rcx, frame.rdx }); 109 | logger.err(" rsi={X:0>16} rdi={X:0>16} rbp={X:0>16} rsp={X:0>16}", .{ frame.rsi, frame.rdi, frame.rbp, frame.iret.rsp }); 110 | logger.err(" r8={X:0>16} r9={X:0>16} r10={X:0>16} r11={X:0>16}", .{ frame.r8, frame.r9, frame.r10, frame.r11 }); 111 | logger.err(" r12={X:0>16} r13={X:0>16} r14={X:0>16} r15={X:0>16}", .{ frame.r12, frame.r13, frame.r14, frame.r15 }); 112 | logger.err(" rip={X:0>16} cr2={X:0>16} cr3={X:0>16} err={X:0>16}", .{ frame.iret.rip, cr2, cr3, frame.iret.err }); 113 | } 114 | 115 | fn exceptionHandler(frame: *InterruptFrame) void { 116 | const cpu_info = per_cpu.get(); 117 | 118 | if (frame.vector == 0x6) { 119 | const code = @as([*]const u8, @ptrFromInt(frame.iret.rip)); 120 | 121 | if (std.mem.eql(u8, code[0..2], &.{ 0x0F, 0x05 })) { 122 | frame.iret.rip += 2; 123 | return @call(.always_tail, handlers[syscall_vector], .{frame}); 124 | } 125 | } else if (frame.vector == 0xE) blk: { 126 | const cr2 = asm volatile ("mov %%cr2, %[result]" 127 | : [result] "=r" (-> u64), 128 | ); 129 | 130 | // If the access violation comes from kernel mode (CPL=0) and the 131 | // first bit of error code (present bit) is not set, then we can 132 | // make sure that we did in fact hit a guard page of a kernel stack 133 | if ((frame.iret.cs & 0x3) == 0 and (frame.iret.err & (1 << 0)) == 0) { 134 | if (virt.kernel_address_space.page_table.getPTE(cr2, false)) |pte| { 135 | if ((pte.getFlags() & virt.PTEFlags.guard_page) != 0) { 136 | @panic("A kernel stack guard page was hit!!! O_O"); 137 | } 138 | } 139 | } 140 | 141 | if (virt.handlePageFault(cr2, frame.iret.err) catch |err| { 142 | logger.err("Failed to handle the page fault: {any}", .{err}); 143 | break :blk; 144 | }) { 145 | return; 146 | } 147 | } 148 | 149 | logger.err("An exception #{} occurred", .{frame.vector}); 150 | debug.printStackIterator(std.debug.StackIterator.init(@returnAddress(), @frameAddress())); 151 | printRegisters(frame); 152 | 153 | if ((frame.iret.cs & 0x3) != 0 and cpu_info.currentProcess() != null) { 154 | // TODO: Implement signals and stuff like that so we can properly 155 | // terminate processes that violate memory protections 156 | const process = cpu_info.currentProcess().?; 157 | logger.info("Killed {}:{} because of a protection violation", .{ process.pid, cpu_info.thread.?.tid }); 158 | scheduler.exitProcess(process, 0xff); 159 | cpu_info.thread = null; 160 | } else { 161 | while (true) { 162 | arch.hlt(); 163 | } 164 | } 165 | } 166 | 167 | fn unhandledInterruptHandler(frame: *InterruptFrame) void { 168 | logger.err("An unhandled interrupt #{} occurred", .{frame.vector}); 169 | debug.printStackIterator(std.debug.StackIterator.init(@returnAddress(), @frameAddress())); 170 | printRegisters(frame); 171 | while (true) { 172 | arch.hlt(); 173 | } 174 | } 175 | 176 | const swapgs_if_needed = std.fmt.comptimePrint( 177 | \\ 178 | \\testb $0x3, 0x{X}(%%rsp) 179 | \\je 1f 180 | \\swapgs 181 | \\1: 182 | \\ 183 | , .{@offsetOf(IretFrame, "cs")}); 184 | 185 | fn makeHandler(comptime vector: usize) InterruptStub { 186 | // https://wiki.osdev.org/Exceptions 187 | const has_error_code = switch (vector) { 188 | 0x8 => true, 189 | 0xA...0xE => true, 190 | 0x11 => true, 191 | 0x15 => true, 192 | 0x1D...0x1E => true, 193 | else => false, 194 | }; 195 | 196 | return struct { 197 | fn handler() callconv(.Naked) void { 198 | if (has_error_code) { 199 | asm volatile (swapgs_if_needed ++ 200 | \\pushq %[vector] 201 | \\jmp interruptCommonHandler 202 | : 203 | : [vector] "i" (vector), 204 | [_] "i" (interruptCommonHandler), 205 | ); 206 | } else { 207 | asm volatile ( 208 | \\pushq $0 209 | ++ swapgs_if_needed ++ 210 | \\pushq %[vector] 211 | \\jmp interruptCommonHandler 212 | : 213 | : [vector] "i" (vector), 214 | [_] "i" (interruptCommonHandler), 215 | ); 216 | } 217 | } 218 | }.handler; 219 | } 220 | 221 | export fn interruptHandler(frame: *InterruptFrame) callconv(.C) void { 222 | const handler = handlers[frame.vector & 0xFF]; 223 | handler(frame); 224 | } 225 | 226 | export fn interruptCommonHandler() callconv(.Naked) void { 227 | asm volatile ( 228 | \\push %%rax 229 | \\push %%rbx 230 | \\push %%rcx 231 | \\push %%rdx 232 | \\push %%rbp 233 | \\push %%rdi 234 | \\push %%rsi 235 | \\push %%r8 236 | \\push %%r9 237 | \\push %%r10 238 | \\push %%r11 239 | \\push %%r12 240 | \\push %%r13 241 | \\push %%r14 242 | \\push %%r15 243 | \\xor %%rax, %%rax 244 | \\mov %%ds, %%ax 245 | \\push %%rax 246 | \\mov %%es, %%ax 247 | \\push %%rax 248 | \\mov %%rsp, %%rdi 249 | \\call interruptHandler 250 | \\pop %%rax 251 | \\mov %%ax, %%es 252 | \\pop %%rax 253 | \\mov %%ax, %%ds 254 | \\pop %%r15 255 | \\pop %%r14 256 | \\pop %%r13 257 | \\pop %%r12 258 | \\pop %%r11 259 | \\pop %%r10 260 | \\pop %%r9 261 | \\pop %%r8 262 | \\pop %%rsi 263 | \\pop %%rdi 264 | \\pop %%rbp 265 | \\pop %%rdx 266 | \\pop %%rcx 267 | \\pop %%rbx 268 | \\pop %%rax 269 | \\add $8, %%rsp 270 | ++ swapgs_if_needed ++ 271 | \\add $8, %%rsp 272 | \\iretq 273 | : 274 | : [_] "i" (interruptHandler), 275 | ); 276 | } 277 | -------------------------------------------------------------------------------- /kernel/src/lock.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | const arch = @import("arch.zig"); 4 | const debug = @import("debug.zig"); 5 | 6 | pub const CriticalSection = struct { 7 | previous_rflags: u64, 8 | 9 | pub fn enter() @This() { 10 | const rflags = arch.readEFlags(); 11 | asm volatile ("cli"); 12 | return .{ .previous_rflags = rflags }; 13 | } 14 | 15 | pub fn leave(self: @This()) void { 16 | if ((self.previous_rflags & (1 << 9)) != 0) { 17 | asm volatile ("sti"); 18 | } 19 | } 20 | }; 21 | 22 | pub const Spinlock = struct { 23 | value: std.atomic.Value(bool) = .{ .raw = false }, 24 | 25 | pub fn tryLock(self: *@This()) bool { 26 | return self.value.cmpxchgStrong(false, true, .acquire, .monotonic) == null; 27 | } 28 | 29 | pub fn lock(self: *@This()) void { 30 | while (!self.tryLock()) { 31 | std.atomic.spinLoopHint(); 32 | } 33 | } 34 | 35 | pub fn unlock(self: *@This()) void { 36 | self.value.store(false, .release); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /kernel/src/main.zig: -------------------------------------------------------------------------------- 1 | const logger = std.log.scoped(.main); 2 | 3 | const C = @cImport({ 4 | @cInclude("flanterm.h"); 5 | @cInclude("backends/fb.h"); 6 | }); 7 | 8 | const std = @import("std"); 9 | const limine = @import("limine"); 10 | 11 | const acpi = @import("./acpi.zig"); 12 | const apic = @import("./apic.zig"); 13 | const arch = @import("./arch.zig"); 14 | const debug = @import("./debug.zig"); 15 | const hpet = @import("./hpet.zig"); 16 | const interrupts = @import("./interrupts.zig"); 17 | const lock = @import("./lock.zig"); 18 | const per_cpu = @import("./per_cpu.zig"); 19 | const pci = @import("./pci.zig"); 20 | const phys = @import("./phys.zig"); 21 | const scheduler = @import("./scheduler.zig"); 22 | const time = @import("./time.zig"); 23 | const utils = @import("./utils.zig"); 24 | const vfs = @import("./vfs.zig"); 25 | const virt = @import("./virt.zig"); 26 | 27 | const PageAllocator = struct { 28 | pub fn allocate(_: *@This(), pages: usize) ?u64 { 29 | return kernel_paged_arena.allocate(pages * std.mem.page_size); 30 | } 31 | 32 | fn alloc(ctx: *anyopaque, len: usize, _: u8, _: usize) ?[*]u8 { 33 | const self = @as(*PageAllocator, @ptrCast(@alignCast(ctx))); 34 | const pages = std.math.divCeil(usize, len, std.mem.page_size) catch unreachable; 35 | return @ptrFromInt(self.allocate(pages) orelse return null); 36 | } 37 | 38 | fn resize(_: *anyopaque, _: []u8, _: u8, _: usize, _: usize) bool { 39 | return false; 40 | } 41 | 42 | fn free(_: *anyopaque, _: []u8, _: u8, _: usize) void {} 43 | }; 44 | 45 | var flanterm_ctx: ?*C.flanterm_context = null; 46 | var print_lock: lock.Spinlock = .{}; 47 | 48 | pub const log_level = std.log.Level.debug; 49 | 50 | pub const os = struct { 51 | pub const system = struct {}; 52 | 53 | pub const heap = struct { 54 | pub const page_allocator = std.mem.Allocator{ 55 | .ptr = &page_heap_allocator, 56 | .vtable = &std.mem.Allocator.VTable{ 57 | .alloc = PageAllocator.alloc, 58 | .resize = PageAllocator.resize, 59 | .free = PageAllocator.free, 60 | }, 61 | }; 62 | }; 63 | }; 64 | 65 | pub const std_options: std.Options = .{ .logFn = log }; 66 | 67 | pub var kernel_va_arena: virt.Arena = .{}; 68 | pub var kernel_paged_arena: virt.Arena = .{}; 69 | 70 | pub var page_heap_allocator: PageAllocator = .{}; 71 | pub var gp_allocator = std.heap.GeneralPurposeAllocator(.{ .thread_safe = true, .MutexType = lock.Spinlock }){}; 72 | pub var allocator = gp_allocator.allocator(); 73 | 74 | pub export var boot_info_req: limine.BootloaderInfoRequest = .{}; 75 | pub export var hhdm_req: limine.HhdmRequest = .{}; 76 | pub export var memory_map_req: limine.MemoryMapRequest = .{}; 77 | pub export var modules_req: limine.ModuleRequest = .{}; 78 | pub export var kernel_file_req: limine.KernelFileRequest = .{}; 79 | pub export var rsdp_req: limine.RsdpRequest = .{}; 80 | pub export var kernel_addr_req: limine.KernelAddressRequest = .{}; 81 | pub export var framebuffer_req: limine.FramebufferRequest = .{}; 82 | pub export var boot_time_req: limine.BootTimeRequest = .{}; 83 | 84 | export fn _start() callconv(.C) noreturn { 85 | main() catch |err| { 86 | logger.err("Failed to initialize: {any}", .{err}); 87 | 88 | if (@errorReturnTrace()) |stack_trace| { 89 | debug.printStackTrace(stack_trace); 90 | } 91 | }; 92 | 93 | while (true) { 94 | arch.hlt(); 95 | } 96 | } 97 | 98 | fn mainThread(_: u8) !void { 99 | try pci.init(); 100 | try acpi.enumerateDevices(); 101 | 102 | // const process = try scheduler.spawnProcess(null); 103 | // const thread = try scheduler.spawnThread(process); 104 | // const init = try vfs.resolve(null, "/usr/bin/init", 0); 105 | 106 | // try thread.exec(init, &.{"/usr/bin/init"}, &.{}); 107 | 108 | // scheduler.enqueue(thread); 109 | 110 | while (true) { 111 | scheduler.yield(); 112 | } 113 | } 114 | 115 | fn pagedAlloc(source: *virt.Arena, size: usize) ?u64 { 116 | const page_table = virt.kernel_address_space.page_table; 117 | const pages = @divExact(size, std.mem.page_size); 118 | const address = source.allocate(pages * std.mem.page_size) orelse return null; 119 | for (0..pages) |i| { 120 | const page = phys.allocate(1, .conventional) orelse return null; 121 | page_table.mapPage( 122 | address + i * std.mem.page_size, 123 | page, 124 | virt.PTEFlags.present | virt.PTEFlags.writable, 125 | ) catch return null; 126 | } 127 | return address; 128 | } 129 | 130 | fn pagedFree(source: *virt.Arena, address: u64, size: usize) void { 131 | const page_table = virt.kernel_address_space.page_table; 132 | const pages = std.math.divCeil(usize, size, std.mem.page_size) catch unreachable; 133 | for (0..pages) |i| { 134 | const addr = address + i * std.mem.page_size; 135 | const phys_addr = page_table.translate(addr) orelse unreachable; 136 | page_table.unmapPage(addr) catch unreachable; 137 | phys.free(phys_addr, 1); 138 | } 139 | source.free(address, size); 140 | } 141 | 142 | fn main() !void { 143 | asm volatile ("cli"); 144 | defer asm volatile ("sti"); 145 | 146 | per_cpu.initBsp(); 147 | per_cpu.initFeatures(); 148 | 149 | if (framebuffer_req.response) |fb_res| { 150 | const framebuffer = fb_res.framebuffers()[0]; 151 | flanterm_ctx = C.flanterm_fb_init(null, null, @ptrCast(@alignCast(framebuffer.address)), // 152 | framebuffer.width, framebuffer.height, framebuffer.pitch, framebuffer.red_mask_size, framebuffer.red_mask_shift, // 153 | framebuffer.green_mask_size, framebuffer.green_mask_shift, framebuffer.blue_mask_size, framebuffer.blue_mask_shift, // 154 | null, null, null, null, null, null, null, null, 0, 0, 1, 0, 0, 0); 155 | } 156 | 157 | virt.bootstrapArena(); 158 | 159 | kernel_va_arena = virt.Arena.init("kernel-va", 0xFFFF_A000_0000_0000, utils.tib(16)); 160 | kernel_paged_arena = virt.Arena.initWithSource("kernel-paged", &kernel_va_arena, pagedAlloc, pagedFree); 161 | 162 | const boot_info_res = boot_info_req.response.?; 163 | const hhdm_res = hhdm_req.response.?; 164 | const memory_map_res = memory_map_req.response.?; 165 | const kernel_addr_res = kernel_addr_req.response.?; 166 | const modules_res = modules_req.response.?; 167 | const rsdp_res = rsdp_req.response.?; 168 | 169 | if (kernel_file_req.response) |res| { 170 | debug.init(res) catch |err| 171 | logger.warn("Failed to parsee debug information: {any}", .{err}); 172 | } 173 | 174 | if (boot_time_req.response) |res| { 175 | time.init(res); 176 | } 177 | 178 | std.debug.assert(hhdm_res.offset == virt.asHigherHalf(u64, 0)); 179 | logger.info("Booted using {s} {s}", .{ boot_info_res.name, boot_info_res.version }); 180 | 181 | try phys.init(memory_map_res); 182 | try virt.init(memory_map_res, kernel_addr_res); 183 | 184 | try per_cpu.init(); 185 | try acpi.init(rsdp_res); 186 | try hpet.init(false); 187 | apic.init(); 188 | 189 | try vfs.init(modules_res); 190 | try scheduler.init(); 191 | _ = try scheduler.startKernelThread(mainThread, 0); 192 | } 193 | 194 | pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, ret_addr: ?usize) noreturn { 195 | asm volatile ("cli"); 196 | _ = error_return_trace; 197 | _ = ret_addr; 198 | logger.err("Kernel panic: {s}", .{msg}); 199 | debug.printStackIterator(std.debug.StackIterator.init(@returnAddress(), @frameAddress())); 200 | while (true) { 201 | arch.hlt(); 202 | } 203 | } 204 | 205 | const LogWriter = struct { 206 | pub const Error = error{}; 207 | 208 | pub fn write(_: @This(), bytes: []const u8) Error!usize { 209 | debug.debugPrint(bytes); 210 | if (flanterm_ctx) |ctx| { 211 | C.flanterm_write(ctx, bytes.ptr, bytes.len); 212 | } 213 | return bytes.len; 214 | } 215 | 216 | pub fn writeByte(self: @This(), byte: u8) Error!void { 217 | _ = try self.write(&.{byte}); 218 | } 219 | 220 | pub fn writeBytesNTimes(self: @This(), bytes: []const u8, n: usize) Error!void { 221 | for (0..n) |_| { 222 | _ = try self.write(bytes); 223 | } 224 | } 225 | 226 | pub fn writeAll(self: @This(), bytes: []const u8) Error!void { 227 | _ = try self.write(bytes); 228 | } 229 | }; 230 | 231 | pub fn log( 232 | comptime level: std.log.Level, 233 | comptime scope: anytype, 234 | comptime fmt: []const u8, 235 | args: anytype, 236 | ) void { 237 | print_lock.lock(); 238 | defer print_lock.unlock(); 239 | const current_time = time.getClock(.monotonic); 240 | std.fmt.format( 241 | @as(LogWriter, undefined), 242 | "[{d:>5}.{d:0>6}] [CPU{d}] {s}({s}): " ++ fmt ++ "\n", 243 | .{ 244 | @as(u64, @intCast(current_time.seconds)), 245 | @as(u64, @intCast(current_time.nanoseconds)) / std.time.ns_per_us, 246 | per_cpu.get().lapic_id, 247 | @tagName(level), 248 | @tagName(scope), 249 | } ++ args, 250 | ) catch unreachable; 251 | } 252 | -------------------------------------------------------------------------------- /kernel/src/pci.zig: -------------------------------------------------------------------------------- 1 | const logger = std.log.scoped(.pci); 2 | 3 | const root = @import("root"); 4 | const std = @import("std"); 5 | 6 | const acpi = @import("./acpi.zig"); 7 | const arch = @import("./arch.zig"); 8 | const drivers = @import("./drivers.zig"); 9 | const uacpi = @import("./uacpi.zig"); 10 | const virt = @import("./virt.zig"); 11 | 12 | pub const Address = packed struct(u32) { 13 | segment: u16, 14 | bus: u8, 15 | device: u5, 16 | function: u3, 17 | 18 | pub fn format( 19 | self: @This(), 20 | comptime _: []const u8, 21 | _: std.fmt.FormatOptions, 22 | writer: anytype, 23 | ) !void { 24 | try std.fmt.format( 25 | writer, 26 | "[{X:0>4}:{X:0>2}:{X:0>2}.{X:0>1}]", 27 | .{ self.segment, self.bus, self.device, self.function }, 28 | ); 29 | } 30 | }; 31 | 32 | const Segment = struct { 33 | segment: u16, 34 | base_address: u64, 35 | start_bus: u8, 36 | end_bus: u8, 37 | }; 38 | 39 | const ConfigSpaceIO = struct { 40 | fn selectField(address: Address, offset: u32) void { 41 | std.debug.assert((offset & 0x3) == 0); 42 | std.debug.assert(address.segment == 0); 43 | arch.out(u32, 0xCF8, (1 << 31) | 44 | @as(u32, address.bus) << 16 | 45 | @as(u32, address.device) << 11 | 46 | @as(u32, address.function) << 8 | 47 | offset); 48 | } 49 | 50 | fn read(address: Address, offset: u32) u32 { 51 | selectField(address, offset); 52 | return arch.in(u32, 0xCFC); 53 | } 54 | 55 | fn write(address: Address, offset: u32, value: u32) void { 56 | selectField(address, offset); 57 | arch.out(u32, 0xCFC, value); 58 | } 59 | }; 60 | 61 | const ConfigSpaceECAM = struct { 62 | fn getMMIOConfigSpace(address: Address) [*]volatile u32 { 63 | const segment = pci_segments[address.segment].?; 64 | std.debug.assert(address.bus >= segment.start_bus and address.bus <= segment.end_bus); 65 | return virt.asHigherHalfUncached( 66 | [*]volatile u32, 67 | segment.base_address | @as(u64, address.bus) << 20 | 68 | @as(u64, address.device) << 15 | @as(u64, address.function) << 12, 69 | ); 70 | } 71 | 72 | fn read(address: Address, offset: u32) u32 { 73 | std.debug.assert(offset & 0x3 == 0); 74 | return getMMIOConfigSpace(address)[offset / 4]; 75 | } 76 | 77 | fn write(address: Address, offset: u32, value: u32) void { 78 | std.debug.assert(offset & 0x3 == 0); 79 | getMMIOConfigSpace(address)[offset / 4] = value; 80 | } 81 | }; 82 | 83 | var pci_segments: [16]?Segment = [1]?Segment{null} ** 16; 84 | var read_config_space_fn: *const fn (Address, u32) u32 = ConfigSpaceIO.read; 85 | var write_config_space_fn: *const fn (Address, u32, u32) void = ConfigSpaceIO.write; 86 | 87 | pub fn readConfigSpace(comptime T: type, address: Address, offset: u32) T { 88 | const misalignment = offset & 0x3; 89 | const value = read_config_space_fn(address, offset & ~@as(u32, 0x3)); 90 | std.debug.assert(misalignment + @sizeOf(T) <= 4); 91 | return @intCast((value >> @intCast(misalignment * 8)) & std.math.maxInt(T)); 92 | } 93 | 94 | pub fn writeConfigSpace(comptime T: type, address: Address, offset: u32, value: T) void { 95 | const misalignment = offset & 0x3; 96 | std.debug.assert(misalignment + @sizeOf(T) <= 4); 97 | if (T == u32) { 98 | write_config_space_fn(address, offset, @as(u32, value)); 99 | return; 100 | } 101 | const aligned_offset = offset & ~@as(u32, 0x3); 102 | const current = read_config_space_fn(address, aligned_offset); 103 | const new_value = current & (@as(u32, std.math.maxInt(T)) << @intCast(misalignment * 8)) | 104 | (@as(u32, value) << @intCast(misalignment * 8)); 105 | write_config_space_fn(address, aligned_offset, new_value); 106 | } 107 | 108 | const DeviceList = std.TailQueue(void); 109 | 110 | pub const BarInfo = struct { 111 | base_address: u64, 112 | length: usize, 113 | is_mmio: bool, 114 | is_64bit: bool, 115 | is_prefetchable: bool, 116 | }; 117 | 118 | pub const Capability = struct { 119 | device: *const Device, 120 | offset: u32, 121 | id: u8, 122 | 123 | pub fn read(self: @This(), comptime T: type, offset: u32) T { 124 | return self.device.read(T, self.offset + offset); 125 | } 126 | 127 | pub fn write(self: @This(), comptime T: type, offset: u32, value: T) void { 128 | self.device.write(T, self.offset + offset, value); 129 | } 130 | }; 131 | 132 | pub const MSI = struct { 133 | capability: Capability, 134 | is_msix: bool, 135 | 136 | pub fn enable(self: @This(), lapic_id: u32, vector: u8) void { 137 | const msi_addr: u64 = 0xFEE00000 | (lapic_id << 12); 138 | const msi_data = @as(u16, vector); // | (1 << 15); // Level triggered 139 | if (self.is_msix) { 140 | logger.info("Enabling MSI-X for device, lapic_id: {}, vector: {}", .{ lapic_id, vector }); 141 | const msi_table_info = self.capability.read(u32, 0x4); 142 | const msi_table_bar = self.capability.device.bars[msi_table_info & 0x7]; 143 | const msi_table = virt.asHigherHalfUncached( 144 | [*]volatile u32, 145 | msi_table_bar.?.base_address + (msi_table_info & 0xFFFFFFF8), 146 | ); 147 | msi_table[3] = 0; 148 | msi_table[2] = msi_data; 149 | msi_table[0] = @truncate(msi_addr); 150 | msi_table[1] = @truncate(msi_addr >> 32); 151 | // Set the enable bit in the message control register 152 | const message_control = self.capability.read(u16, 0x2); 153 | self.capability.write(u16, 0x2, message_control | (1 << 15)); 154 | } else { 155 | logger.info("Enabling MSI for device, lapic_id: {}, vector: {}", .{ lapic_id, vector }); 156 | self.capability.write(u32, 0x4, @truncate(msi_addr)); 157 | const message_control = self.capability.read(u16, 0x2); 158 | // 64-bit 159 | if (message_control & (1 << 7) != 0) { 160 | self.capability.write(u32, 0x8, @as(u32, @intCast(msi_addr >> 32))); 161 | self.capability.write(u16, 0xC, msi_data); 162 | } else { 163 | self.capability.write(u16, 0x8, msi_data); 164 | } 165 | // Set the enable bit in the message control register 166 | self.capability.write(u16, 0x2, message_control | (1 << 0)); 167 | } 168 | 169 | // Clear the interrupt disable bit in the command register 170 | const command_register = self.capability.device.read(u32, 0x4); 171 | self.capability.device.write(u32, 0x4, command_register & ~@as(u16, 1 << 10)); 172 | } 173 | }; 174 | 175 | pub const Device = struct { 176 | node: DeviceList.Node = .{ .data = {} }, 177 | address: Address, 178 | parent: ?*Device, 179 | bars: [6]?BarInfo, 180 | capabilities: std.BoundedArray(Capability, 32) = .{}, 181 | msis: std.BoundedArray(MSI, 32) = .{}, 182 | vendor_id: u16, 183 | device_id: u16, 184 | class_id: u8, 185 | subclass_id: u8, 186 | prog_if: u8, 187 | is_multifunction: bool, 188 | 189 | pub fn read(self: @This(), comptime T: type, offset: u32) T { 190 | return readConfigSpace(T, self.address, offset); 191 | } 192 | 193 | pub fn write(self: @This(), comptime T: type, offset: u32, value: T) void { 194 | writeConfigSpace(T, self.address, offset, value); 195 | } 196 | 197 | pub fn getMSI(self: @This(), prefer_msix: bool) ?MSI { 198 | var result: ?MSI = null; 199 | for (self.msis.constSlice()) |it| { 200 | if (it.is_msix == prefer_msix) { 201 | return it; 202 | } 203 | } 204 | if (result == null and self.msis.len > 0) { 205 | result = self.msis.get(0); 206 | } 207 | return result; 208 | } 209 | }; 210 | 211 | var devices: DeviceList = .{}; 212 | 213 | fn checkFunction(address: Address, parent: ?*Device) !?*Device { 214 | const dword0 = readConfigSpace(u32, address, 0x0); 215 | // Check if the device is present by checking the vendor ID 216 | if ((dword0 & 0xFFFF) == 0xFFFF) { 217 | return null; 218 | } 219 | 220 | const dword1 = readConfigSpace(u32, address, 0x4); 221 | const dword2 = readConfigSpace(u32, address, 0x8); 222 | const dword3 = readConfigSpace(u32, address, 0xC); 223 | const device = try root.allocator.create(Device); 224 | device.* = .{ 225 | .address = address, 226 | .parent = parent, 227 | .bars = [1]?BarInfo{null} ** 6, 228 | .vendor_id = @truncate(dword0), 229 | .device_id = @intCast(dword0 >> 16), 230 | .class_id = @intCast(dword2 >> 24), 231 | .subclass_id = @truncate(dword2 >> 16), 232 | .prog_if = @truncate(dword2 >> 8), 233 | .is_multifunction = ((dword3 >> 16) & (1 << 7)) != 0, 234 | }; 235 | 236 | logger.info( 237 | "Found device {X:0>4}:{X:0>4} at {any}", 238 | .{ device.vendor_id, device.device_id, device.address }, 239 | ); 240 | 241 | // Find all BARs 242 | var last_bar_was_64bit = false; 243 | for (&device.bars, 0..) |*bar_info, i| { 244 | if (last_bar_was_64bit) { 245 | last_bar_was_64bit = false; 246 | continue; 247 | } 248 | 249 | const bar_offset: u32 = 0x10 + @as(u32, @intCast(i)) * 4; 250 | const bar = device.read(u32, bar_offset); 251 | const is_mmio = (bar & 0x1) == 0; 252 | if (is_mmio) { 253 | var base_address: u64 = bar & 0xFFFFFFF0; 254 | const is_64bit = ((bar >> 1) & 0x3) == 0x2; 255 | if (is_64bit) { 256 | const bar_high = device.read(u32, bar_offset + 4); 257 | base_address |= @as(u64, bar_high) << 32; 258 | } 259 | device.write(u32, bar_offset, 0xFFFFFFFF); 260 | const ones_out = device.read(u32, bar_offset) & 0xFFFFFFF0; 261 | const length = ~ones_out +% 1; 262 | bar_info.* = .{ 263 | .base_address = base_address, 264 | .length = length, 265 | .is_mmio = true, 266 | .is_64bit = is_64bit, 267 | .is_prefetchable = (bar & (1 << 3)) != 0, 268 | }; 269 | } else { 270 | const base_address = bar & 0xFFFFFFFC; 271 | device.write(u32, bar_offset, 0xFFFFFFFF); 272 | const ones_out = device.read(u32, bar_offset) & 0xFFFFFFFC; 273 | bar_info.* = .{ 274 | .base_address = base_address, 275 | .length = ~ones_out +% 1, 276 | .is_mmio = false, 277 | .is_64bit = false, 278 | .is_prefetchable = false, 279 | }; 280 | } 281 | 282 | device.write(u32, bar_offset, bar); 283 | } 284 | 285 | // Iterate the capability list 286 | if (((dword1 >> 16) & (1 << 4)) != 0) { 287 | var offset: u32 = device.read(u32, 0x34) & 0xFC; 288 | while (offset != 0) { 289 | const pci_word = device.read(u16, offset); 290 | const capability = try device.capabilities.addOne(); 291 | capability.* = .{ .device = device, .offset = offset, .id = @intCast(pci_word & 0xFF) }; 292 | offset = pci_word >> 8; 293 | } 294 | } 295 | 296 | // Collect MSI capabilities 297 | for (device.capabilities.constSlice()) |cap| { 298 | switch (cap.id) { 299 | 0x5 => { 300 | const msi = try device.msis.addOne(); 301 | msi.* = .{ .capability = cap, .is_msix = false }; 302 | }, 303 | 0x11 => { 304 | const msi = try device.msis.addOne(); 305 | msi.* = .{ .capability = cap, .is_msix = true }; 306 | }, 307 | else => {}, 308 | } 309 | } 310 | 311 | devices.append(&device.node); 312 | 313 | inline for (drivers.pci_drivers) |driver| { 314 | switch (@as(drivers.PCIDriverDiscovery, driver.discovery)) { 315 | .all => try driver.handler(device), 316 | .id => |id| if (device.vendor_id == id.vendor and device.device_id == id.device) { 317 | try driver.handler(device); 318 | }, 319 | .class => |class| if (device.class_id == class.class_id and device.subclass_id == class.subclass_id) { 320 | if (class.prog_if == null or device.prog_if == class.prog_if.?) { 321 | try driver.handler(device); 322 | } 323 | }, 324 | } 325 | } 326 | 327 | return device; 328 | } 329 | 330 | fn checkDevice(segment: u16, bus: u8, device_id: u5) !void { 331 | var address: Address = .{ .segment = segment, .bus = bus, .device = device_id, .function = 0 }; 332 | const device = try checkFunction(address, null) orelse return; 333 | if (device.is_multifunction) { 334 | for (1..8) |function| { 335 | address.function = @intCast(function); 336 | _ = try checkFunction(address, device); 337 | } 338 | } 339 | } 340 | 341 | pub fn scanBus(segment: u16, bus: u8) !void { 342 | for (0..32) |i| { 343 | try checkDevice(segment, bus, @intCast(i)); 344 | } 345 | } 346 | 347 | pub fn init() !void { 348 | const mcfg_table = acpi.findTable(uacpi.ACPI_MCFG_SIGNATURE) orelse { 349 | logger.err("Could not find the MCFG table", .{}); 350 | return error.TableNotFound; 351 | }; 352 | 353 | read_config_space_fn = ConfigSpaceECAM.read; 354 | write_config_space_fn = ConfigSpaceECAM.write; 355 | 356 | const hdr = mcfg_table.uacpi_table.unnamed_0.hdr; 357 | const mcfg: *uacpi.acpi_mcfg = @ptrCast(hdr); 358 | const entry_count = @divExact(hdr.*.length - @sizeOf(uacpi.acpi_mcfg), @sizeOf(uacpi.struct_acpi_mcfg_allocation)); 359 | for (mcfg.entries()[0..entry_count]) |entry| { 360 | pci_segments[entry.segment] = .{ 361 | .base_address = entry.address, 362 | .segment = entry.segment, 363 | .start_bus = entry.start_bus, 364 | .end_bus = entry.end_bus, 365 | }; 366 | logger.info( 367 | "Found PCI segment {X:0>4} with base address 0x{X} and bus range {d}-{d}", 368 | .{ entry.segment, entry.address, entry.start_bus, entry.end_bus }, 369 | ); 370 | } 371 | } 372 | -------------------------------------------------------------------------------- /kernel/src/per_cpu.zig: -------------------------------------------------------------------------------- 1 | const root = @import("root"); 2 | const std = @import("std"); 3 | 4 | const apic = @import("./apic.zig"); 5 | const arch = @import("./arch.zig"); 6 | const phys = @import("./phys.zig"); 7 | const process = @import("./process.zig"); 8 | const scheduler = @import("./scheduler.zig"); 9 | const utils = @import("./utils.zig"); 10 | const virt = @import("./virt.zig"); 11 | 12 | pub const PerCpu = struct { 13 | self: *PerCpu, 14 | gdt: arch.GDT = .{}, 15 | idt: arch.IDT = .{}, 16 | tss: arch.TSS = std.mem.zeroes(arch.TSS), 17 | lapic_base: u64 = 0, 18 | lapic_id: u32 = 0, 19 | thread: ?*scheduler.Thread = null, 20 | 21 | pub fn currentProcess(self: *PerCpu) ?*process.Process { 22 | if (self.thread) |thread| { 23 | return thread.parent; 24 | } else { 25 | return null; 26 | } 27 | } 28 | }; 29 | 30 | var bsp_percpu: PerCpu = .{ .self = undefined }; 31 | 32 | pub fn initBsp() void { 33 | bsp_percpu = .{ 34 | .self = &bsp_percpu, 35 | .lapic_base = virt.asHigherHalf(u64, arch.rdmsr(.IA32_APIC_BASE) & ~@as(u64, 0xFFF)), 36 | }; 37 | 38 | arch.wrmsr(.IA32_GS_BASE, @intFromPtr(&bsp_percpu)); 39 | arch.wrmsr(.IA32_KERNEL_GS_BASE, 0); 40 | 41 | bsp_percpu.gdt.load(); 42 | bsp_percpu.idt.load(false); 43 | } 44 | 45 | pub fn initFeatures() void { 46 | var cr4 = asm volatile ("mov %%cr4, %[result]" 47 | : [result] "=r" (-> u64), 48 | ); 49 | 50 | cr4 |= (1 << 9); 51 | 52 | asm volatile ("mov %[value], %%cr4" 53 | : 54 | : [value] "r" (cr4), 55 | ); 56 | } 57 | 58 | pub fn init() !void { 59 | const instance = try root.allocator.create(PerCpu); 60 | instance.* = .{ 61 | .self = instance, 62 | .lapic_base = virt.asHigherHalf(u64, arch.rdmsr(.IA32_APIC_BASE) & ~@as(u64, 0xFFF)), 63 | }; 64 | 65 | const intr_stack = try utils.KernelStack.allocate(4); 66 | const ist_stack = try utils.KernelStack.allocate(4); 67 | const sched_stack = try utils.KernelStack.allocate(4); 68 | const pf_stack = try utils.KernelStack.allocate(4); 69 | 70 | instance.lapic_id = apic.getLapicID(); 71 | instance.tss.rsp[0] = intr_stack.getEndAddress(); 72 | instance.tss.ist[0] = ist_stack.getEndAddress(); 73 | instance.tss.ist[1] = sched_stack.getEndAddress(); 74 | instance.tss.ist[2] = pf_stack.getEndAddress(); 75 | 76 | instance.gdt.load(); 77 | instance.gdt.loadTSS(&instance.tss); 78 | instance.idt.load(true); 79 | 80 | arch.wrmsr(.IA32_GS_BASE, @intFromPtr(&bsp_percpu)); 81 | arch.wrmsr(.IA32_KERNEL_GS_BASE, 0); 82 | } 83 | 84 | pub inline fn get() *PerCpu { 85 | return asm volatile ("mov %%gs:0, %[result]" 86 | : [result] "=r" (-> *PerCpu), 87 | ); 88 | } 89 | -------------------------------------------------------------------------------- /kernel/src/phys.zig: -------------------------------------------------------------------------------- 1 | const logger = std.log.scoped(.phys); 2 | 3 | const std = @import("std"); 4 | const limine = @import("limine"); 5 | 6 | const arch = @import("arch.zig"); 7 | const lock = @import("lock.zig"); 8 | const utils = @import("utils.zig"); 9 | const virt = @import("virt.zig"); 10 | 11 | const PageList = std.SinglyLinkedList(void); 12 | const RegionQueue = std.TailQueue(void); 13 | 14 | pub const PageUsage = enum(u4) { 15 | invalid, 16 | pfn_database, 17 | free, 18 | conventional, 19 | dma, 20 | stack, 21 | page_table, 22 | max = 0b1111, 23 | }; 24 | 25 | pub const Page = struct { 26 | node: PageList.Node = .{ .data = {} }, 27 | info: packed struct(u64) { 28 | pfn: u52, 29 | usage: PageUsage, 30 | is_dirty: u1, 31 | reserved: u7, 32 | }, 33 | }; 34 | 35 | const PhysicalRegion = struct { 36 | node: RegionQueue.Node = .{ .data = {} }, 37 | base_address: u64, 38 | page_count: usize, 39 | 40 | fn pages(self: *@This()) []Page { 41 | const ptr: [*]Page = @ptrFromInt(@intFromPtr(self) + @sizeOf(PhysicalRegion)); 42 | return ptr[0..self.page_count]; 43 | } 44 | }; 45 | 46 | var pmm_lock: lock.Spinlock = .{}; 47 | var free_pages: PageList = .{}; 48 | var regions: RegionQueue = .{}; 49 | 50 | pub fn init(memory_map_res: *limine.MemoryMapResponse) !void { 51 | logger.info("Current system memory map:", .{}); 52 | 53 | for (memory_map_res.entries()) |entry| { 54 | if (entry.kind != .usable) { 55 | continue; 56 | } 57 | 58 | const page_count = std.math.divCeil(usize, entry.length, std.mem.page_size) catch unreachable; 59 | const reserved = std.mem.alignForward(usize, @sizeOf(PhysicalRegion) + @sizeOf(Page) * page_count, std.mem.page_size); 60 | const aligned_length = page_count * std.mem.page_size; 61 | 62 | if (aligned_length - reserved < std.mem.page_size * 8) { 63 | logger.debug( 64 | "Skipping usable physical region 0x{X}-0x{X}, too small to track", 65 | .{ entry.base, entry.base + aligned_length }, 66 | ); 67 | continue; 68 | } 69 | 70 | const region = virt.asHigherHalf(*PhysicalRegion, entry.base); 71 | region.* = .{ .base_address = entry.base, .page_count = page_count }; 72 | 73 | logger.info( 74 | "Tracking physical region 0x{X}-0x{X}, {d} KiB, {d} pages, {d} KiB for PFDB", 75 | .{ region.base_address, region.base_address + aligned_length, aligned_length / 1024, region.page_count, reserved / 1024 }, 76 | ); 77 | 78 | const pages = region.pages(); 79 | const reserved_page_count = reserved / std.mem.page_size; 80 | 81 | for (pages, 0..) |*page, i| { 82 | page.* = Page{ 83 | .info = .{ 84 | .pfn = @truncate((region.base_address + i * std.mem.page_size) >> 12), 85 | .usage = if (i < reserved_page_count) .pfn_database else .free, 86 | .is_dirty = @intFromBool(i >= reserved_page_count), 87 | .reserved = 0, 88 | }, 89 | }; 90 | 91 | if (i >= reserved_page_count) { 92 | free_pages.prepend(&page.node); 93 | } 94 | } 95 | 96 | regions.append(®ion.node); 97 | } 98 | } 99 | 100 | pub fn allocate(pages: usize, usage: PageUsage) ?u64 { 101 | std.debug.assert(pages == 1); 102 | std.debug.assert(@intFromEnum(usage) >= @intFromEnum(PageUsage.conventional)); 103 | 104 | const page: *Page = blk: { 105 | pmm_lock.lock(); 106 | defer pmm_lock.unlock(); 107 | break :blk @fieldParentPtr("node", free_pages.popFirst() orelse return null); 108 | }; 109 | 110 | std.debug.assert(page.info.usage == .free); 111 | page.info.usage = usage; 112 | 113 | const page_phys = page.info.pfn << 12; 114 | if (page.info.is_dirty == 1) { 115 | const page_virt = virt.asHigherHalf(*[std.mem.page_size]u8, page_phys); 116 | @memset(page_virt, 0); 117 | } 118 | 119 | return page_phys; 120 | } 121 | 122 | pub fn free(address: u64, pages: usize) void { 123 | _ = address; 124 | _ = pages; 125 | } 126 | -------------------------------------------------------------------------------- /kernel/src/tar.zig: -------------------------------------------------------------------------------- 1 | // https://github.com/FlorenceOS/Florence/blob/master/lib/format/tar.zig 2 | 3 | const std = @import("std"); 4 | 5 | const abi = @import("./abi.zig"); 6 | 7 | pub const FileType = enum(u8) { 8 | normal = '0', 9 | hard_link = '1', 10 | symbolic_link = '2', 11 | character_device = '3', 12 | block_device = '4', 13 | directory = '5', 14 | fifo = '6', 15 | contiguous_file = '7', 16 | long_file_name = 'L', 17 | _, 18 | }; 19 | 20 | pub const File = struct { 21 | name: []const u8, 22 | kind: FileType, 23 | data: []const u8, 24 | link: []const u8, 25 | uid: abi.uid_t, 26 | gid: abi.gid_t, 27 | mtime: abi.time_t, 28 | mode: abi.mode_t, 29 | }; 30 | 31 | const TarHeader = extern struct { 32 | file_name: [100]u8, 33 | file_mode: [8]u8, 34 | uid: [8]u8, 35 | gid: [8]u8, 36 | file_size: [12]u8, 37 | last_modified: [12]u8, 38 | header_checksum: [8]u8, 39 | link_indicator: u8, 40 | linked_name: [100]u8, 41 | padding: [255]u8, 42 | 43 | fn validate(self: *const @This()) bool { 44 | for (self.file_size[0..11]) |ch| { 45 | if (ch < '0' or ch > '9') { 46 | return false; 47 | } 48 | } 49 | 50 | if (self.file_size[11] != 0) { 51 | return false; 52 | } 53 | 54 | return true; 55 | } 56 | 57 | fn fileName(self: *const @This()) []const u8 { 58 | const length = std.mem.indexOfScalar(u8, &self.file_name, 0) orelse self.file_name.len; 59 | return self.file_name[0..length]; 60 | } 61 | 62 | fn fileMode(self: *const @This()) !abi.mode_t { 63 | return std.fmt.parseUnsigned(abi.mode_t, self.file_mode[0..7], 8); 64 | } 65 | 66 | fn getUID(self: *const @This()) !abi.uid_t { 67 | return std.fmt.parseUnsigned(abi.uid_t, self.uid[0..7], 8); 68 | } 69 | 70 | fn getGID(self: *const @This()) !abi.gid_t { 71 | return std.fmt.parseUnsigned(abi.gid_t, self.gid[0..7], 8); 72 | } 73 | 74 | fn lastModified(self: *const @This()) !abi.time_t { 75 | return std.fmt.parseUnsigned(abi.time_t, self.last_modified[0..11], 8); 76 | } 77 | 78 | fn linkedName(self: *const @This()) []const u8 { 79 | const length = std.mem.indexOfScalar(u8, &self.linked_name, 0) orelse self.linked_name.len; 80 | return self.linked_name[0..length]; 81 | } 82 | 83 | fn fileSize(self: *const @This()) !usize { 84 | return std.fmt.parseUnsigned(usize, self.file_size[0..11], 8); 85 | } 86 | }; 87 | 88 | const TarIterator = struct { 89 | buffer: []const u8, 90 | name_override: ?[]const u8, 91 | offset: usize, 92 | 93 | pub fn next(self: *TarIterator) !?File { 94 | const header_buf = self.buffer[self.offset..]; 95 | 96 | if (header_buf.len < @sizeOf(TarHeader)) { 97 | return error.UnexpectedEof; 98 | } 99 | 100 | // We expect at least two null entries at the end of the file 101 | if (std.mem.allEqual(u8, header_buf[0..@sizeOf(TarHeader)], 0)) { 102 | const next_header_buf = self.buffer[self.offset + @sizeOf(TarHeader) ..]; 103 | 104 | if (next_header_buf.len < @sizeOf(TarHeader)) { 105 | return error.UnexpectedEof; 106 | } 107 | 108 | if (!std.mem.allEqual(u8, next_header_buf[0..@sizeOf(TarHeader)], 0)) { 109 | return error.FormatError; 110 | } 111 | 112 | return null; 113 | } 114 | 115 | const header = @as(*const TarHeader, @ptrCast(header_buf)); 116 | 117 | if (!header.validate()) { 118 | return error.InvalidHeader; 119 | } 120 | 121 | const file_size = try header.fileSize(); 122 | const block_leftover = file_size % 512; 123 | const file_block_size = if (block_leftover == 0) file_size else file_size + 512 - block_leftover; 124 | 125 | self.offset += @sizeOf(TarHeader) + file_block_size; 126 | 127 | // TODO: Handle files with long names properly 128 | if (header.link_indicator == 'L') { 129 | self.name_override = header_buf[@sizeOf(TarHeader) .. @sizeOf(TarHeader) + file_size - 1]; 130 | return self.next(); 131 | } 132 | 133 | const name = self.name_override orelse header.fileName(); 134 | self.name_override = null; 135 | return .{ 136 | .name = name, 137 | .kind = @as(FileType, @enumFromInt(header.link_indicator)), 138 | .data = header_buf[@sizeOf(TarHeader) .. @sizeOf(TarHeader) + file_size], 139 | .link = header.linkedName(), 140 | .uid = try header.getUID(), 141 | .gid = try header.getGID(), 142 | .mtime = try header.lastModified(), 143 | .mode = try header.fileMode(), 144 | }; 145 | } 146 | }; 147 | 148 | pub fn iterate(bytes: []const u8) TarIterator { 149 | return .{ 150 | .buffer = bytes, 151 | .name_override = null, 152 | .offset = 0, 153 | }; 154 | } 155 | -------------------------------------------------------------------------------- /kernel/src/time.zig: -------------------------------------------------------------------------------- 1 | const logger = std.log.scoped(.time); 2 | 3 | const std = @import("std"); 4 | const limine = @import("limine"); 5 | 6 | const Clock = enum { 7 | realtime, 8 | monotonic, 9 | }; 10 | 11 | pub const Timespec = struct { 12 | seconds: i64, 13 | nanoseconds: i64, 14 | 15 | pub fn add(self: @This(), other: @This()) @This() { 16 | var result = self; 17 | if (self.nanoseconds + other.nanoseconds >= std.time.ns_per_s - 1) { 18 | result.nanoseconds = (self.nanoseconds + other.nanoseconds) - std.time.ns_per_s; 19 | result.seconds += 1; 20 | } else { 21 | result.nanoseconds += other.nanoseconds; 22 | } 23 | result.seconds += other.seconds; 24 | return result; 25 | } 26 | }; 27 | 28 | var realtime_clock: Timespec = .{ .seconds = 0, .nanoseconds = 0 }; 29 | var monotonic_clock: Timespec = .{ .seconds = 0, .nanoseconds = 0 }; 30 | 31 | pub fn init(time_res: *limine.BootTimeResponse) void { 32 | realtime_clock.seconds = time_res.boot_time; 33 | } 34 | 35 | pub fn getClock(clock: Clock) Timespec { 36 | return switch (clock) { 37 | .realtime => realtime_clock, 38 | .monotonic => monotonic_clock, 39 | }; 40 | } 41 | 42 | pub fn setClock(clock: Clock, time: Timespec) void { 43 | switch (clock) { 44 | .realtime => realtime_clock = time, 45 | .monotonic => monotonic_clock = time, 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /kernel/src/utils.zig: -------------------------------------------------------------------------------- 1 | const root = @import("root"); 2 | const std = @import("std"); 3 | 4 | const phys = @import("./phys.zig"); 5 | const virt = @import("./virt.zig"); 6 | 7 | pub inline fn kib(comptime value: comptime_int) comptime_int { 8 | return value * 1024; 9 | } 10 | 11 | pub inline fn mib(comptime value: comptime_int) comptime_int { 12 | return kib(value) * 1024; 13 | } 14 | 15 | pub inline fn gib(comptime value: comptime_int) comptime_int { 16 | return mib(value) * 1024; 17 | } 18 | 19 | pub inline fn tib(comptime value: comptime_int) comptime_int { 20 | return gib(value) * 1024; 21 | } 22 | 23 | pub const KernelStack = struct { 24 | base: u64, 25 | size: usize, 26 | 27 | pub fn allocate(pages: usize) !@This() { 28 | const address = root.kernel_va_arena.allocate((pages + 1) * std.mem.page_size) orelse 29 | return error.OutOfMemory; 30 | 31 | try virt.kernel_address_space.page_table.mapPage(address, 0, virt.PTEFlags.guard_page); 32 | 33 | for (0..pages) |i| { 34 | const phys_addr = phys.allocate(1, .stack) orelse return error.NoMemory; 35 | try virt.kernel_address_space.page_table.mapPage( 36 | address + (i + 1) * std.mem.page_size, 37 | phys_addr, 38 | virt.PTEFlags.present | virt.PTEFlags.writable, 39 | ); 40 | } 41 | 42 | return .{ 43 | .base = address + std.mem.page_size, 44 | .size = pages * std.mem.page_size, 45 | }; 46 | } 47 | 48 | pub fn getEndAddress(self: *const @This()) u64 { 49 | return self.base + self.size; 50 | } 51 | 52 | pub fn deinit(self: @This()) void { 53 | try virt.kernel_address_space.page_table.unmap(self.base, self.size + std.mem.page_size); 54 | root.kernel_va_arena.free(self.base - std.mem.page_size, self.size + std.mem.page_size); 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /kernel/src/vfs/dev_fs.zig: -------------------------------------------------------------------------------- 1 | const logger = std.log.scoped(.devfs); 2 | 3 | const root = @import("root"); 4 | const std = @import("std"); 5 | 6 | const abi = @import("../abi.zig"); 7 | const debug = @import("../debug.zig"); 8 | const vfs = @import("../vfs.zig"); 9 | const utils = @import("../utils.zig"); 10 | const per_cpu = @import("../per_cpu.zig"); 11 | const input = @import("../drivers/input.zig"); 12 | const ram_fs = @import("ram_fs.zig"); 13 | 14 | const tty_vtable: vfs.VNodeVTable = .{ 15 | .read = TtyVNode.read, 16 | .write = TtyVNode.write, 17 | .ioctl = TtyVNode.ioctl, 18 | .stat = TtyVNode.stat, 19 | }; 20 | 21 | var disk_number: usize = 1; 22 | 23 | const BlockDeviceError = error{ 24 | InputOutput, 25 | }; 26 | 27 | const BlockDeviceVTable = struct { 28 | read_block: fn (self: *BlockDevice, block: usize, buffer: []u8) BlockDeviceError!void, 29 | write_block: fn (self: *BlockDevice, block: usize, buffer: []const u8) BlockDeviceError!void, 30 | }; 31 | 32 | const BlockDevice = struct { 33 | vnode: vfs.VNode, 34 | vtable: *const BlockDeviceVTable, 35 | sector_size: usize, 36 | sector_count: usize, 37 | name: [24]u8 = undefined, 38 | 39 | const vnode_vtable: vfs.VNodeVTable = .{ 40 | .read = BlockDevice.read, 41 | .write = BlockDevice.write, 42 | .stat = BlockDevice.stat, 43 | }; 44 | 45 | fn iterateSectors( 46 | self: *@This(), 47 | buffer_in: anytype, 48 | disk_offset_in: usize, 49 | small_callback: anytype, 50 | large_callback: anytype, 51 | ) !void { 52 | if (buffer_in.len == 0) { 53 | return; 54 | } 55 | 56 | var first_sector = utils.alignDown(usize, disk_offset_in, self.sector_size) / self.sector_size; 57 | const last_sector = utils.alignDown(usize, disk_offset_in + buffer_in.len - 1, self.sector_size) / self.sector_size; 58 | 59 | if (first_sector == last_sector) { 60 | return small_callback(self, buffer_in, first_sector, disk_offset_in % self.sector_size); 61 | } 62 | 63 | var disk_offset = disk_offset_in; 64 | var buffer = buffer_in; 65 | 66 | if (!utils.isAligned(usize, disk_offset, self.sector_size)) { 67 | const step = utils.alignUp(usize, disk_offset, self.sector_size) - disk_offset; 68 | try small_callback(self, buffer[0..step], first_sector, self.sector_size - step); 69 | buffer = buffer[step..]; 70 | disk_offset += step; 71 | first_sector += 1; 72 | } 73 | 74 | while (buffer.len >= self.sector_size) { 75 | try large_callback(self, buffer[0..self.sector_size], first_sector); 76 | buffer = buffer[self.sector_size..]; 77 | disk_offset += self.sector_size; 78 | first_sector += 1; 79 | } 80 | 81 | if (buffer.len == 0) { 82 | return; 83 | } 84 | 85 | try small_callback(self, buffer, first_sector, 0); 86 | } 87 | 88 | fn doLargeRead(self: *BlockDevice, buffer: []u8, sector: usize) !void { 89 | return self.vtable.read_block(self, sector, buffer); 90 | } 91 | 92 | fn doLargeWrite(self: *BlockDevice, buffer: []const u8, sector: usize) !void { 93 | return self.vtable.write_block(self, sector, buffer); 94 | } 95 | 96 | fn doSmallRead(self: *BlockDevice, buffer: []u8, sector: usize, offset: usize) !void { 97 | var temp_buffer: [512]u8 = undefined; 98 | try self.vtable.read_block(self, sector, &temp_buffer); 99 | @memcpy(buffer, temp_buffer[offset..][0..buffer.len]); 100 | } 101 | 102 | fn doSmallWrite(self: *BlockDevice, buffer: []const u8, sector: usize, offset: usize) !void { 103 | var temp_buffer: [512]u8 = undefined; 104 | try self.vtable.read_block(self, sector, &temp_buffer); 105 | @memcpy(temp_buffer[offset..][0..buffer.len], buffer); 106 | try self.vtable.write_block(self, sector, &temp_buffer); 107 | } 108 | 109 | fn read(vnode: *vfs.VNode, buffer: []u8, offset: usize, _: usize) vfs.ReadError!usize { 110 | const self: *@This() = @fieldParentPtr("vnode", vnode); 111 | const max_read = @min(buffer.len, self.sector_count * self.sector_size - offset); 112 | try self.iterateSectors(buffer[0..max_read], offset, doSmallRead, doLargeRead); 113 | return max_read; 114 | } 115 | 116 | fn write(vnode: *vfs.VNode, buffer: []const u8, offset: usize, _: usize) vfs.WriteError!usize { 117 | const self: *@This() = @fieldParentPtr("vnode", vnode); 118 | const max_write = @min(buffer.len, self.sector_count * self.sector_size - offset); 119 | try self.iterateSectors(buffer[0..max_write], offset, doSmallWrite, doLargeWrite); 120 | return max_write; 121 | } 122 | 123 | fn stat(vnode: *vfs.VNode, buffer: *abi.stat) vfs.StatError!void { 124 | const self: *@This() = @fieldParentPtr("vnode", vnode); 125 | buffer.* = std.mem.zeroes(abi.stat); 126 | buffer.st_mode = 0o777 | abi.S_IFBLK; 127 | buffer.st_size = @intCast(self.sector_count * self.sector_size); 128 | buffer.st_blksize = @intCast(self.sector_size); 129 | buffer.st_blocks = @intCast(self.sector_count); 130 | } 131 | }; 132 | 133 | fn BlockDeviceWrapper(comptime T: type) type { 134 | return struct { 135 | block: BlockDevice, 136 | device: T, 137 | 138 | const block_vtable: BlockDeviceVTable = .{ 139 | .read_block = @This().readBlock, 140 | .write_block = @This().writeBlock, 141 | }; 142 | 143 | fn readBlock(block_dev: *BlockDevice, block: usize, buffer: []u8) BlockDeviceError!void { 144 | const self: *@This() = @fieldParentPtr("block", block_dev); 145 | try self.device.readBlock(block, buffer); 146 | } 147 | 148 | fn writeBlock(block_dev: *BlockDevice, block: usize, buffer: []const u8) BlockDeviceError!void { 149 | const self: *@This() = @fieldParentPtr("block", block_dev); 150 | try self.device.writeBlock(block, buffer); 151 | } 152 | }; 153 | } 154 | 155 | const PartitionBlockDeviceWrapper = struct { 156 | block: BlockDevice, 157 | parent: *BlockDevice, 158 | start_block: usize, 159 | end_block: usize, 160 | 161 | const block_vtable: BlockDeviceVTable = .{ 162 | .read_block = @This().readBlock, 163 | .write_block = @This().writeBlock, 164 | }; 165 | 166 | fn readBlock(block_dev: *BlockDevice, block: usize, buffer: []u8) BlockDeviceError!void { 167 | const self: *@This() = @fieldParentPtr("block", block_dev); 168 | std.debug.assert(self.start_block + block < self.end_block); 169 | return self.parent.vtable.read_block(self.parent, self.start_block + block, buffer); 170 | } 171 | 172 | fn writeBlock(block_dev: *BlockDevice, block: usize, buffer: []const u8) BlockDeviceError!void { 173 | const self: *@This() = @fieldParentPtr("block", block_dev); 174 | std.debug.assert(self.start_block + block < self.end_block); 175 | return self.parent.vtable.write_block(self.parent, self.start_block + block, buffer); 176 | } 177 | }; 178 | 179 | var tty_buffer: @import("../containers/ring_buffer.zig").RingBuffer(u8, 16) = .{}; 180 | 181 | const TtyVNode = struct { 182 | vnode: vfs.VNode, 183 | state: abi.termios = std.mem.zeroInit(abi.termios, .{ 184 | .c_lflag = abi.ECHO | abi.ICANON, 185 | }), 186 | 187 | fn read(_: *vfs.VNode, _: []u8, _: usize, _: usize) vfs.ReadError!usize { 188 | return 0; 189 | } 190 | 191 | fn write(_: *vfs.VNode, buffer: []const u8, _: usize, _: usize) vfs.WriteError!usize { 192 | return buffer.len; 193 | } 194 | 195 | fn ioctl(vnode: *vfs.VNode, request: u64, arg: u64) vfs.IoctlError!u64 { 196 | const self: *@This() = @fieldParentPtr("vnode", vnode); 197 | const process = per_cpu.get().currentProcess().?; 198 | switch (request) { 199 | abi.TCGETS => { 200 | const result = process.validatePointer(abi.termios, arg) catch return .{ .err = abi.EINVAL }; 201 | result.* = self.state; 202 | return 0; 203 | }, 204 | abi.TCSETSW, abi.TCSETSF, abi.TCSETS => { 205 | const result = process.validatePointer(abi.termios, arg) catch return .{ .err = abi.EINVAL }; 206 | self.state = result.*; 207 | return 0; 208 | }, 209 | else => { 210 | logger.warn("Unhandled TTY IO control request 0x{X}", .{request}); 211 | return error.InvalidArgument; 212 | }, 213 | } 214 | } 215 | 216 | fn stat(_: *vfs.VNode, buffer: *abi.stat) vfs.StatError!void { 217 | buffer.* = std.mem.zeroes(abi.stat); 218 | buffer.st_mode = 0o777 | abi.S_IFCHR; 219 | } 220 | }; 221 | 222 | pub fn init(name: []const u8, parent: ?*vfs.VNode) !*vfs.VNode { 223 | const ramfs = try ram_fs.init(name, parent); 224 | ramfs.filesystem.name = "devfs"; 225 | 226 | const tty = try root.allocator.create(TtyVNode); 227 | tty.* = .{ 228 | .vnode = .{ 229 | .vtable = &tty_vtable, 230 | .filesystem = ramfs.filesystem, 231 | .kind = .character_device, 232 | .name = "tty", 233 | }, 234 | }; 235 | try ramfs.insert(&tty.vnode); 236 | 237 | return ramfs; 238 | } 239 | 240 | pub fn addDiskBlockDevice(name: []const u8, device: anytype) !void { 241 | const WrappedDevice = BlockDeviceWrapper(@TypeOf(device)); 242 | 243 | const dev = try vfs.resolve(null, "/dev", 0); 244 | const node = try root.allocator.create(WrappedDevice); 245 | const disk_id = @atomicRmw(usize, &disk_number, .Add, 1, .acq_rel); 246 | node.* = .{ 247 | .block = .{ 248 | .vnode = undefined, 249 | .vtable = &WrappedDevice.block_vtable, 250 | .sector_size = device.getSectorSize(), 251 | .sector_count = device.getSectorCount(), 252 | }, 253 | .device = device, 254 | }; 255 | node.block.vnode = .{ 256 | .vtable = &BlockDevice.vnode_vtable, 257 | .filesystem = dev.filesystem, 258 | .name = try std.fmt.bufPrint(&node.block.name, "{s}{d}", .{ name, disk_id }), 259 | }; 260 | try dev.insert(&node.block.vnode); 261 | try probePartitions(&node.block, node.block.sector_size); 262 | } 263 | 264 | const MbrHeader = extern struct { 265 | reserved0: [510]u8, 266 | magic: u16, 267 | }; 268 | 269 | const GptHeader = extern struct { 270 | signature: [8]u8, 271 | revision: u32, 272 | header_size: u32, 273 | header_crc32: u32, 274 | reserved14: [4]u8, 275 | current_lba: u64, 276 | backup_lba: u64, 277 | first_usable_lba: u64, 278 | last_usable_lba: u64, 279 | disk_guid: std.os.uefi.Guid, 280 | partition_entry_lba: u64, 281 | num_partition_entries: u32, 282 | size_of_partition_entry: u32, 283 | partition_entry_array_crc32: u32, 284 | }; 285 | 286 | const GptPartitionEntry = extern struct { 287 | partition_type_guid: std.os.uefi.Guid, 288 | unique_partition_guid: std.os.uefi.Guid, 289 | starting_lba: u64, 290 | ending_lba: u64, 291 | attributes: u64, 292 | name: [36]u16, 293 | }; 294 | 295 | fn probePartitions(device: *BlockDevice, sector_size: usize) !void { 296 | var buffer = try root.allocator.alloc(u8, sector_size * 2); 297 | _ = try device.vnode.read(buffer, 0, 0); 298 | const dev = try vfs.resolve(null, "/dev", 0); 299 | const mbr_header = @as(*align(1) const MbrHeader, @ptrCast(buffer)); 300 | const gpt_header = @as(*align(1) const GptHeader, @ptrCast(buffer[512..])); 301 | if (std.mem.eql(u8, &gpt_header.signature, "EFI PART") and gpt_header.revision == 0x10000) { 302 | var entry: GptPartitionEntry = undefined; 303 | 304 | for (0..10) |i| { 305 | _ = try device.vnode.read( 306 | std.mem.asBytes(&entry), 307 | gpt_header.partition_entry_lba * sector_size + i * gpt_header.size_of_partition_entry, 308 | 0, 309 | ); 310 | 311 | if (entry.starting_lba == 0) { 312 | break; 313 | } 314 | 315 | const part_node = try root.allocator.create(PartitionBlockDeviceWrapper); 316 | part_node.* = .{ 317 | .block = .{ 318 | .vnode = undefined, 319 | .vtable = &PartitionBlockDeviceWrapper.block_vtable, 320 | .sector_size = device.sector_size, 321 | .sector_count = entry.ending_lba - entry.starting_lba + 1, 322 | }, 323 | .parent = device, 324 | .start_block = entry.starting_lba, 325 | .end_block = entry.ending_lba + 1, 326 | }; 327 | part_node.block.vnode = .{ 328 | .vtable = &BlockDevice.vnode_vtable, 329 | .filesystem = dev.filesystem, 330 | .name = try std.fmt.bufPrint(&part_node.block.name, "{s}p{d}", .{ device.vnode.name, i + 1 }), 331 | }; 332 | try dev.insert(&part_node.block.vnode); 333 | 334 | logger.debug( 335 | "{}: Added partition with start LBA of {} and end LBA of {}", 336 | .{ part_node.block.vnode.getFullPath(), entry.starting_lba, entry.ending_lba }, 337 | ); 338 | } 339 | } else if (mbr_header.magic == 0xAA55) { 340 | logger.debug("{}: MBR partition table detected", .{device.vnode.getFullPath()}); 341 | } else { 342 | logger.debug("{}: No partition table detected", .{device.vnode.getFullPath()}); 343 | } 344 | } 345 | -------------------------------------------------------------------------------- /kernel/src/vfs/ram_fs.zig: -------------------------------------------------------------------------------- 1 | const logger = std.log.scoped(.ramfs); 2 | 3 | const root = @import("root"); 4 | const std = @import("std"); 5 | 6 | const abi = @import("../abi.zig"); 7 | const phys = @import("../phys.zig"); 8 | const utils = @import("../utils.zig"); 9 | const vfs = @import("../vfs.zig"); 10 | const virt = @import("../virt.zig"); 11 | 12 | const ram_fs_vtable: vfs.FileSystemVTable = .{ 13 | .create_file = RamFS.createFile, 14 | .create_dir = RamFS.createDir, 15 | .create_symlink = RamFS.createSymlink, 16 | .allocate_inode = RamFS.allocateInode, 17 | }; 18 | 19 | const ram_fs_file_vtable: vfs.VNodeVTable = .{ 20 | .read = RamFSFile.read, 21 | .write = RamFSFile.write, 22 | .stat = RamFSFile.stat, 23 | }; 24 | 25 | const ram_fs_directory_vtable: vfs.VNodeVTable = .{ 26 | .open = RamFSDirectory.open, 27 | .read_dir = RamFSDirectory.readDir, 28 | .insert = RamFSDirectory.insert, 29 | .stat = RamFSDirectory.stat, 30 | }; 31 | 32 | const RamFSFile = struct { 33 | vnode: vfs.VNode, 34 | data: std.ArrayListAlignedUnmanaged(u8, std.mem.page_size) = .{}, 35 | 36 | fn read(vnode: *vfs.VNode, buffer: []u8, offset: usize, _: usize) vfs.ReadError!usize { 37 | const self: *@This() = @fieldParentPtr("vnode", vnode); 38 | if (offset >= self.data.items.len) { 39 | return 0; 40 | } 41 | 42 | const bytes_read = @min(buffer.len, self.data.items.len - offset); 43 | @memcpy(buffer[0..bytes_read], self.data.items[offset .. offset + bytes_read]); 44 | return bytes_read; 45 | } 46 | 47 | fn write(vnode: *vfs.VNode, buffer: []const u8, offset: usize, _: usize) vfs.WriteError!usize { 48 | const self: *@This() = @fieldParentPtr("vnode", vnode); 49 | if (buffer.len + offset > self.data.items.len) { 50 | self.data.resize(root.allocator, buffer.len + offset) catch return error.OutOfMemory; 51 | // TODO: Zero out the newly allocated content 52 | } 53 | 54 | @memcpy(self.data.items[offset..], buffer); 55 | return buffer.len; 56 | } 57 | 58 | fn stat(vnode: *vfs.VNode, buffer: *abi.stat) vfs.StatError!void { 59 | const self: *@This() = @fieldParentPtr("vnode", vnode); 60 | buffer.* = std.mem.zeroes(abi.stat); 61 | buffer.st_ino = @intCast(vnode.inode); 62 | buffer.st_mode = 0o777 | abi.S_IFREG; 63 | buffer.st_size = @intCast(self.data.items.len); 64 | buffer.st_blksize = std.mem.page_size; 65 | buffer.st_blocks = @intCast(std.mem.alignForward(usize, self.data.items.len, std.mem.page_size) / std.mem.page_size); 66 | } 67 | }; 68 | 69 | const RamFSDirectory = struct { 70 | vnode: vfs.VNode, 71 | children: std.ArrayListUnmanaged(*vfs.VNode) = .{}, 72 | 73 | fn open(vnode: *vfs.VNode, name: []const u8, _: usize) vfs.OpenError!*vfs.VNode { 74 | const self: *@This() = @fieldParentPtr("vnode", vnode); 75 | for (self.children.items) |child| { 76 | if (std.mem.eql(u8, child.name.?, name)) { 77 | return child; 78 | } 79 | } 80 | return error.FileNotFound; 81 | } 82 | 83 | fn readDir(vnode: *vfs.VNode, buffer: []u8, offset: *usize) vfs.ReadDirError!usize { 84 | const self: *@This() = @fieldParentPtr("vnode", vnode); 85 | 86 | var dir_ent = @as(*abi.dirent, @ptrCast(@alignCast(buffer))); 87 | var buffer_offset: usize = 0; 88 | while (offset.* < self.children.items.len) : (offset.* += 1) { 89 | const child = self.children.items[offset.*]; 90 | const name = child.name.?; 91 | const real_size = @sizeOf(abi.dirent) - 1024 + name.len + 1; 92 | if (buffer_offset + real_size > buffer.len) { 93 | break; 94 | } 95 | 96 | dir_ent.d_off = 0; 97 | dir_ent.d_ino = @intCast(vnode.inode); 98 | dir_ent.d_reclen = @intCast(real_size); 99 | dir_ent.d_type = switch (child.kind) { 100 | .file => abi.DT_REG, 101 | .directory => abi.DT_DIR, 102 | .symlink => abi.DT_LNK, 103 | .character_device => abi.DT_CHR, 104 | .block_device => abi.DT_BLK, 105 | .fifo => abi.DT_FIFO, 106 | .socket => abi.DT_SOCK, 107 | }; 108 | 109 | @memcpy(dir_ent.d_name[0..name.len], name); 110 | dir_ent.d_name[name.len] = 0; 111 | buffer_offset += real_size; 112 | dir_ent = @as(*abi.dirent, @ptrCast(@alignCast(buffer[buffer_offset..]))); 113 | } 114 | 115 | return buffer_offset; 116 | } 117 | 118 | fn insert(vnode: *vfs.VNode, child: *vfs.VNode) vfs.InsertError!void { 119 | const self: *@This() = @fieldParentPtr("vnode", vnode); 120 | for (self.children.items) |it| { 121 | if (std.mem.eql(u8, it.name.?, child.name.?)) { 122 | return error.PathAlreadyExists; 123 | } 124 | } 125 | try self.children.append(root.allocator, child); 126 | } 127 | 128 | fn stat(vnode: *vfs.VNode, buffer: *abi.stat) vfs.StatError!void { 129 | buffer.* = std.mem.zeroes(abi.stat); 130 | buffer.st_ino = @intCast(vnode.inode); 131 | buffer.st_mode = 0o777 | abi.S_IFDIR; 132 | } 133 | }; 134 | 135 | const RamFS = struct { 136 | filesystem: vfs.FileSystem, 137 | root: RamFSDirectory, 138 | inode_counter: u64, 139 | 140 | fn createFile(fs: *vfs.FileSystem) vfs.OomError!*vfs.VNode { 141 | const node = try root.allocator.create(RamFSFile); 142 | node.* = .{ 143 | .vnode = .{ 144 | .vtable = &ram_fs_file_vtable, 145 | .filesystem = fs, 146 | .kind = .file, 147 | }, 148 | }; 149 | return &node.vnode; 150 | } 151 | 152 | fn createDir(fs: *vfs.FileSystem) vfs.OomError!*vfs.VNode { 153 | const node = try root.allocator.create(RamFSDirectory); 154 | node.* = .{ 155 | .vnode = .{ 156 | .vtable = &ram_fs_directory_vtable, 157 | .filesystem = fs, 158 | .kind = .directory, 159 | }, 160 | }; 161 | return &node.vnode; 162 | } 163 | 164 | fn createSymlink(fs: *vfs.FileSystem, target: []const u8) vfs.OomError!*vfs.VNode { 165 | const node = try root.allocator.create(vfs.VNode); 166 | node.* = .{ 167 | .vtable = &.{}, 168 | .filesystem = fs, 169 | .kind = .symlink, 170 | .symlink_target = try root.allocator.dupe(u8, target), 171 | }; 172 | return node; 173 | } 174 | 175 | fn allocateInode(self: *vfs.FileSystem) vfs.OomError!u64 { 176 | const fs: *@This() = @fieldParentPtr("filesystem", self); 177 | return @atomicRmw(usize, &fs.inode_counter, .Add, 1, .acq_rel); 178 | } 179 | }; 180 | 181 | pub fn init(name: []const u8, parent: ?*vfs.VNode) !*vfs.VNode { 182 | const ramfs = try root.allocator.create(RamFS); 183 | ramfs.* = .{ 184 | .filesystem = .{ 185 | .vtable = &ram_fs_vtable, 186 | .case_sensitive = true, 187 | .name = "ramfs", 188 | }, 189 | .root = .{ 190 | .vnode = .{ 191 | .vtable = &ram_fs_directory_vtable, 192 | .filesystem = &ramfs.filesystem, 193 | .kind = .directory, 194 | .name = name, 195 | .parent = parent, 196 | }, 197 | }, 198 | .inode_counter = 0, 199 | }; 200 | return &ramfs.root.vnode; 201 | } 202 | -------------------------------------------------------------------------------- /misc/create-image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | # Prepare the sysroot. 6 | rm -rf sysroot 7 | ./jinx sysroot 8 | ./jinx host-build limine 9 | 10 | limine_dir="host-pkgs/limine/usr/local/" 11 | 12 | # Prepare the ISO root. 13 | rm -rf iso_root 14 | mkdir -p iso_root 15 | mkdir -p iso_root/EFI/BOOT 16 | cp pkgs/kernel/usr/bin/zigux iso_root/ 17 | cp $limine_dir/share/limine/limine-bios.sys iso_root/ 18 | cp $limine_dir/share/limine/limine-bios-cd.bin iso_root/ 19 | cp $limine_dir/share/limine/limine-uefi-cd.bin iso_root/ 20 | cp $limine_dir/share/limine/BOOTX64.EFI iso_root/EFI/BOOT/ 21 | 22 | # Make an initramfs tarball from the sysroot. 23 | if type pigz >/dev/null 2>&1; then 24 | tar -I pigz -C sysroot -cf iso_root/initramfs.tar.gz . 25 | else 26 | tar -C sysroot -czf iso_root/initramfs.tar.gz . 27 | fi 28 | 29 | # Write the limine config 30 | cat <iso_root/limine.cfg 31 | TIMEOUT=0 32 | SERIAL=yes 33 | 34 | :kernel 35 | PROTOCOL=limine 36 | KERNEL_PATH=boot:///zigux 37 | MODULE_PATH=\$boot:///initramfs.tar.gz 38 | EOF 39 | 40 | # Create the ISO. 41 | xorriso -as mkisofs -b limine-bios-cd.bin \ 42 | -no-emul-boot -boot-load-size 4 -boot-info-table \ 43 | --efi-boot limine-uefi-cd.bin -efi-boot-part --efi-boot-image \ 44 | --protective-msdos-label iso_root -o zigux.iso 45 | 46 | # Install Limine. 47 | $limine_dir/bin/limine bios-install zigux.iso 48 | -------------------------------------------------------------------------------- /misc/cross_file.txt: -------------------------------------------------------------------------------- 1 | [binaries] 2 | c = 'x86_64-zigux-gcc' 3 | cpp = 'x86_64-zigux-g++' 4 | ar = 'x86_64-zigux-ar' 5 | strip = 'x86_64-zigux-strip' 6 | pkg-config = 'x86_64-zigux-pkg-config' 7 | 8 | [host_machine] 9 | system = 'zigux' 10 | cpu_family = 'x86_64' 11 | cpu = 'x86_64' 12 | endian = 'little' 13 | -------------------------------------------------------------------------------- /misc/run-emulator.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | qemu_args="-cdrom $1 -debugcon stdio -smp 1 -m 2G -M q35,smm=off -enable-kvm -cpu qemu64,+fsgsbase -no-reboot -no-shutdown -s -d int -D int_log" 4 | 5 | qemu-system-x86_64 ${qemu_args} ${@:2} 6 | -------------------------------------------------------------------------------- /patches/autoconf/jinx-working-patch.patch: -------------------------------------------------------------------------------- 1 | diff --git autoconf-clean/build-aux/config.guess autoconf-workdir/build-aux/config.guess 2 | index cdfc439..0e1b56a 100755 3 | --- autoconf-clean/build-aux/config.guess 4 | +++ autoconf-workdir/build-aux/config.guess 5 | @@ -4,7 +4,8 @@ 6 | 7 | # shellcheck disable=SC2006,SC2268 # see below for rationale 8 | 9 | -timestamp='2023-08-22' 10 | +# timestamp it to always be newer 11 | +timestamp='9999-99-99' 12 | 13 | # This file is free software; you can redistribute it and/or modify it 14 | # under the terms of the GNU General Public License as published by 15 | @@ -933,6 +934,9 @@ EOF 16 | i*:PW*:*) 17 | GUESS=$UNAME_MACHINE-pc-pw32 18 | ;; 19 | + *:Zigux:*:*) 20 | + GUESS=$UNAME_MACHINE-pc-zigux 21 | + ;; 22 | *:SerenityOS:*:*) 23 | GUESS=$UNAME_MACHINE-pc-serenity 24 | ;; 25 | diff --git autoconf-clean/build-aux/config.sub autoconf-workdir/build-aux/config.sub 26 | index defe52c..8600125 100755 27 | --- autoconf-clean/build-aux/config.sub 28 | +++ autoconf-workdir/build-aux/config.sub 29 | @@ -4,7 +4,8 @@ 30 | 31 | # shellcheck disable=SC2006,SC2268 # see below for rationale 32 | 33 | -timestamp='2023-09-19' 34 | +# timestamp it to always be newer 35 | +timestamp='9999-99-99' 36 | 37 | # This file is free software; you can redistribute it and/or modify it 38 | # under the terms of the GNU General Public License as published by 39 | @@ -1749,7 +1750,7 @@ case $os in 40 | | mirbsd* | netbsd* | dicos* | openedition* | ose* \ 41 | | bitrig* | openbsd* | secbsd* | solidbsd* | libertybsd* | os108* \ 42 | | ekkobsd* | freebsd* | riscix* | lynxos* | os400* \ 43 | - | bosx* | nextstep* | cxux* | oabi* \ 44 | + | bosx* | nextstep* | cxux* | oabi* | zigux* \ 45 | | ptx* | ecoff* | winnt* | domain* | vsta* \ 46 | | udi* | lites* | ieee* | go32* | aux* | hcos* \ 47 | | chorusrdb* | cegcc* | glidix* | serenity* \ 48 | -------------------------------------------------------------------------------- /patches/binutils/jinx-working-patch.patch: -------------------------------------------------------------------------------- 1 | diff --git binutils-clean/bfd/Makefile.am binutils-workdir/bfd/Makefile.am 2 | index 5c5fdef..3c91651 100644 3 | --- binutils-clean/bfd/Makefile.am 4 | +++ binutils-workdir/bfd/Makefile.am 5 | @@ -778,7 +778,7 @@ ofiles: stamp-ofiles ; @true 6 | libbfd_la_SOURCES = $(BFD32_LIBS_CFILES) 7 | EXTRA_libbfd_la_SOURCES = $(CFILES) 8 | libbfd_la_DEPENDENCIES = $(OFILES) ofiles ../libsframe/libsframe.la 9 | -libbfd_la_LIBADD = `cat ofiles` @SHARED_LIBADD@ $(LIBDL) $(ZLIB) $(ZSTD_LIBS) ../libsframe/libsframe.la 10 | +libbfd_la_LIBADD = `cat ofiles` @SHARED_LIBADD@ $(LIBDL) $(ZLIB) $(ZSTD_LIBS) $(SFRAME_LIB_PATH) ../libsframe/libsframe.la 11 | libbfd_la_LDFLAGS += -release `cat libtool-soversion` @SHARED_LDFLAGS@ 12 | 13 | # This file holds an array associating configuration triplets and 14 | diff --git binutils-clean/bfd/config.bfd binutils-workdir/bfd/config.bfd 15 | index bdee539..48b2360 100644 16 | --- binutils-clean/bfd/config.bfd 17 | +++ binutils-workdir/bfd/config.bfd 18 | @@ -664,6 +664,11 @@ case "${targ}" in 19 | targ_selvecs= 20 | targ64_selvecs=x86_64_elf64_vec 21 | ;; 22 | + i[3-7]86-*-zigux*) 23 | + targ_defvec=i386_elf32_vec 24 | + targ_selvecs= 25 | + targ64_selvecs=x86_64_elf64_vec 26 | + ;; 27 | #ifdef BFD64 28 | x86_64-*-cloudabi*) 29 | targ_defvec=x86_64_elf64_cloudabi_vec 30 | @@ -734,6 +739,11 @@ case "${targ}" in 31 | targ_selvecs="i386_elf32_vec iamcu_elf32_vec x86_64_elf32_vec" 32 | want64=true 33 | ;; 34 | + x86_64-*-zigux*) 35 | + targ_defvec=x86_64_elf64_vec 36 | + targ_selvecs=i386_elf32_vec 37 | + want64=true 38 | + ;; 39 | #endif 40 | i[3-7]86-*-lynxos*) 41 | targ_defvec=i386_elf32_vec 42 | diff --git binutils-clean/gas/configure.tgt binutils-workdir/gas/configure.tgt 43 | index 3429f85..8fe03c1 100644 44 | --- binutils-clean/gas/configure.tgt 45 | +++ binutils-workdir/gas/configure.tgt 46 | @@ -227,6 +227,7 @@ case ${generic_target} in 47 | i386-*-beos*) fmt=elf ;; 48 | i386-*-elfiamcu) fmt=elf arch=iamcu ;; 49 | i386-*-elf*) fmt=elf ;; 50 | + i386-*-zigux*) fmt=elf em=gnu ;; 51 | i386-*-fuchsia*) fmt=elf ;; 52 | i386-*-haiku*) fmt=elf em=haiku ;; 53 | i386-*-genode*) fmt=elf ;; 54 | diff --git binutils-clean/gprofng/libcollector/configure.ac binutils-workdir/gprofng/libcollector/configure.ac 55 | index 7af9581..c55e424 100644 56 | --- binutils-clean/gprofng/libcollector/configure.ac 57 | +++ binutils-workdir/gprofng/libcollector/configure.ac 58 | @@ -18,7 +18,7 @@ dnl . 59 | 60 | m4_include([../../bfd/version.m4]) 61 | AC_INIT([gprofng], BFD_VERSION) 62 | -AC_CONFIG_MACRO_DIRS([../../config ../..]) 63 | +#AC_CONFIG_MACRO_DIRS([../../config ../..]) 64 | AC_CONFIG_AUX_DIR(../..) 65 | AC_CANONICAL_TARGET 66 | AM_INIT_AUTOMAKE 67 | diff --git binutils-clean/ld/configure.tgt binutils-workdir/ld/configure.tgt 68 | index c62b958..3873c96 100644 69 | --- binutils-clean/ld/configure.tgt 70 | +++ binutils-workdir/ld/configure.tgt 71 | @@ -378,6 +378,9 @@ i[3-7]86-*-linux-*) targ_emul=elf_i386 72 | i[3-7]86-*-redox*) targ_emul=elf_i386 73 | targ_extra_emuls=elf_x86_64 74 | ;; 75 | +i[3-7]86-*-zigux*) targ_emul=elf_i386 76 | + targ_extra_emuls=elf_x86_64 77 | + ;; 78 | i[3-7]86-*-solaris2*) targ_emul=elf_i386_sol2 79 | targ_extra_emuls="elf_i386_ldso elf_i386 elf_iamcu elf_x86_64_sol2 elf_x86_64" 80 | targ_extra_libpath=$targ_extra_emuls 81 | @@ -1011,6 +1014,9 @@ x86_64-*-linux-*) targ_emul=elf_x86_64 82 | x86_64-*-redox*) targ_emul=elf_x86_64 83 | targ_extra_emuls=elf_i386 84 | ;; 85 | +x86_64-*-zigux*) targ_emul=elf_x86_64 86 | + targ_extra_emuls=elf_i386 87 | + ;; 88 | x86_64-*-solaris2*) targ_emul=elf_x86_64_sol2 89 | targ_extra_emuls="elf_x86_64 elf_i386_sol2 elf_i386_ldso elf_i386 elf_iamcu" 90 | targ_extra_libpath=$targ_extra_emuls 91 | diff --git binutils-clean/libiberty/configure.ac binutils-workdir/libiberty/configure.ac 92 | index 0748c59..954e014 100644 93 | --- binutils-clean/libiberty/configure.ac 94 | +++ binutils-workdir/libiberty/configure.ac 95 | @@ -37,7 +37,7 @@ else 96 | libiberty_topdir="${srcdir}/.." 97 | fi 98 | AC_SUBST(libiberty_topdir) 99 | -AC_CONFIG_AUX_DIR($libiberty_topdir) 100 | +AC_CONFIG_AUX_DIR([.]) 101 | 102 | dnl Very limited version of automake's enable-maintainer-mode 103 | 104 | diff --git binutils-workdir/multilib.am binutils-workdir/multilib.am 105 | new file mode 100644 106 | index 0000000..5c98b69 107 | --- /dev/null 108 | +++ binutils-workdir/multilib.am 109 | @@ -0,0 +1,45 @@ 110 | +## automake - create Makefile.in from Makefile.am 111 | + 112 | +## Copyright (C) 1994-2017 Free Software Foundation, Inc. 113 | +## This Makefile.in is free software; the Free Software Foundation 114 | +## gives unlimited permission to copy and/or distribute it, 115 | +## with or without modifications, as long as this notice is preserved. 116 | + 117 | +## This program is distributed in the hope that it will be useful, 118 | +## but WITHOUT ANY WARRANTY; without even the implied warranty of 119 | +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 120 | +## GNU General Public License for more details. 121 | + 122 | +MULTISRCTOP = 123 | +MULTIBUILDTOP = 124 | +MULTIDIRS = 125 | +MULTISUBDIR = 126 | +MULTIDO = true 127 | +MULTICLEAN = true 128 | + 129 | +# GNU Make needs to see an explicit $(MAKE) variable in the command it 130 | +# runs to enable its job server during parallel builds. Hence the 131 | +# comments below. 132 | +all-multi: 133 | + $(MULTIDO) $(AM_MAKEFLAGS) DO=all multi-do # $(MAKE) 134 | +install-multi: 135 | + $(MULTIDO) $(AM_MAKEFLAGS) DO=install multi-do # $(MAKE) 136 | +mostlyclean-multi: 137 | + $(MULTICLEAN) $(AM_MAKEFLAGS) DO=mostlyclean multi-clean # $(MAKE) 138 | +clean-multi: 139 | + $(MULTICLEAN) $(AM_MAKEFLAGS) DO=clean multi-clean # $(MAKE) 140 | +distclean-multi: 141 | + $(MULTICLEAN) $(AM_MAKEFLAGS) DO=distclean multi-clean # $(MAKE) 142 | +maintainer-clean-multi: 143 | + $(MULTICLEAN) $(AM_MAKEFLAGS) DO=maintainer-clean multi-clean # $(MAKE) 144 | + 145 | +.MAKE .PHONY: all-multi clean-multi distclean-multi install-am \ 146 | + install-multi maintainer-clean-multi mostlyclean-multi 147 | + 148 | +install-exec-local: install-multi 149 | + 150 | +all-local: all-multi 151 | +mostlyclean-local: mostlyclean-multi 152 | +clean-local: clean-multi 153 | +distclean-local: distclean-multi 154 | +maintainer-clean-local: maintainer-clean-multi 155 | -------------------------------------------------------------------------------- /patches/gcc-host/jinx-working-patch.patch: -------------------------------------------------------------------------------- 1 | diff --git gcc-host-clean/fixincludes/mkfixinc.sh gcc-host-workdir/fixincludes/mkfixinc.sh 2 | index df90720..da6408a 100755 3 | --- gcc-host-clean/fixincludes/mkfixinc.sh 4 | +++ gcc-host-workdir/fixincludes/mkfixinc.sh 5 | @@ -12,6 +12,7 @@ target=fixinc.sh 6 | # Check for special fix rules for particular targets 7 | case $machine in 8 | i?86-*-cygwin* | \ 9 | + x86_64-*-zigux* | \ 10 | i?86-*-mingw32* | \ 11 | x86_64-*-mingw32* | \ 12 | powerpc-*-eabisim* | \ 13 | diff --git gcc-host-workdir/gcc/config/zigux.h gcc-host-workdir/gcc/config/zigux.h 14 | new file mode 100644 15 | index 0000000..be79aae 16 | --- /dev/null 17 | +++ gcc-host-workdir/gcc/config/zigux.h 18 | @@ -0,0 +1,29 @@ 19 | +#undef TARGET_ZIGUX 20 | +#define TARGET_ZIGUX 1 21 | + 22 | +#undef LIB_SPEC 23 | +#define LIB_SPEC "-lc" 24 | + 25 | +#undef STARTFILE_SPEC 26 | +#define STARTFILE_SPEC "%{!shared:crt0.o%s} crti.o%s %{shared:crtbeginS.o%s;:crtbegin.o%s}" 27 | + 28 | +#undef ENDFILE_SPEC 29 | +#define ENDFILE_SPEC "%{shared:crtendS.o%s;:crtend.o%s} crtn.o%s" 30 | + 31 | +#define GNU_USER_LINK_EMULATION32 "elf_i386" 32 | +#define GNU_USER_LINK_EMULATION64 "elf_x86_64" 33 | +#define GNU_USER_LINK_EMULATIONX32 "elf32_x86_64" 34 | + 35 | +#define GNU_USER_DYNAMIC_LINKER32 "/usr/lib/ld_i386.so" 36 | +#define GNU_USER_DYNAMIC_LINKER64 "/usr/lib/ld.so" 37 | +#define GNU_USER_DYNAMIC_LINKERX32 "/usr/lib/ld32.so" 38 | + 39 | +#undef TARGET_OS_CPP_BUILTINS 40 | +#define TARGET_OS_CPP_BUILTINS() \ 41 | + do { \ 42 | + builtin_define ("__zigux__"); \ 43 | + builtin_define ("__unix__"); \ 44 | + builtin_assert ("system=zigux"); \ 45 | + builtin_assert ("system=unix"); \ 46 | + builtin_assert ("system=posix"); \ 47 | + } while (0); 48 | diff --git gcc-host-clean/gcc/config.gcc gcc-host-workdir/gcc/config.gcc 49 | index 648b3dc..f2b0217 100644 50 | --- gcc-host-clean/gcc/config.gcc 51 | +++ gcc-host-workdir/gcc/config.gcc 52 | @@ -840,6 +840,15 @@ case ${target} in 53 | tmake_file="${tmake_file} t-freebsd" 54 | target_has_targetdm=yes 55 | ;; 56 | +*-*-zigux*) 57 | + extra_options="$extra_options gnu-user.opt" 58 | + gas=yes 59 | + gnu_ld=yes 60 | + default_use_cxa_atexit=yes 61 | + use_gcc_stdint=wrap 62 | + tmake_file="${tmake_file} t-slibgcc" 63 | + thread_file='posix' 64 | + ;; 65 | *-*-fuchsia*) 66 | native_system_header_dir=/include 67 | ;; 68 | @@ -2214,6 +2223,9 @@ i[34567]86-*-mingw* | x86_64-*-mingw*) 69 | ;; 70 | esac 71 | ;; 72 | +x86_64-*-zigux*) 73 | + tm_file="${tm_file} i386/unix.h i386/att.h elfos.h gnu-user.h glibc-stdint.h i386/x86-64.h i386/gnu-user-common.h i386/gnu-user64.h zigux.h" 74 | + ;; 75 | x86_64-*-fuchsia*) 76 | tmake_file="${tmake_file} i386/t-x86_64-elf" 77 | tm_file="${tm_file} i386/unix.h i386/att.h elfos.h newlib-stdint.h i386/i386elf.h i386/x86-64.h fuchsia.h" 78 | diff --git gcc-host-clean/libgcc/config.host gcc-host-workdir/libgcc/config.host 79 | index 9d72120..b4e90ab 100644 80 | --- gcc-host-clean/libgcc/config.host 81 | +++ gcc-host-workdir/libgcc/config.host 82 | @@ -281,6 +281,11 @@ case ${host} in 83 | tmake_file="$tmake_file t-crtstuff-pic t-libgcc-pic t-eh-dw2-dip t-slibgcc t-slibgcc-fuchsia" 84 | extra_parts="crtbegin.o crtend.o" 85 | ;; 86 | +*-*-zigux*) 87 | + extra_parts="$extra_parts crti.o crtbegin.o crtbeginS.o crtend.o crtendS.o crtn.o" 88 | + tmake_file="$tmake_file t-crtstuff-pic" 89 | + tmake_file="$tmake_file t-slibgcc t-slibgcc-gld t-slibgcc-elf-ver t-libgcc-pic" 90 | + ;; 91 | *-*-linux* | frv-*-*linux* | *-*-kfreebsd*-gnu | *-*-gnu* | *-*-kopensolaris*-gnu | *-*-uclinuxfdpiceabi) 92 | tmake_file="$tmake_file t-crtstuff-pic t-libgcc-pic t-eh-dw2-dip t-slibgcc t-slibgcc-gld t-slibgcc-elf-ver t-linux" 93 | extra_parts="crtbegin.o crtbeginS.o crtbeginT.o crtend.o crtendS.o" 94 | @@ -715,6 +720,10 @@ x86_64-*-elf* | x86_64-*-rtems*) 95 | x86_64-*-fuchsia*) 96 | tmake_file="$tmake_file t-libgcc-pic" 97 | ;; 98 | +x86_64-*-zigux*) 99 | + extra_parts="$extra_parts crtprec32.o crtprec64.o crtprec80.o crtfastmath.o" 100 | + tmake_file="$tmake_file i386/t-crtpc t-crtfm i386/t-crtstuff t-dfprules" 101 | + ;; 102 | i[34567]86-*-dragonfly*) 103 | tmake_file="${tmake_file} i386/t-dragonfly i386/t-crtstuff" 104 | md_unwind_header=i386/dragonfly-unwind.h 105 | diff --git gcc-host-clean/libgcc/configure.ac gcc-host-workdir/libgcc/configure.ac 106 | index 2fc9d5d..83d246e 100644 107 | --- gcc-host-clean/libgcc/configure.ac 108 | +++ gcc-host-workdir/libgcc/configure.ac 109 | @@ -46,7 +46,7 @@ else 110 | libgcc_topdir="${srcdir}/.." 111 | fi 112 | AC_SUBST(libgcc_topdir) 113 | -AC_CONFIG_AUX_DIR($libgcc_topdir) 114 | +AC_CONFIG_AUX_DIR([.]) 115 | AC_CONFIG_HEADER(auto-target.h:config.in) 116 | 117 | AC_ARG_ENABLE(shared, 118 | diff --git gcc-host-clean/libiberty/configure.ac gcc-host-workdir/libiberty/configure.ac 119 | index 28d996f..61ff752 100644 120 | --- gcc-host-clean/libiberty/configure.ac 121 | +++ gcc-host-workdir/libiberty/configure.ac 122 | @@ -37,7 +37,7 @@ else 123 | libiberty_topdir="${srcdir}/.." 124 | fi 125 | AC_SUBST(libiberty_topdir) 126 | -AC_CONFIG_AUX_DIR($libiberty_topdir) 127 | +AC_CONFIG_AUX_DIR([.]) 128 | 129 | dnl Very limited version of automake's enable-maintainer-mode 130 | 131 | diff --git gcc-host-clean/libstdc++-v3/crossconfig.m4 gcc-host-workdir/libstdc++-v3/crossconfig.m4 132 | index b3269cb..a1d4a28 100644 133 | --- gcc-host-clean/libstdc++-v3/crossconfig.m4 134 | +++ gcc-host-workdir/libstdc++-v3/crossconfig.m4 135 | @@ -136,6 +136,18 @@ case "${host}" in 136 | AC_CHECK_FUNCS(uselocale) 137 | ;; 138 | 139 | + *-zigux*) 140 | + GLIBCXX_CHECK_COMPILER_FEATURES 141 | + GLIBCXX_CHECK_LINKER_FEATURES 142 | + GLIBCXX_CHECK_MATH_SUPPORT 143 | + GLIBCXX_CHECK_STDLIB_SUPPORT 144 | + AC_DEFINE(_GLIBCXX_USE_DEV_RANDOM) 145 | + AC_DEFINE(_GLIBCXX_USE_RANDOM_TR1) 146 | + GCC_CHECK_TLS 147 | + AC_CHECK_FUNCS(aligned_alloc posix_memalign memalign _aligned_malloc) 148 | + AC_CHECK_FUNCS(timespec_get) 149 | + ;; 150 | + 151 | *-fuchsia*) 152 | SECTION_FLAGS='-ffunction-sections -fdata-sections' 153 | AC_SUBST(SECTION_FLAGS) 154 | -------------------------------------------------------------------------------- /patches/libtool/jinx-working-patch.patch: -------------------------------------------------------------------------------- 1 | diff --git libtool-clean/build-aux/ltmain.in libtool-workdir/build-aux/ltmain.in 2 | index a5f21a1..c06d55c 100644 3 | --- libtool-clean/build-aux/ltmain.in 4 | +++ libtool-workdir/build-aux/ltmain.in 5 | @@ -6497,7 +6497,7 @@ func_mode_link () 6 | fi 7 | else 8 | # We cannot seem to hardcode it, guess we'll fake it. 9 | - add_dir=-L$libdir 10 | + add_dir=-L$lt_sysroot$libdir 11 | # Try looking first in the location we're being installed to. 12 | if test -n "$inst_prefix_dir"; then 13 | case $libdir in 14 | diff --git libtool-clean/libtoolize.in libtool-workdir/libtoolize.in 15 | index 0c40fed..763619b 100644 16 | --- libtool-clean/libtoolize.in 17 | +++ libtool-workdir/libtoolize.in 18 | @@ -1891,7 +1891,7 @@ func_require_seen_libtool () 19 | # Do not remove config.guess, config.sub or install-sh, we don't 20 | # install them without --install, and the project may not be using 21 | # Automake. Similarly, do not remove Gnulib files. 22 | - all_pkgaux_files="compile depcomp missing ltmain.sh" 23 | + all_pkgaux_files="" 24 | all_pkgmacro_files="libtool.m4 ltargz.m4 ltdl.m4 ltoptions.m4 ltsugar.m4 ltversion.in ltversion.m4 lt~obsolete.m4" 25 | all_pkgltdl_files="COPYING.LIB Makefile Makefile.in Makefile.inc Makefile.am README acinclude.m4 aclocal.m4 argz_.h argz.c config.h.in config-h.in configure configure.ac configure.in libltdl/lt__alloc.h libltdl/lt__argz.h libltdl/lt__dirent.h libltdl/lt__glibc.h libltdl/lt__private.h libltdl/lt__strl.h libltdl/lt_dlloader.h libltdl/lt_error.h libltdl/lt_system.h libltdl/slist.h loaders/dld_link.c loaders/dlopen.c loaders/dyld.c loaders/load_add_on.c loaders/loadlibrary.c loaders/preopen.c loaders/shl_load.c lt__alloc.c lt__argz.c lt__dirent.c lt__strl.c lt_dlloader.c lt_error.c ltdl.c ltdl.h ltdl.mk slist.c" 26 | 27 | diff --git libtool-clean/m4/libtool.m4 libtool-workdir/m4/libtool.m4 28 | index 79a2451..b6e8ca4 100644 29 | --- libtool-clean/m4/libtool.m4 30 | +++ libtool-workdir/m4/libtool.m4 31 | @@ -1696,7 +1696,7 @@ AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl 32 | lt_cv_sys_max_cmd_len=12288; # 12K is about right 33 | ;; 34 | 35 | - gnu*) 36 | + gnu* | zigux*) 37 | # Under GNU Hurd, this test is not required because there is 38 | # no limit to the length of command line arguments. 39 | # Libtool will interpret -1 as no limit whatsoever 40 | @@ -2925,6 +2925,18 @@ netbsd*) 41 | hardcode_into_libs=yes 42 | ;; 43 | 44 | +zigux*) 45 | + version_type=linux # correct to gnu/linux during the next big refactor 46 | + need_lib_prefix=no 47 | + need_version=no 48 | + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' 49 | + soname_spec='$libname$release$shared_ext$major' 50 | + dynamic_linker='mlibc ld.so' 51 | + shlibpath_var=LD_LIBRARY_PATH 52 | + shlibpath_overrides_runpath=no 53 | + hardcode_into_libs=yes 54 | + ;; 55 | + 56 | newsos6) 57 | version_type=linux # correct to gnu/linux during the next big refactor 58 | library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' 59 | @@ -3566,6 +3578,10 @@ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) 60 | lt_cv_deplibs_check_method=pass_all 61 | ;; 62 | 63 | +zigux*) 64 | + lt_cv_deplibs_check_method=pass_all 65 | + ;; 66 | + 67 | netbsd*) 68 | if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then 69 | lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' 70 | @@ -4446,6 +4462,8 @@ m4_if([$1], [CXX], [ 71 | ;; 72 | netbsd*) 73 | ;; 74 | + zigux*) 75 | + ;; 76 | *qnx* | *nto*) 77 | # QNX uses GNU C++, but need to define -shared option too, otherwise 78 | # it will coredump. 79 | @@ -4794,6 +4812,12 @@ m4_if([$1], [CXX], [ 80 | _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' 81 | ;; 82 | 83 | + zigux*) 84 | + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' 85 | + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' 86 | + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' 87 | + ;; 88 | + 89 | *nto* | *qnx*) 90 | # QNX uses GNU C++, but need to define -shared option too, otherwise 91 | # it will coredump. 92 | @@ -5273,6 +5297,11 @@ _LT_EOF 93 | fi 94 | ;; 95 | 96 | + zigux*) 97 | + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' 98 | + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' 99 | + ;; 100 | + 101 | netbsd*) 102 | if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then 103 | _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' 104 | @@ -5815,6 +5844,9 @@ _LT_EOF 105 | esac 106 | ;; 107 | 108 | + zigux*) 109 | + ;; 110 | + 111 | netbsd*) 112 | if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then 113 | _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out 114 | @@ -7115,6 +7147,10 @@ if test yes != "$_lt_caught_CXX_error"; then 115 | esac 116 | ;; 117 | 118 | + zigux*) 119 | + _LT_TAGVAR(ld_shlibs, $1)=yes 120 | + ;; 121 | + 122 | netbsd*) 123 | if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then 124 | _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' 125 | diff --git libtool-clean/m4/ltdl.m4 libtool-workdir/m4/ltdl.m4 126 | index 772c150..642966e 100644 127 | --- libtool-clean/m4/ltdl.m4 128 | +++ libtool-workdir/m4/ltdl.m4 129 | @@ -497,6 +497,9 @@ AC_CACHE_CHECK([whether deplibs are loaded by dlopen], 130 | # at 6.2 and later dlopen does load deplibs. 131 | lt_cv_sys_dlopen_deplibs=yes 132 | ;; 133 | + zigux*) 134 | + lt_cv_sys_dlopen_deplibs=yes 135 | + ;; 136 | netbsd*) 137 | lt_cv_sys_dlopen_deplibs=yes 138 | ;; 139 | -------------------------------------------------------------------------------- /patches/ncurses/jinx-working-patch.patch: -------------------------------------------------------------------------------- 1 | diff --git ncurses-clean/configure ncurses-workdir/configure 2 | index d652dc0..1572ae3 100755 3 | --- ncurses-clean/configure 4 | +++ ncurses-workdir/configure 5 | @@ -6923,6 +6923,10 @@ CF_EOF 6 | LINK_PROGS="$SHELL ${rel_builddir}/mk_prog.sh" 7 | LINK_TESTS="$SHELL ${rel_builddir}/mk_prog.sh" 8 | ;; 9 | + (zigux*) 10 | + CC_SHARED_OPTS='-fPIC' 11 | + MK_SHARED_LIB='${CC} -shared -o $@' 12 | + ;; 13 | (mingw*) 14 | cf_cv_shlib_version=mingw 15 | cf_cv_shlib_version_infix=mingw 16 | -------------------------------------------------------------------------------- /patches/readline/jinx-working-patch.patch: -------------------------------------------------------------------------------- 1 | diff --git readline-clean/support/shlib-install readline-workdir/support/shlib-install 2 | index 661355d..cd7d659 100755 3 | --- readline-clean/support/shlib-install 4 | +++ readline-workdir/support/shlib-install 5 | @@ -71,7 +71,7 @@ fi 6 | # Cygwin installs both a dll (which must go in $BINDIR) and an implicit 7 | # link library (in $libdir) 8 | case "$host_os" in 9 | -hpux*|darwin*|macosx*|linux*|solaris2*) 10 | +hpux*|darwin*|macosx*|linux*|solaris2*|zigux*) 11 | if [ -z "$uninstall" ]; then 12 | chmod 755 ${INSTALLDIR}/${LIBNAME} 13 | fi ;; 14 | @@ -146,7 +146,7 @@ bsdi4*|*gnu*|darwin*|macosx*|netbsd*|mirbsd*) 15 | fi 16 | ;; 17 | 18 | -solaris2*|aix4.[2-9]*|aix[5-9]*|osf*|irix[56]*|sysv[45]*|dgux*|interix*) 19 | +solaris2*|aix4.[2-9]*|aix[5-9]*|osf*|irix[56]*|sysv[45]*|dgux*|interix*|zigux*) 20 | # libname.so -> libname.so.M 21 | ${echo} ${RM} ${INSTALLDIR}/$LINK1 22 | if [ -z "$uninstall" ]; then 23 | -------------------------------------------------------------------------------- /patches/zig/jinx-working-patch.patch: -------------------------------------------------------------------------------- 1 | diff --git zig-clean/lib/std/bounded_array.zig zig-workdir/lib/std/bounded_array.zig 2 | index d418570..80bda17 100644 3 | --- zig-clean/lib/std/bounded_array.zig 4 | +++ zig-workdir/lib/std/bounded_array.zig 5 | @@ -52,17 +52,13 @@ pub fn BoundedArrayAligned( 6 | } 7 | 8 | /// View the internal array as a slice whose size was previously set. 9 | - pub fn slice(self: anytype) switch (@TypeOf(&self.buffer)) { 10 | - *align(alignment) [buffer_capacity]T => []align(alignment) T, 11 | - *align(alignment) const [buffer_capacity]T => []align(alignment) const T, 12 | - else => unreachable, 13 | - } { 14 | + pub fn slice(self: *Self) []align(alignment) T { 15 | return self.buffer[0..self.len]; 16 | } 17 | 18 | /// View the internal array as a constant slice whose size was previously set. 19 | pub fn constSlice(self: *const Self) []align(alignment) const T { 20 | - return self.slice(); 21 | + return self.buffer[0..self.len]; 22 | } 23 | 24 | /// Adjust the slice's length to `len`. 25 | @@ -80,7 +76,7 @@ pub fn BoundedArrayAligned( 26 | } 27 | 28 | /// Return the element at index `i` of the slice. 29 | - pub fn get(self: Self, i: usize) T { 30 | + pub fn get(self: *const Self, i: usize) T { 31 | return self.constSlice()[i]; 32 | } 33 | 34 | @@ -90,12 +86,12 @@ pub fn BoundedArrayAligned( 35 | } 36 | 37 | /// Return the maximum length of a slice. 38 | - pub fn capacity(self: Self) usize { 39 | + pub fn capacity(self: *const Self) usize { 40 | return self.buffer.len; 41 | } 42 | 43 | /// Check that the slice can hold at least `additional_count` items. 44 | - pub fn ensureUnusedCapacity(self: Self, additional_count: usize) error{Overflow}!void { 45 | + pub fn ensureUnusedCapacity(self: *const Self, additional_count: usize) error{Overflow}!void { 46 | if (self.len + additional_count > buffer_capacity) { 47 | return error.Overflow; 48 | } 49 | -------------------------------------------------------------------------------- /recipes/base-files: -------------------------------------------------------------------------------- 1 | name=base-files 2 | source_dir=base-files 3 | revision=1 4 | 5 | package() { 6 | cp -rpv "${source_dir}"/. "${dest_dir}"/ 7 | } 8 | -------------------------------------------------------------------------------- /recipes/bash: -------------------------------------------------------------------------------- 1 | name=bash 2 | version=5.2.21 3 | revision=1 4 | tarball_url="https://ftp.gnu.org/gnu/bash/bash-${version}.tar.gz" 5 | tarball_blake2b="6789c9a0d9eb1ad167d4199bf1438d77934a7bbeae9f9fdd7167cae006b17b3894852440248db1bb6e9cf6d930e8a18b6448a3bb4db8831b2e6d1445b56a2065" 6 | source_hostdeps="autoconf automake libtool pkg-config" 7 | imagedeps="gcc" 8 | hostdeps="gcc autoconf automake libtool pkg-config" 9 | deps="core-libs ncurses readline" 10 | 11 | regenerate() { 12 | AUTOHEADER=true autoreconf -fvi 13 | } 14 | 15 | build() { 16 | autotools_configure \ 17 | --with-curses \ 18 | --enable-readline \ 19 | --without-bash-malloc \ 20 | --with-installed-readline="${sysroot}/usr/lib" 21 | 22 | make -j${parallelism} 23 | } 24 | 25 | package() { 26 | make install DESTDIR="${dest_dir}" 27 | 28 | ln -s bash "${dest_dir}${prefix}/bin/sh" 29 | 30 | post_package_strip 31 | } 32 | -------------------------------------------------------------------------------- /recipes/core-libs: -------------------------------------------------------------------------------- 1 | name=core-libs 2 | revision=1 3 | deps="mlibc libgcc libstdc++ libintl libiconv libxcrypt" 4 | -------------------------------------------------------------------------------- /recipes/coreutils: -------------------------------------------------------------------------------- 1 | name=coreutils 2 | version=9.4 3 | revision=1 4 | tarball_url="https://ftp.gnu.org/gnu/coreutils/coreutils-${version}.tar.xz" 5 | tarball_blake2b="83d41c48804c1d470c0e5eed38e692bb6875436dda3f6e2c29784ad6ef563d86e8e066a050e222621b400f78ea4630b1e127d20fc9b76f12096528c42677e35d" 6 | source_imagedeps="gcc gperf" 7 | source_hostdeps="automake autoconf libtool pkg-config" 8 | imagedeps="gcc gperf" 9 | hostdeps="gcc automake autoconf libtool pkg-config" 10 | deps="core-libs tzdata" 11 | 12 | regenerate() { 13 | autoreconf -fvi 14 | 15 | # Huge hack: coreutils does not compile the build-machine binary make-prime-list 16 | # using the build-machine compiler. Hence, build and invoke the binary manually here. 17 | mkdir tmp_build_dir && cd tmp_build_dir 18 | ../configure 19 | make src/make-prime-list 20 | ./src/make-prime-list 5000 > ../src/primes.h 21 | 22 | cd .. 23 | rm -rf tmp_build_dir 24 | } 25 | 26 | build() { 27 | cp -rp "${source_dir}"/. ./ 28 | 29 | configure_script_path=./configure \ 30 | CFLAGS="-DSLOW_BUT_NO_HACKS $CFLAGS" \ 31 | autotools_configure 32 | 33 | make -j${parallelism} 34 | } 35 | 36 | package() { 37 | DESTDIR="${dest_dir}" make install 38 | 39 | post_package_strip 40 | } 41 | -------------------------------------------------------------------------------- /recipes/cxxshim: -------------------------------------------------------------------------------- 1 | name=cxxshim 2 | version=6f146a41dda736572879fc524cf729eb193dc0a6 3 | revision=1 4 | tarball_url="https://github.com/managarm/cxxshim/archive/${version}.tar.gz" 5 | tarball_blake2b="05d8cbad3d46b4272c7e31b1c8041cc5e640cc66853dddf58af953aeba6697dcbc05decb01dc6d4669fec52acd18b3265706dbf710d11dd98e7c1771ac598a49" 6 | imagedeps="meson ninja" 7 | hostdeps="pkg-config" 8 | 9 | build() { 10 | meson_configure \ 11 | --includedir=share/cxxshim/include \ 12 | -Dinstall_headers=true 13 | 14 | ninja -j${parallelism} 15 | } 16 | 17 | package() { 18 | DESTDIR="${dest_dir}" ninja install 19 | 20 | post_package_strip 21 | } 22 | -------------------------------------------------------------------------------- /recipes/frigg: -------------------------------------------------------------------------------- 1 | name=frigg 2 | version=a24e99eeb3125e7f48f657ff8afca26a9ac4aaae 3 | revision=1 4 | tarball_url="https://github.com/managarm/frigg/archive/${version}.tar.gz" 5 | tarball_blake2b="bb47eb23f4a0d6cc31d8d2345d424d713f4c0f7b02c28a5a17d937023e778961a9a3a553facfdd60ce58d45da2479e6018ecbb9b39f9bf87c30995bb19698666" 6 | imagedeps="gcc meson ninja" 7 | hostdeps="pkg-config" 8 | 9 | build() { 10 | meson_configure \ 11 | --includedir=share/frigg/include \ 12 | --buildtype=debugoptimized \ 13 | -Dbuild_tests=disabled 14 | 15 | ninja -j${parallelism} 16 | } 17 | 18 | package() { 19 | DESTDIR="${dest_dir}" ninja install 20 | 21 | post_package_strip 22 | } 23 | -------------------------------------------------------------------------------- /recipes/init: -------------------------------------------------------------------------------- 1 | name=init 2 | source_dir=init 3 | revision=1 4 | imagedeps="meson ninja" 5 | hostdeps="gcc" 6 | deps="core-libs" 7 | 8 | build() { 9 | meson_configure 10 | 11 | ninja -j${parallelism} 12 | } 13 | 14 | package() { 15 | DESTDIR="${dest_dir}" ninja install 16 | 17 | post_package_strip 18 | } 19 | -------------------------------------------------------------------------------- /recipes/kernel: -------------------------------------------------------------------------------- 1 | name=kernel 2 | source_dir=kernel 3 | revision=1 4 | hostdeps="zig" 5 | deps="mlibc-headers" 6 | allow_network=yes 7 | 8 | package() { 9 | zig build \ 10 | --cache-dir $(realpath .) \ 11 | --global-cache-dir $(realpath .) \ 12 | --prefix "${dest_dir}/${prefix}" \ 13 | --build-file "${source_dir}/build.zig" \ 14 | -freference-trace 15 | } 16 | -------------------------------------------------------------------------------- /recipes/libgcc: -------------------------------------------------------------------------------- 1 | name=libgcc 2 | revision=1 3 | hostdeps="gcc autoconf-2.69 automake libtool pkg-config" 4 | imagedeps="gcc" 5 | deps="mlibc" 6 | 7 | build() { 8 | cd "${base_dir}/host-builds/gcc/build" 9 | make -j${parallelism} all-target-libgcc 10 | } 11 | 12 | package() { 13 | cd "${base_dir}/host-builds/gcc/build" 14 | 15 | rm -rf tmp_libgcc_dir 16 | mkdir tmp_libgcc_dir 17 | 18 | DESTDIR="$(realpath tmp_libgcc_dir)" make install-target-libgcc 19 | 20 | mkdir -p "${dest_dir}${prefix}" 21 | 22 | cp -r tmp_libgcc_dir/usr/local/lib "${dest_dir}${prefix}/" 23 | cp -r "tmp_libgcc_dir/usr/local/${OS_TRIPLET}"/* "${dest_dir}${prefix}/" 24 | 25 | rm "${dest_dir}${prefix}"/lib/gcc/${OS_TRIPLET}/13.2.0/crti.o 26 | rm "${dest_dir}${prefix}"/lib/gcc/${OS_TRIPLET}/13.2.0/crtn.o 27 | 28 | # Copy libgcc into GCC's tree else it will complain. 29 | mkdir -p "${base_dir}/host-pkgs/gcc/usr/local/lib" 30 | cp -r tmp_libgcc_dir/usr/local/lib/* "${base_dir}/host-pkgs/gcc/usr/local/lib/" 31 | 32 | rm "${base_dir}/host-pkgs/gcc/usr/local/lib/gcc/${OS_TRIPLET}/13.2.0/crti.o" 33 | rm "${base_dir}/host-pkgs/gcc/usr/local/lib/gcc/${OS_TRIPLET}/13.2.0/crtn.o" 34 | } 35 | -------------------------------------------------------------------------------- /recipes/libgcc-binaries: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/48cf/zigux/ad2f32b6ef9767af8ced34368b8aa2578f9a14a2/recipes/libgcc-binaries -------------------------------------------------------------------------------- /recipes/libiconv: -------------------------------------------------------------------------------- 1 | name=libiconv 2 | version=1.17 3 | revision=1 4 | tarball_url="https://ftp.gnu.org/gnu/libiconv/libiconv-${version}.tar.gz" 5 | tarball_blake2b="1d317dd0655c680a2082c38561cdff51ac1a9181d4734a8bb1e86861dfd66f1a6c0846a90b5b88f3b38b1fa9983d9e563551f27e95a8e329896b71becceae52b" 6 | source_hostdeps="automake autoconf libtool pkg-config" 7 | imagedeps="binutils" 8 | hostdeps="gcc autoconf automake libtool pkg-config" 9 | deps="mlibc libgcc libstdc++" 10 | 11 | regenerate() { 12 | cp /usr/local/share/aclocal/libtool.m4 ./m4/ 13 | cp /usr/local/share/aclocal/libtool.m4 ./libcharset/m4/ 14 | cp /usr/local/share/libtool/build-aux/ltmain.sh ./build-aux/ 15 | cp /usr/local/share/libtool/build-aux/ltmain.sh ./libcharset/build-aux/ 16 | cp /usr/local/share/aclocal/ltversion.m4 ./m4/ 17 | cp /usr/local/share/aclocal/ltversion.m4 ./libcharset/m4/ 18 | 19 | autotools_recursive_regen -I"${source_dir}/m4" -I"${source_dir}/srcm4" 20 | } 21 | 22 | build() { 23 | autotools_configure 24 | 25 | make -j${parallelism} 26 | } 27 | 28 | package() { 29 | DESTDIR="${dest_dir}" make install 30 | 31 | post_package_strip 32 | } 33 | -------------------------------------------------------------------------------- /recipes/libintl: -------------------------------------------------------------------------------- 1 | name=libintl 2 | version=0.22.3 3 | revision=1 4 | tarball_url="https://ftp.gnu.org/gnu/gettext/gettext-${version}.tar.gz" 5 | tarball_blake2b="aebe85a82cb94c37ed81e9801acf1e89d150f5992fb9be42d53b3f2734c5c95804f0051fabc26b8d0892dfaf89d18df16d4bca6bcb2e9b95eef5d4ae93a64379" 6 | source_hostdeps="autoconf automake libtool pkg-config" 7 | hostdeps="gcc automake autoconf libtool pkg-config" 8 | deps="mlibc libgcc libstdc++ libiconv" 9 | 10 | regenerate() { 11 | ( cd gettext-runtime/libasprintf && autoreconf -fvi ) 12 | ( cd gettext-runtime/intl && autoreconf -fvi ) 13 | ( cd gettext-runtime && autoreconf -fvi ) 14 | ( cd gettext-tools && autoreconf -fvi ) 15 | ( cd libtextstyle && autoreconf -fvi ) 16 | 17 | autoreconf -fvi 18 | } 19 | 20 | build() { 21 | ACLOCAL=true \ 22 | AUTOCONF=true \ 23 | AUTOMAKE=true \ 24 | AUTOHEADER=true \ 25 | autotools_configure \ 26 | --without-emacs \ 27 | --without-lispdir \ 28 | `# Normally this controls nls behavior in general, but the libintl` \ 29 | `# subdir is skipped unless this is explicitly set.` \ 30 | --enable-nls \ 31 | `# This magic flag enables libintl.` \ 32 | --with-included-gettext \ 33 | --disable-c++ \ 34 | --disable-libasprintf \ 35 | --disable-java \ 36 | --enable-threads=posix \ 37 | --disable-curses \ 38 | --without-git \ 39 | --without-cvs \ 40 | --without-bzip2 \ 41 | --without-xz 42 | 43 | sed -i 's/touch $@//g' gettext-runtime/intl/Makefile 44 | 45 | make -C gettext-runtime/intl -j${parallelism} 46 | } 47 | 48 | package() { 49 | DESTDIR="${dest_dir}" make -C gettext-runtime/intl install 50 | 51 | post_package_strip 52 | } 53 | -------------------------------------------------------------------------------- /recipes/libstdc++: -------------------------------------------------------------------------------- 1 | name=libstdc++ 2 | revision=1 3 | hostdeps="gcc autoconf-2.69 automake libtool pkg-config" 4 | imagedeps="gcc" 5 | deps="mlibc libgcc" 6 | 7 | build() { 8 | cd "${base_dir}/host-builds/gcc/build" 9 | make -j${parallelism} all-target-libstdc++-v3 10 | } 11 | 12 | package() { 13 | cd "${base_dir}/host-builds/gcc/build" 14 | 15 | rm -rf tmp_libstdc++_dir 16 | mkdir tmp_libstdc++_dir 17 | 18 | DESTDIR="$(realpath tmp_libstdc++_dir)" make install-target-libstdc++-v3 19 | 20 | # For some reason this also installs libgcc even though it shouldn't... 21 | rm -fv "tmp_libstdc++_dir/usr/local/${OS_TRIPLET}/lib"/libgcc* 22 | 23 | mkdir -p "${dest_dir}${prefix}" 24 | 25 | cp -r tmp_libstdc++_dir/usr/local/share "${dest_dir}${prefix}/" 26 | cp -r "tmp_libstdc++_dir/usr/local/${OS_TRIPLET}"/* "${dest_dir}${prefix}/" 27 | 28 | # Copy libstdc++ and headers into GCC's tree else it will complain. 29 | mkdir -p "${base_dir}/host-pkgs/gcc/usr/local/${OS_TRIPLET}" 30 | cp -r "tmp_libstdc++_dir/usr/local/${OS_TRIPLET}"/* "${base_dir}/host-pkgs/gcc/usr/local/${OS_TRIPLET}/" 31 | } 32 | -------------------------------------------------------------------------------- /recipes/libxcrypt: -------------------------------------------------------------------------------- 1 | name=libxcrypt 2 | version=4.4.36 3 | revision=1 4 | tarball_url="https://github.com/besser82/libxcrypt/releases/download/v${version}/libxcrypt-${version}.tar.xz" 5 | tarball_blake2b="9f028e0fe2cb7bb4273f3f6d1e579e0fe93cd71eba21286aa7dc078c904ea3cdce38b2955bdcd618853f7657b01aea7e28c4d898680e69fdf75f812b5a304c1d" 6 | source_hostdeps="autoconf automake libtool pkg-config" 7 | imagedeps="python-passlib" 8 | hostdeps="gcc automake autoconf libtool pkg-config" 9 | deps="mlibc libgcc libstdc++" 10 | 11 | regenerate() { 12 | autoreconf -fvi 13 | } 14 | 15 | build() { 16 | autotools_configure \ 17 | --enable-obsolete-api=yes \ 18 | --disable-xcrypt-compat-files 19 | 20 | make -j${parallelism} 21 | } 22 | 23 | package() { 24 | DESTDIR="${dest_dir}" make install 25 | 26 | post_package_strip 27 | } 28 | -------------------------------------------------------------------------------- /recipes/linux-headers: -------------------------------------------------------------------------------- 1 | name=linux-headers 2 | version=6.8.5 3 | revision=1 4 | tarball_url="https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-${version}.tar.xz" 5 | tarball_blake2b="795c67356a7992cc73a85a733471d0379156f7ba68eedd68d4aa099eb07b4eea4626239ae15cdcc409819c833beb1ec2dc4032b203363db52ab1cb8bc31ac4ea" 6 | imagedeps="gcc rsync" 7 | 8 | build() { 9 | cp -rp "${source_dir}"/. ./ 10 | 11 | make ARCH=x86_64 headers_install 12 | find usr/include -type f ! -name *.h -delete 13 | 14 | # remove this file, as mlibc will override this file with one suited to mlibc 15 | rm usr/include/linux/libc-compat.h 16 | } 17 | 18 | package() { 19 | mkdir -pv "${dest_dir}${prefix}" 20 | cp -rv usr/include "${dest_dir}${prefix}"/ 21 | } 22 | -------------------------------------------------------------------------------- /recipes/mlibc: -------------------------------------------------------------------------------- 1 | name=mlibc 2 | version=b5627685013748f0faff3c16ebc2569f958f3cb5 3 | revision=1 4 | tarball_url="https://github.com/mintsuki/mlibc/archive/${version}.tar.gz" 5 | tarball_blake2b="c98c20c03647f0f5828bc95d82217ece8e5d0eee1bbb293dc809d1b39bd8219064d68ab8499c5f7e26c7377a8829cccdeeca1974bdd574cd3d85166e56c7622d" 6 | imagedeps="meson ninja" 7 | hostdeps="gcc pkg-config libgcc-binaries" 8 | builddeps="cxxshim frigg" 9 | deps="mlibc-headers linux-headers" 10 | 11 | build() { 12 | LDFLAGS="-Wl,/usr/local/libgcc-binaries/libgcc-x86_64.a" \ 13 | meson_configure \ 14 | --buildtype=debugoptimized \ 15 | -Dmlibc_no_headers=true \ 16 | -Ddefault_library=both \ 17 | -Ddisable_crypt_option=true \ 18 | -Ddisable_iconv_option=true \ 19 | -Ddisable_intl_option=true \ 20 | -Ddisable_libgcc_dependency=true \ 21 | -Dlinux_kernel_headers="${sysroot_dir}${prefix}/include" 22 | 23 | ninja -j${parallelism} 24 | } 25 | 26 | package() { 27 | DESTDIR="${dest_dir}" ninja install 28 | } 29 | -------------------------------------------------------------------------------- /recipes/mlibc-headers: -------------------------------------------------------------------------------- 1 | name=mlibc-headers 2 | revision=1 3 | from_source=mlibc 4 | imagedeps="meson ninja" 5 | hostdeps="pkg-config" 6 | builddeps="cxxshim frigg linux-headers" 7 | 8 | build() { 9 | meson_configure \ 10 | -Dheaders_only=true \ 11 | -Ddisable_crypt_option=true \ 12 | -Ddisable_iconv_option=true \ 13 | -Ddisable_intl_option=true \ 14 | -Dlinux_kernel_headers="${sysroot_dir}${prefix}/include" 15 | 16 | ninja -j${parallelism} 17 | } 18 | 19 | package() { 20 | DESTDIR="${dest_dir}" ninja install 21 | } 22 | -------------------------------------------------------------------------------- /recipes/nano: -------------------------------------------------------------------------------- 1 | name=nano 2 | version=7.2 3 | revision=1 4 | tarball_url="https://ftp.gnu.org/gnu/nano/nano-${version}.tar.xz" 5 | tarball_blake2b="c7e3b18383e9f2f9db1f6059c875ddd164d730ea0e5b363e66fb8e5f30e8598ba49a5afd8eea3a55e295f1e43fb136019f60cc9154ae276c5d589002c0e5298a" 6 | source_hostdeps="automake autoconf libtool pkg-config" 7 | hostdeps="gcc automake autoconf libtool pkg-config" 8 | deps="core-libs ncurses" 9 | 10 | regenerate() { 11 | autoreconf -fvi 12 | } 13 | 14 | build() { 15 | autotools_configure 16 | 17 | make -j${parallelism} 18 | } 19 | 20 | package() { 21 | DESTDIR="${dest_dir}" make install 22 | 23 | post_package_strip 24 | } 25 | -------------------------------------------------------------------------------- /recipes/ncurses: -------------------------------------------------------------------------------- 1 | name=ncurses 2 | version=6.4.20231111 3 | revision=1 4 | tarball_url="https://github.com/ThomasDickey/ncurses-snapshots/archive/refs/tags/v6_4_20231111.tar.gz" 5 | tarball_blake2b="0d7b490b50e58281250cc4ebdac8f35cbb3fbf0e13578524003ae4c26c10507d59fb8dd2a4d67102067df77d857c41e6c37c509d9a7cee8661dd3bb80f7cbfef" 6 | source_hostdeps="autoconf automake libtool pkg-config" 7 | imagedeps="gcc ncurses patchelf" 8 | hostdeps="gcc automake autoconf libtool pkg-config" 9 | deps="core-libs" 10 | 11 | regenerate() { 12 | cp -pv /usr/local/share/libtool/build-aux/config.guess ./ 13 | cp -pv /usr/local/share/libtool/build-aux/config.sub ./ 14 | } 15 | 16 | build() { 17 | cf_cv_func_nanosleep=yes \ 18 | autotools_configure \ 19 | --enable-widec \ 20 | --enable-pc-files \ 21 | --with-shared \ 22 | --with-cxx-shared \ 23 | --without-normal \ 24 | --without-debug \ 25 | --with-manpage-format=normal \ 26 | --with-pkg-config-libdir=/usr/lib/pkgconfig \ 27 | --with-termlib 28 | 29 | make -j${parallelism} 30 | } 31 | 32 | package() { 33 | make DESTDIR="${dest_dir}" install 34 | 35 | # As we build ncurses with wide character support, make some compatibility links 36 | for lib in ncurses ncurses++ form panel menu tinfo ; do 37 | rm -vf "${dest_dir}${prefix}/lib/lib${lib}.so" 38 | echo "INPUT(-l${lib}w)" >"${dest_dir}${prefix}/lib/lib${lib}.so" 39 | ln -sfv "${lib}w.pc" "${dest_dir}${prefix}/lib/pkgconfig/${lib}.pc" 40 | patchelf --set-soname "lib${lib}w.so" "${dest_dir}${prefix}/lib/lib${lib}w.so" 41 | done 42 | 43 | rm -vf "${dest_dir}${prefix}/lib/libcursesw.so" 44 | echo "INPUT(-lncursesw)" >"${dest_dir}${prefix}/lib/libcursesw.so" 45 | ln -sfv libncurses.so "${dest_dir}${prefix}/lib/libcurses.so" 46 | 47 | # Remove static libraries 48 | rm -rf "${dest_dir}${prefix}/lib"/*.a 49 | 50 | post_package_strip 51 | } 52 | -------------------------------------------------------------------------------- /recipes/readline: -------------------------------------------------------------------------------- 1 | name=readline 2 | version=8.2 3 | revision=1 4 | tarball_url="https://ftp.gnu.org/gnu/readline/readline-${version}.tar.gz" 5 | tarball_blake2b="7974322b9c092a756a79e537df08e8532f8e0fcb598f77732e28287c33ebec9e9837ed88b43334c310892d56a871b423903f0f564def2fbe700a1004f2ae7b18" 6 | source_hostdeps="automake autoconf libtool pkg-config" 7 | imagedeps="patchelf" 8 | hostdeps="gcc autoconf automake libtool pkg-config" 9 | deps="core-libs ncurses" 10 | 11 | regenerate() { 12 | AUTOHEADER=true autoreconf -fvi 13 | } 14 | 15 | build() { 16 | autotools_configure \ 17 | --enable-multibyte \ 18 | --with-curses 19 | 20 | make SHLIB_LIBS="-lncursesw" -j${parallelism} 21 | } 22 | 23 | package() { 24 | make DESTDIR="${dest_dir}" SHLIB_LIBS="-lncursesw" install 25 | 26 | # libraries are created without soname... fix that 27 | for lib in libhistory.so.8 libreadline.so.8; do 28 | patchelf --set-soname "$lib" "${dest_dir}${prefix}/lib/$lib" 29 | done 30 | 31 | post_package_strip 32 | } 33 | -------------------------------------------------------------------------------- /recipes/tzdata: -------------------------------------------------------------------------------- 1 | name=tzdata 2 | version=2023c 3 | revision=1 4 | tarball_url="https://data.iana.org/time-zones/releases/tzdata${version}.tar.gz" 5 | tarball_blake2b="8a50aa5f338565d86b8fa5428c138b251bd8dcc3ea66c144b49625d02c5c7aa27f1ace66babd36f10f75cf5eb832ec327b9c2e8adb0384c450130d1ee8c45562" 6 | imagedeps="tzdata" 7 | hostdeps="gcc binutils" 8 | deps="core-libs" 9 | 10 | build() { 11 | cp -r "${source_dir}"/. ./ 12 | } 13 | 14 | package() { 15 | # Create the required directories 16 | mkdir -p "${dest_dir}/etc" 17 | mkdir -p "${dest_dir}/usr/share/zoneinfo/posix" 18 | mkdir -p "${dest_dir}/usr/share/zoneinfo/right" 19 | 20 | # Create the time zone files without leap seconds, convention puts these in both zoneinfo and zoneinfo/posix. 21 | # After that. create time time zone files with leap seconds 22 | zic -L /dev/null -d "${dest_dir}${prefix}/share/zoneinfo" etcetera 23 | zic -L /dev/null -d "${dest_dir}${prefix}/share/zoneinfo/posix" etcetera 24 | zic -L "${source_dir}/leapseconds" -d "${dest_dir}${prefix}/share/zoneinfo/right" etcetera 25 | zic -L /dev/null -d "${dest_dir}${prefix}/share/zoneinfo" southamerica 26 | zic -L /dev/null -d "${dest_dir}${prefix}/share/zoneinfo/posix" southamerica 27 | zic -L "${source_dir}/leapseconds" -d "${dest_dir}${prefix}/share/zoneinfo/right" southamerica 28 | zic -L /dev/null -d "${dest_dir}${prefix}/share/zoneinfo" northamerica 29 | zic -L /dev/null -d "${dest_dir}${prefix}/share/zoneinfo/posix" northamerica 30 | zic -L "${source_dir}/leapseconds" -d "${dest_dir}${prefix}/share/zoneinfo/right" northamerica 31 | zic -L /dev/null -d "${dest_dir}${prefix}/share/zoneinfo" europe 32 | zic -L /dev/null -d "${dest_dir}${prefix}/share/zoneinfo/posix" europe 33 | zic -L "${source_dir}/leapseconds" -d "${dest_dir}${prefix}/share/zoneinfo/right" europe 34 | zic -L /dev/null -d "${dest_dir}${prefix}/share/zoneinfo" africa 35 | zic -L /dev/null -d "${dest_dir}${prefix}/share/zoneinfo/posix" africa 36 | zic -L "${source_dir}/leapseconds" -d "${dest_dir}${prefix}/share/zoneinfo/right" africa 37 | zic -L /dev/null -d "${dest_dir}${prefix}/share/zoneinfo" antarctica 38 | zic -L /dev/null -d "${dest_dir}${prefix}/share/zoneinfo/posix" antarctica 39 | zic -L "${source_dir}/leapseconds" -d "${dest_dir}${prefix}/share/zoneinfo/right" antarctica 40 | zic -L /dev/null -d "${dest_dir}${prefix}/share/zoneinfo" asia 41 | zic -L /dev/null -d "${dest_dir}${prefix}/share/zoneinfo/posix" asia 42 | zic -L "${source_dir}/leapseconds" -d "${dest_dir}${prefix}/share/zoneinfo/right" asia 43 | zic -L /dev/null -d "${dest_dir}${prefix}/share/zoneinfo" australasia 44 | zic -L /dev/null -d "${dest_dir}${prefix}/share/zoneinfo/posix" australasia 45 | zic -L "${source_dir}/leapseconds" -d "${dest_dir}${prefix}/share/zoneinfo/right" australasia 46 | zic -L /dev/null -d "${dest_dir}${prefix}/share/zoneinfo" backward 47 | zic -L /dev/null -d "${dest_dir}${prefix}/share/zoneinfo/posix" backward 48 | zic -L "${source_dir}/leapseconds" -d "${dest_dir}${prefix}/share/zoneinfo/right" backward 49 | 50 | # Create the posixrules file, POSIX requires daylight saving rules to be in accordance with US rules, thus use New York 51 | zic -d "${dest_dir}/usr/share/zoneinfo" -p America/New_York 52 | 53 | # Default to UTC for localtime, this should be fixed, but that is pending xbstrap support. 54 | ln -sf /usr/share/zoneinfo/UTC "${dest_dir}/etc/localtime" 55 | 56 | post_package_strip 57 | } 58 | -------------------------------------------------------------------------------- /source-recipes/autoconf: -------------------------------------------------------------------------------- 1 | name=autoconf 2 | version=2.72 3 | tarball_url="https://ftp.gnu.org/gnu/autoconf/autoconf-${version}.tar.gz" 4 | tarball_blake2b="48fff54704176cbf2642230229c628b75c43ef3f810c39eea40cae91dd02e1203d04a544407de96f9172419a94b952865909d969d9e9b6c10879a9d9aeea5ad0" 5 | -------------------------------------------------------------------------------- /source-recipes/autoconf-2.69: -------------------------------------------------------------------------------- 1 | name=autoconf-2.69 2 | version=2.69 3 | tarball_url="https://ftp.gnu.org/gnu/autoconf/autoconf-${version}.tar.gz" 4 | tarball_blake2b="7e8a513bbfcabadad1577919c048cc05ca0a084788850b42570f88afc2fa9c25fb32277412f135b81ba1c0d8079465a6b581d2d78662c991d2183b739fac407c" 5 | -------------------------------------------------------------------------------- /source-recipes/automake: -------------------------------------------------------------------------------- 1 | name=automake 2 | version=1.16.5 3 | tarball_url="https://ftp.gnu.org/gnu/automake/automake-${version}.tar.gz" 4 | tarball_blake2b="5ccdcbe2d3deb2b0baed4a8590b07714cd7098fbda251afebe83232ed03f4db84abbe023cf0544622dbc5137254347273247428eb5420564a167b86de95d113e" 5 | -------------------------------------------------------------------------------- /source-recipes/binutils: -------------------------------------------------------------------------------- 1 | name=binutils 2 | version=2.41 3 | tarball_url="https://ftp.gnu.org/gnu/binutils/binutils-${version}.tar.xz" 4 | tarball_blake2b="3bccec2b52f7e82a727121bf2a2e51a6249ba63dcd74c665fd834e858645c912ffd8245d848435288b938852830b482905606f55c40df4061215fd75c52ffc75" 5 | hostdeps="autoconf-2.69 automake libtool pkg-config" 6 | 7 | regenerate() { 8 | autotools_recursive_regen -I"$(realpath ./config)" 9 | 10 | cp -pv /usr/local/share/libtool/build-aux/{config.sub,config.guess,install-sh} libiberty/ 11 | } 12 | -------------------------------------------------------------------------------- /source-recipes/gcc-host: -------------------------------------------------------------------------------- 1 | name=gcc-host 2 | version=13.2.0 3 | tarball_url="https://ftp.gnu.org/gnu/gcc/gcc-${version}/gcc-${version}.tar.xz" 4 | tarball_blake2b="0034b29d3d6cc05821f0c4253ce077805943aff7b370729dd203bda57d89c107edd657eeddc2fb1e69ea15c7b0323b961f46516c7f4af89a3ccf7fea84701be2" 5 | hostdeps="automake autoconf-2.69 libtool pkg-config" 6 | imagedeps="git" 7 | allow_network="yes" 8 | 9 | regenerate() { 10 | ./contrib/download_prerequisites 11 | 12 | autotools_recursive_regen -I"$(realpath ./config)" 13 | 14 | cp -pv /usr/local/share/libtool/build-aux/{config.sub,config.guess,install-sh} libiberty/ 15 | cp -pv /usr/local/share/libtool/build-aux/{config.sub,config.guess,install-sh} libgcc/ 16 | } 17 | -------------------------------------------------------------------------------- /source-recipes/gnulib: -------------------------------------------------------------------------------- 1 | name=gnulib 2 | version=4f6545e79c4a7cd7feb2c8f23f1d5167e7165907 3 | tarball_url="https://git.savannah.gnu.org/cgit/gnulib.git/snapshot/gnulib-${version}.tar.gz" 4 | tarball_blake2b="0206d3bdeb25bdc3562065dc1832d3ea2ac95920b84ed0461626efba83de3edfcdcbc257c493a9a3d6035d0edb09526248c2483f898110be774ac31caf650e58" 5 | -------------------------------------------------------------------------------- /source-recipes/libgcc-binaries: -------------------------------------------------------------------------------- 1 | name=libgcc-binaries 2 | version=1e4b24ef15a7d9a2db7570d1c9283fc474dcbd73 3 | tarball_url="https://github.com/mintsuki/libgcc-binaries/archive/${version}.tar.gz" 4 | tarball_blake2b="77b8af0466577ca5af9b16d968865d24d42fb422566de2f03dd5b2d984f70015da6b1bd28878855889ee665f0ace4419cee3c40d20ac1176b0c500a1e50302bd" 5 | -------------------------------------------------------------------------------- /source-recipes/libtool: -------------------------------------------------------------------------------- 1 | name=libtool 2 | version=2.4.7 3 | tarball_url="https://ftp.gnu.org/gnu/libtool/libtool-${version}.tar.gz" 4 | tarball_blake2b="3b7c66050237931443008d6be9c0c30f4938402bf68576cdf02f2248b216bb68c6b797bbfdb8a92caa5e12cb10208cd19771cdcb6b0d83572ad60bfc03e67e98" 5 | hostdeps="autoconf automake gnulib" 6 | imagedeps="help2man" 7 | 8 | regenerate() { 9 | cp -rp "${base_dir}/sources/gnulib" ./ 10 | 11 | ./bootstrap \ 12 | --force \ 13 | --skip-git \ 14 | --skip-po \ 15 | --gnulib-srcdir="$(pwd)/gnulib" 16 | } 17 | -------------------------------------------------------------------------------- /source-recipes/limine: -------------------------------------------------------------------------------- 1 | name=limine 2 | version=7.5.1 3 | tarball_url="https://github.com/limine-bootloader/limine/releases/download/v${version}/limine-${version}.tar.gz" 4 | tarball_blake2b="3d6fd20a94b517a384b3bc977a6a28811b38b4db0747c25843a8841b19896c63e2468fd17ebbeb37869423bfc533cd23cd5e2cff50d3fce54355812f4c4d99ae" 5 | hostdeps="autoconf automake libtool pkg-config" 6 | 7 | regenerate() { 8 | autoreconf -fvi 9 | } 10 | -------------------------------------------------------------------------------- /source-recipes/pkg-config: -------------------------------------------------------------------------------- 1 | name=pkg-config 2 | version=2.1.0 3 | tarball_url="https://github.com/pkgconf/pkgconf/archive/refs/tags/pkgconf-${version}.tar.gz" 4 | tarball_blake2b="ab0f03494c37659c18a882ff03e6bb746c3cfe0ad66b7a9d9d0b2de66bec89258b2374addd3eb3a571442d8bab3c1311410e296379b697d16aaebe2bc89b318c" 5 | hostdeps="autoconf automake libtool" 6 | 7 | regenerate() { 8 | autoreconf -fvi 9 | } 10 | -------------------------------------------------------------------------------- /source-recipes/zig: -------------------------------------------------------------------------------- 1 | name=zig 2 | version=0.12.0-dev.3644+05d975576 3 | tarball_url="https://ziglang.org/builds/zig-linux-x86_64-${version}.tar.xz" 4 | tarball_blake2b="7aba141e4638ea03d9949ddc28b2149af9a18226910ace17ed1645bb7764e4582e6f2a7dfd70d0fbc76d0f202373fff8f8aec280dea2547688d357d15dd48d47" 5 | --------------------------------------------------------------------------------