├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── doc └── TODO.md ├── kernel ├── Makefile ├── build.zig ├── linker-x86_64.ld └── src │ ├── acpi.zig │ ├── fs │ └── tmpfs.zig │ ├── main.zig │ ├── pmm.zig │ ├── process.zig │ ├── sched.zig │ ├── smp.zig │ ├── util │ └── libc.zig │ ├── vfs.zig │ ├── vmm.zig │ └── x86_64 │ ├── arch.zig │ ├── cpu.zig │ ├── lapic.zig │ ├── paging.zig │ └── trap.zig ├── limine.cfg └── user ├── Makefile ├── bootstrap.yml ├── patches ├── automake-v1.16 │ └── 0001-update-config-sub.patch ├── binutils │ └── 0001-implement-support-for-munix.patch ├── gcc │ ├── 0001-create-munix-target.patch │ └── 0002-fixes-for-musl.patch └── oksh │ └── 0001-changes-for-munix.patch └── util-munix ├── Makefile └── init.c /.gitignore: -------------------------------------------------------------------------------- 1 | limine 2 | limine-zig 3 | ovmf-x64 4 | zig-cache 5 | zig-out 6 | user/build 7 | user/bundled 8 | user/ports 9 | user/initrd.img 10 | *.xbstrap 11 | *.hdd 12 | *.iso 13 | 14 | # util-munix stuff 15 | user/util-munix/init 16 | user/util-munix/util-munix 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .POSIX: 2 | .PHONY: all all-hdd run run-uefi run-hdd run-hdd-uefi 3 | 4 | QEMU_COMMON_FLAGS = -cpu max --enable-kvm -M q35 -m 2G -smp 2 5 | 6 | all: munix.iso 7 | 8 | all-hdd: munix.hdd 9 | 10 | run: munix.iso 11 | qemu-system-x86_64 $(QEMU_COMMON_FLAGS) -cdrom munix.iso 12 | 13 | run-uefi: ovmf-x64 munix.iso 14 | qemu-system-x86_64 $(QEMU_COMMON_FLAGS) -bios ovmf-x64/OVMF.fd -cdrom munix.iso 15 | 16 | run-hdd: munix.hdd 17 | qemu-system-x86_64 $(QEMU_COMMON_FLAGS) -hda munix.hdd 18 | 19 | run-hdd-uefi: ovmf-x64 munix.hdd 20 | qemu-system-x86_64 $(QEMU_COMMON_FLAGS) -bios ovmf-x64/OVMF.fd -hda munix.hdd 21 | 22 | ovmf-x64: 23 | mkdir -p ovmf-x64 24 | cd ovmf-x64 && curl -o OVMF-X64.zip https://efi.akeo.ie/OVMF/OVMF-X64.zip && unzip OVMF-X64.zip 25 | 26 | limine: 27 | git clone https://github.com/limine-bootloader/limine.git --branch=v4.x-branch-binary --depth=1 28 | $(MAKE) -C limine 29 | 30 | .PHONY: kernel clean distclean 31 | kernel: 32 | $(MAKE) -C kernel 33 | 34 | munix.iso: limine kernel 35 | rm -rf iso_root 36 | mkdir -p iso_root 37 | cp kernel/zig-out/bin/kernel \ 38 | limine.cfg limine/limine.sys limine/limine-cd.bin limine/limine-cd-efi.bin iso_root/ 39 | [ -f user/initrd.img ] && cp user/initrd.img iso_root/ 40 | xorriso -as mkisofs -b limine-cd.bin \ 41 | -no-emul-boot -boot-load-size 4 -boot-info-table \ 42 | --efi-boot limine-cd-efi.bin \ 43 | -efi-boot-part --efi-boot-image --protective-msdos-label \ 44 | iso_root -o munix.iso 45 | limine/limine-deploy munix.iso 46 | rm -rf iso_root 47 | 48 | munix.hdd: limine kernel 49 | rm -f munix.hdd 50 | dd if=/dev/zero bs=1M count=0 seek=64 of=munix.hdd 51 | parted -s munix.hdd mklabel gpt 52 | parted -s munix.hdd mkpart ESP fat32 2048s 100% 53 | parted -s munix.hdd set 1 esp on 54 | limine/limine-deploy munix.hdd 55 | sudo losetup -Pf --show munix.hdd >loopback_dev 56 | sudo mkfs.fat -F 32 `cat loopback_dev`p1 57 | mkdir -p img_mount 58 | sudo mount `cat loopback_dev`p1 img_mount 59 | sudo mkdir -p img_mount/EFI/BOOT 60 | sudo cp -v kernel/zig-out/bin/kernel limine.cfg limine/limine.sys img_mount/ 61 | sudo cp -v limine/BOOTX64.EFI img_mount/EFI/BOOT/ 62 | [ -f user/initrd.img ] && sudo cp user/initrd.img img_mount/ 63 | sync 64 | sudo umount img_mount 65 | sudo losetup -d `cat loopback_dev` 66 | sudo rm -rf loopback_dev img_mount 67 | 68 | clean: 69 | rm -rf iso_root munix.iso munix.hdd 70 | $(MAKE) -C kernel clean 71 | 72 | distclean: clean 73 | rm -rf limine ovmf-x64 74 | $(MAKE) -C kernel distclean 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Munix 2 | 3 | Munix is my (new) attempt at writing a proper unix clone, without straying away for a month (or two) 4 | to add completly unnecessary and complicated features. 5 | 6 | ## Building 7 | 8 | To build/run the kernel and to generate images, run the following... 9 | 10 | ```bash 11 | $ # Kernel + ISO 12 | $ make && make run 13 | $ # Kernel + HDD 14 | $ make all-hdd && make run-hdd 15 | $ # Kernel + HDD + UEFI 16 | $ make all-hdd && make run-hdd-uefi 17 | ``` 18 | 19 | **NOTE: The latest stage 2/3 Zig compiler is required to build Munix! (instructions [here](https://github.com/ziglang/zig/wiki/Building-Zig-From-Source))** 20 | -------------------------------------------------------------------------------- /doc/TODO.md: -------------------------------------------------------------------------------- 1 | ## Munix TODO list 2 | 3 | A list of things that need to get done within munix, organized 4 | by priority (from highest to lowest)... 5 | 6 | #### Bugs 7 | 8 | - [ ] Check that the TSC deadline timer works (on real hw) 9 | 10 | #### Issues 11 | 12 | - [ ] Make error handling more graceful 13 | - [ ] Rewrite `README.md` (make it much more prettier and clear) 14 | - [ ] Clean up imports and dependencies across files 15 | - [x] Redo the kernel initialization scheme (and fix the following issue as well) 16 | - [x] Have initialization functions return errors, than collect them in `main.entry()` 17 | - [ ] In-kernel stacktracing (using `std.dwarf`), since it helps on real hw 18 | 19 | #### Nice to have 20 | 21 | - [ ] PCI stack 22 | - [ ] A timer layer (maybe ditch the ACPI Timer for the HPET??) 23 | - [ ] Intel PT (Processor Trace) Support (can be used for precise examination of callgraph) 24 | 25 | Once again, contributions are deeply apprieciated and welcomed! 26 | 27 | -------------------------------------------------------------------------------- /kernel/Makefile: -------------------------------------------------------------------------------- 1 | .POSIX: 2 | 3 | INTERNALZIGFLAGS := \ 4 | -Drelease-safe=true 5 | 6 | .PHONY: all 7 | all: kernel 8 | 9 | .PHONY: kernel 10 | kernel: limine-zig 11 | zig build $(INTERNALZIGFLAGS) $(ZIGFLAGS) 12 | 13 | limine-zig: 14 | git clone https://github.com/limine-bootloader/limine-zig.git --depth=1 15 | 16 | .PHONY: clean 17 | clean: 18 | rm -rf zig-cache zig-out 19 | 20 | .PHONY: distclean 21 | distclean: clean 22 | rm -rf limine-zig 23 | -------------------------------------------------------------------------------- /kernel/build.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn build(b: *std.build.Builder) !void { 4 | // Define a freestanding x86_64 cross-compilation target. 5 | var target: std.zig.CrossTarget = .{ 6 | .cpu_arch = .x86_64, 7 | .os_tag = .freestanding, 8 | .abi = .none, 9 | }; 10 | 11 | // Disable CPU features that require additional initialization 12 | // like MMX, SSE/2 and AVX. That requires us to enable the soft-float feature. 13 | const Features = std.Target.x86.Feature; 14 | target.cpu_features_sub.addFeature(@enumToInt(Features.mmx)); 15 | target.cpu_features_sub.addFeature(@enumToInt(Features.sse)); 16 | target.cpu_features_sub.addFeature(@enumToInt(Features.sse2)); 17 | target.cpu_features_sub.addFeature(@enumToInt(Features.avx)); 18 | target.cpu_features_sub.addFeature(@enumToInt(Features.avx2)); 19 | target.cpu_features_add.addFeature(@enumToInt(Features.soft_float)); 20 | 21 | // Build the kernel itself. 22 | const mode = b.standardReleaseOptions(); 23 | const kernel = b.addExecutable("kernel", "src/main.zig"); 24 | kernel.code_model = .kernel; 25 | kernel.setBuildMode(mode); 26 | kernel.addPackagePath("limine", "limine-zig/limine.zig"); 27 | kernel.addIncludePath("../user/build/system-root/usr/include"); 28 | kernel.setLinkerScriptPath(.{ .path = "linker-x86_64.ld" }); 29 | kernel.setTarget(target); 30 | kernel.install(); 31 | } 32 | -------------------------------------------------------------------------------- /kernel/linker-x86_64.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 kernel_entry to be our entry point */ 6 | ENTRY(entry) 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 0xffffffff80200000 is chosen as */ 22 | /* that is the beginning of the region. */ 23 | . = 0xffffffff80200000; 24 | 25 | .text : { 26 | *(.text .text.*) 27 | } :text 28 | 29 | /* Move to the next memory page for .rodata */ 30 | . += CONSTANT(MAXPAGESIZE); 31 | 32 | .rodata : { 33 | *(.rodata .rodata.*) 34 | } :rodata 35 | 36 | /* Move to the next memory page for .data */ 37 | . += CONSTANT(MAXPAGESIZE); 38 | 39 | .data : { 40 | *(.data .data.*) 41 | } :data 42 | 43 | .bss : { 44 | *(COMMON) 45 | *(.bss .bss.*) 46 | } :data 47 | 48 | /* Discard notes since they may cause issues on some hosts. */ 49 | /DISCARD/ : { 50 | *(.note .note.*) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /kernel/src/acpi.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const limine = @import("limine"); 3 | const vmm = @import("root").vmm; 4 | const sink = std.log.scoped(.acpi); 5 | 6 | pub const GenericAddress = extern struct { 7 | base_type: u8 align(1), 8 | bit_width: u8 align(1), 9 | bit_offset: u8 align(1), 10 | access_size: u8 align(1), 11 | base: u64 align(1), 12 | 13 | fn read(self: GenericAddress, comptime T: type) T { 14 | if (self.base_type == 0) { // MMIO 15 | return @intToPtr(*align(1) volatile T, self.base).*; 16 | } else { 17 | return switch (T) { 18 | u8 => asm volatile ("inb %[port], %[result]" 19 | : [result] "={al}" (-> T), 20 | : [port] "N{dx}" (@truncate(u16, self.base)), 21 | ), 22 | u16 => asm volatile ("inw %[port], %[result]" 23 | : [result] "={ax}" (-> T), 24 | : [port] "N{dx}" (@truncate(u16, self.base)), 25 | ), 26 | u32 => asm volatile ("inl %[port], %[result]" 27 | : [result] "={eax}" (-> T), 28 | : [port] "N{dx}" (@truncate(u16, self.base)), 29 | ), 30 | else => @compileError("unsupported type for PIO read ->" ++ @typeName(T)), 31 | }; 32 | } 33 | } 34 | }; 35 | 36 | pub const Header = extern struct { 37 | signature: [4]u8 align(1), 38 | length: u32 align(1), 39 | revision: u8 align(1), 40 | checksum: u8 align(1), 41 | oem: [6]u8 align(1), 42 | oem_table: [8]u8 align(1), 43 | oem_revision: u32 align(1), 44 | creator_id: u32 align(1), 45 | creator_revision: u32 align(1), 46 | 47 | fn getContents(self: *Header) []const u8 { 48 | return @ptrCast([*]const u8, self)[0..self.length][@sizeOf(Header)..]; 49 | } 50 | }; 51 | 52 | pub const XSDP = extern struct { 53 | signature: [8]u8, 54 | checksum: u8, 55 | oem: [6]u8, 56 | revision: u8, 57 | rsdt: u32, 58 | length: u32, 59 | xsdt: u64, 60 | ext_checksum: u8, 61 | }; 62 | 63 | pub const FADT = extern struct { 64 | firmware_control: u32 align(1), 65 | dsdt: u32 align(1), 66 | reserved: u8 align(1), 67 | profile: u8 align(1), 68 | sci_irq: u16 align(1), 69 | smi_command_port: u32 align(1), 70 | acpi_enable: u8 align(1), 71 | acpi_disable: u8 align(1), 72 | s4bios_req: u8 align(1), 73 | pstate_control: u8 align(1), 74 | pm1a_event_blk: u32 align(1), 75 | pm1b_event_blk: u32 align(1), 76 | pm1a_control_blk: u32 align(1), 77 | pm1b_control_blk: u32 align(1), 78 | pm2_control_blk: u32 align(1), 79 | pm_timer_blk: u32 align(1), 80 | gpe0_blk: u32 align(1), 81 | gpe1_blk: u32 align(1), 82 | pm1_event_length: u8 align(1), 83 | pm1_control_length: u8 align(1), 84 | pm2_control_length: u8 align(1), 85 | pm_timer_length: u8 align(1), 86 | gpe0_length: u8 align(1), 87 | gpe1_length: u8 align(1), 88 | gpe1_base: u8 align(1), 89 | cstate_control: u8 align(1), 90 | worst_c2_latency: u16 align(1), 91 | worst_c3_latency: u16 align(1), 92 | flush_size: u16 align(1), 93 | flush_stride: u16 align(1), 94 | duty_offset: u8 align(1), 95 | duty_width: u8 align(1), 96 | day_alarm: u8 align(1), 97 | month_alarm: u8 align(1), 98 | century: u8 align(1), 99 | iapc_boot_flags: u16 align(1), 100 | reserved2: u8 align(1), 101 | flags: u32 align(1), 102 | reset_register: GenericAddress align(1), 103 | reset_command: u8 align(1), 104 | arm_boot_flags: u16 align(1), 105 | minor_version: u8 align(1), 106 | x_firmware_control: u64 align(1), 107 | x_dsdt: u64 align(1), 108 | x_pm1a_event_blk: GenericAddress align(1), 109 | x_pm1b_event_blk: GenericAddress align(1), 110 | x_pm1a_control_blk: GenericAddress align(1), 111 | x_pm1b_control_blk: GenericAddress align(1), 112 | x_pm2_control_blk: GenericAddress align(1), 113 | x_pm_timer_blk: GenericAddress align(1), 114 | x_gpe0_blk: GenericAddress align(1), 115 | x_gpe1_blk: GenericAddress align(1), 116 | }; 117 | 118 | pub export var rsdp_request: limine.RsdpRequest = .{}; 119 | var timer_block: GenericAddress = undefined; 120 | var timer_bits: usize = 0; 121 | var xsdt: ?*Header = null; 122 | var rsdt: ?*Header = null; 123 | 124 | fn getEntries(comptime T: type, header: *Header) []align(1) const T { 125 | return std.mem.bytesAsSlice(T, header.getContents()); 126 | } 127 | 128 | fn printTable(sdt: *Header) void { 129 | // real hw systems are packed with SSDT tables (upwards of 14) 130 | // beacuse of this, skip printing SSDTs so the kernel logs 131 | // aren't cluttered 132 | if (std.mem.eql(u8, "SSDT", &sdt.signature)) { 133 | return; 134 | } 135 | 136 | // zig fmt: off 137 | sink.info("\t* [{s}]: 0x{X:0>16}, Length: {d:0>3}, Revision: {}", .{ 138 | sdt.signature, @ptrToInt(sdt), sdt.length, sdt.revision 139 | }); 140 | // zig fmt: on 141 | } 142 | 143 | pub fn getTable(signature: []const u8) ?*Header { 144 | if (xsdt) |x| { 145 | for (getEntries(u64, x)) |ent| { 146 | var entry = @intToPtr(*Header, vmm.toHigherHalf(ent)); 147 | if (std.mem.eql(u8, signature[0..4], &entry.signature)) { 148 | return entry; 149 | } 150 | } 151 | } else { 152 | for (getEntries(u32, rsdt.?)) |ent| { 153 | var entry = @intToPtr(*Header, vmm.toHigherHalf(ent)); 154 | if (std.mem.eql(u8, signature[0..4], &entry.signature)) { 155 | return entry; 156 | } 157 | } 158 | } 159 | 160 | return null; 161 | } 162 | 163 | pub fn pmSleep(us: u64) void { 164 | var shift: u64 = @as(u64, 1) << @truncate(u6, timer_bits); 165 | var target = (us * 3) + ((us * 5) / 10) + ((us * 8) / 100); 166 | 167 | // find out how many 'remaining' ticks to wait after 'n' overflows 168 | var n: u64 = target / shift; 169 | var remaining: u64 = target % shift; 170 | 171 | // bump 'remaining' to reflect current timer state 172 | var cur_ticks = timer_block.read(u32); 173 | remaining += cur_ticks; 174 | 175 | // adjust 'n' to reflect current timer state 176 | if (remaining < cur_ticks) { 177 | n += 1; 178 | } else { 179 | n += remaining / shift; 180 | remaining = remaining % shift; 181 | } 182 | 183 | // next, wait for 'n' overflows to happen 184 | var new_ticks: u32 = 0; 185 | while (n > 0) { 186 | new_ticks = timer_block.read(u32); 187 | if (new_ticks < cur_ticks) { 188 | n -= 1; 189 | } 190 | cur_ticks = new_ticks; 191 | } 192 | 193 | // finally, wait the 'remaining' ticks out 194 | while (remaining > cur_ticks) { 195 | new_ticks = timer_block.read(u32); 196 | if (new_ticks < cur_ticks) { 197 | break; 198 | } 199 | cur_ticks = new_ticks; 200 | } 201 | } 202 | 203 | pub fn init() !void { 204 | var resp = rsdp_request.response orelse return error.MissingBootInfo; 205 | 206 | // TODO(cleanbaja): find a way to fragment 207 | // pages, so that we can map acpi tables, 208 | // without modifying other pages 209 | var xsdp = @intToPtr(*align(1) const XSDP, @ptrToInt(resp.address)); 210 | 211 | if (xsdp.revision >= 2 and xsdp.xsdt != 0) { 212 | xsdt = @intToPtr(*Header, vmm.toHigherHalf(xsdp.xsdt)); 213 | } else { 214 | rsdt = @intToPtr(*Header, vmm.toHigherHalf(@intCast(usize, xsdp.rsdt))); 215 | } 216 | 217 | if (xsdt) |x| { 218 | var num_tables = (x.length - @sizeOf(Header)) / 8; 219 | sink.info("dumping {} tables...", .{num_tables}); 220 | 221 | for (getEntries(u64, x)) |ent| { 222 | var entry = @intToPtr(*Header, vmm.toHigherHalf(ent)); 223 | printTable(entry); 224 | } 225 | } else { 226 | var num_tables = (rsdt.?.length - @sizeOf(Header)) / 4; 227 | sink.info("dumping {} tables...", .{num_tables}); 228 | 229 | for (getEntries(u32, rsdt.?)) |ent| { 230 | var entry = @intToPtr(*Header, vmm.toHigherHalf(ent)); 231 | printTable(entry); 232 | } 233 | } 234 | 235 | // setup the ACPI timer 236 | if (getTable("FACP")) |fadt_sdt| { 237 | var fadt = @ptrCast(*align(1) const FADT, fadt_sdt.getContents()[0..]); 238 | 239 | if (xsdp.revision >= 2 and fadt.x_pm_timer_blk.base_type == 0) { 240 | timer_block = fadt.x_pm_timer_blk; 241 | timer_block.base = vmm.toHigherHalf(timer_block.base); 242 | } else { 243 | if (fadt.pm_timer_blk == 0 or fadt.pm_timer_length != 4) { 244 | @panic("ACPI Timer is unsupported/malformed"); 245 | } 246 | 247 | timer_block = GenericAddress{ 248 | .base = fadt.pm_timer_blk, 249 | .base_type = 1, 250 | .bit_width = 32, 251 | .bit_offset = 0, 252 | .access_size = 0, 253 | }; 254 | } 255 | 256 | if ((fadt.flags & (1 << 8)) == 0) { 257 | timer_bits = 32; 258 | } else { 259 | timer_bits = 24; 260 | } 261 | 262 | if (timer_block.base_type == 0) { 263 | sink.info("detected MMIO acpi timer, with {}-bit counter width", .{timer_bits}); 264 | } else { 265 | sink.info("detected PIO (Port IO) acpi timer, with {}-bit counter width", .{timer_bits}); 266 | } 267 | } else { 268 | return error.InvalidHardware; 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /kernel/src/fs/tmpfs.zig: -------------------------------------------------------------------------------- 1 | const vfs = @import("root").vfs; 2 | const pmm = @import("root").pmm; 3 | const vmm = @import("root").vmm; 4 | const std = @import("std"); 5 | 6 | const allocator = @import("root").allocator; 7 | const types = @import("../util/libc.zig"); 8 | 9 | // export so we can use for initramfs 10 | pub const TmpfsContext = struct { 11 | device: u64 = 0, 12 | inode_counter: u64 = 0, 13 | }; 14 | 15 | // same goes here 16 | pub const TmpfsInode = struct { 17 | base: []u8, 18 | bytes: usize, 19 | }; 20 | 21 | fn tmpfs_read(node: *vfs.VfsNode, buffer: [*]u8, offset: u64, length: usize) vfs.VfsError!usize { 22 | var inode = @ptrCast(*align(1) TmpfsInode, node.inode); 23 | var len = length; 24 | 25 | node.lock.acq(); 26 | defer node.lock.rel(); 27 | 28 | if ((offset + length) > inode.bytes) { 29 | if (offset > inode.bytes) { 30 | return error.InvalidParams; 31 | } else { 32 | len = inode.bytes - offset; 33 | } 34 | } 35 | 36 | std.mem.copy(u8, buffer[0..len], inode.base[offset .. offset + len]); 37 | return len; 38 | } 39 | 40 | fn tmpfs_write(node: *vfs.VfsNode, buffer: [*]const u8, offset: u64, length: usize) vfs.VfsError!usize { 41 | var inode = @ptrCast(*align(1) TmpfsInode, node.inode); 42 | 43 | node.lock.acq(); 44 | defer node.lock.rel(); 45 | 46 | if ((offset + length) > inode.bytes) { 47 | while ((offset + length) > inode.bytes) : (inode.bytes *= 2) {} 48 | 49 | node.stat.st_size = @intCast(i64, offset + length); 50 | inode.base = try allocator().realloc(inode.base, inode.bytes); 51 | } 52 | 53 | std.mem.copy(u8, inode.base[offset .. offset + length], buffer[0..length]); 54 | node.stat.st_size += @intCast(i64, length); 55 | return length; 56 | } 57 | 58 | fn tmpfs_close(node: *vfs.VfsNode) void { 59 | _ = node; 60 | } 61 | 62 | fn tmpfs_create(parent: *vfs.VfsNode, name: []const u8, stat: types.Stat) vfs.VfsError!*vfs.VfsNode { 63 | var context = @ptrCast(*align(1) TmpfsContext, parent.fs.context); 64 | var st = stat; 65 | 66 | var inode = try allocator().create(TmpfsInode); 67 | inode.bytes = 0; 68 | errdefer allocator().destroy(inode); 69 | 70 | context.inode_counter += 1; 71 | st.st_dev = context.device; 72 | st.st_ino = context.inode_counter; 73 | st.st_blksize = 4096; 74 | st.st_nlink = 1; 75 | 76 | var result = try vfs.createNode(parent, name, st, parent.fs, parent.vtable, true); 77 | result.inode = inode; 78 | return result; 79 | } 80 | 81 | pub const vtable: vfs.VTable = .{ 82 | .read = &tmpfs_read, 83 | .write = &tmpfs_write, 84 | .close = &tmpfs_close, 85 | }; 86 | 87 | pub const fs_vtable: vfs.FsVTable = .{ 88 | .create = &tmpfs_create, 89 | }; 90 | -------------------------------------------------------------------------------- /kernel/src/main.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const limine = @import("limine"); 3 | const logger = std.log.scoped(.main); 4 | 5 | // modules 6 | pub const arch = @import("x86_64/arch.zig"); 7 | pub const acpi = @import("acpi.zig"); 8 | pub const pmm = @import("pmm.zig"); 9 | pub const vmm = @import("vmm.zig"); 10 | pub const smp = @import("smp.zig"); 11 | pub const vfs = @import("vfs.zig"); 12 | pub const proc = @import("process.zig"); 13 | pub const sched = @import("sched.zig"); 14 | 15 | var g_alloc = std.heap.GeneralPurposeAllocator(.{ .thread_safe = true, .MutexType = smp.SpinLock }){}; 16 | pub export var terminal_request: limine.TerminalRequest = .{}; 17 | 18 | pub inline fn allocator() std.mem.Allocator { 19 | return g_alloc.allocator(); 20 | } 21 | 22 | pub const os = .{ 23 | .heap = .{ 24 | .page_allocator = std.mem.Allocator{ 25 | .ptr = &pmm.page_allocator, 26 | .vtable = &std.mem.Allocator.VTable{ 27 | .alloc = pmm.PageAllocator.alloc, 28 | .resize = pmm.PageAllocator.resize, 29 | .free = pmm.PageAllocator.free, 30 | }, 31 | }, 32 | }, 33 | }; 34 | 35 | var log_buffer: [16 * 4096]u8 = undefined; 36 | var log_lock = smp.SpinLock{}; 37 | var limine_terminal_cr3: u64 = 0; 38 | 39 | pub fn log( 40 | comptime level: std.log.Level, 41 | comptime scope: anytype, 42 | comptime fmt: []const u8, 43 | args: anytype, 44 | ) void { 45 | log_lock.acq(); 46 | defer log_lock.rel(); 47 | 48 | var buffer = std.io.fixedBufferStream(&log_buffer); 49 | var writer = buffer.writer(); 50 | 51 | if (scope != .default) { 52 | switch (level) { 53 | .warn => { 54 | writer.print("{s}: (\x1b[33mwarn\x1b[0m) ", .{@tagName(scope)}) catch unreachable; 55 | }, 56 | .err => { 57 | writer.print("{s}: (\x1b[31merr\x1b[0m) ", .{@tagName(scope)}) catch unreachable; 58 | }, 59 | else => { 60 | writer.print("{s}: ", .{@tagName(scope)}) catch unreachable; 61 | }, 62 | } 63 | } 64 | 65 | writer.print(fmt ++ "\n", args) catch unreachable; 66 | 67 | var old_pagetable: u64 = arch.paging.saveSpace(); 68 | defer arch.paging.loadSpace(old_pagetable); 69 | arch.paging.loadSpace(limine_terminal_cr3); 70 | 71 | if (terminal_request.response) |resp| { 72 | resp.write(null, buffer.getWritten()); 73 | } 74 | } 75 | 76 | pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, return_addr: ?usize) noreturn { 77 | _ = stack_trace; 78 | _ = return_addr; 79 | 80 | std.log.err("\n<-------------- \x1b[31mKERNEL PANIC\x1b[0m -------------->", .{}); 81 | std.log.err("The munix kernel panicked with the following message...", .{}); 82 | std.log.err(" \"{s}\"", .{message}); 83 | std.log.err("Stacktrace: ", .{}); 84 | 85 | var stack_iter = std.debug.StackIterator.init(@returnAddress(), @frameAddress()); 86 | while (stack_iter.next()) |addr| { 87 | std.log.err(" > 0x{X:0>16} (??:0)", .{addr}); 88 | } 89 | 90 | while (true) { 91 | asm volatile ("hlt"); 92 | } 93 | } 94 | 95 | fn stage2(arg: u64) noreturn { 96 | vfs.init(); 97 | 98 | _ = proc.createProcess(null, "/usr/bin/init", &vfs.root) catch |e| { 99 | logger.err("launching /usr/bin/init failed! (error={any})", .{e}); 100 | while (true) {} 101 | }; 102 | _ = arg; 103 | 104 | logger.warn("init complete, end of kernel reached!", .{}); 105 | sched.exit(); 106 | } 107 | 108 | export fn entry() callconv(.C) noreturn { 109 | limine_terminal_cr3 = arch.paging.saveSpace(); 110 | logger.info("hello from munix!", .{}); 111 | 112 | kernel_main() catch |e| { 113 | logger.err("init failed with error: {any}", .{e}); 114 | }; 115 | 116 | while (true) {} 117 | } 118 | 119 | fn kernel_main() !void { 120 | // setup the essentials 121 | arch.setupCpu(); 122 | try pmm.init(); 123 | try vmm.init(); 124 | try acpi.init(); 125 | try proc.init(); 126 | 127 | // boot all other cores, and setup the scheduler 128 | arch.trap.setHandler(sched.reschedule, sched.TIMER_VECTOR); 129 | _ = try sched.spawnKernelThread(stage2, null); 130 | smp.init(); 131 | 132 | // enter the scheduler, and continue init in stage2 133 | try sched.enable(); 134 | } 135 | -------------------------------------------------------------------------------- /kernel/src/pmm.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const limine = @import("limine"); 3 | const smp = @import("root").smp; 4 | const vmm = @import("root").vmm; 5 | const sink = std.log.scoped(.pmm); 6 | const PAGE_SIZE = std.mem.page_size; 7 | 8 | pub const Bitmap = struct { 9 | bits: [*]u8, 10 | size: usize, 11 | last_free: usize = 0, 12 | 13 | fn check(self: *Bitmap, bit: usize) bool { 14 | return self.bits[bit / 8] & @as(u8, 1) << @intCast(u3, bit % 8) != 0; 15 | } 16 | 17 | pub fn mark(self: *Bitmap, bit: usize) void { 18 | self.bits[bit / 8] |= @as(u8, 1) << @intCast(u3, bit % 8); 19 | } 20 | 21 | fn markRange(self: *Bitmap, start: usize, length: usize) void { 22 | var i: usize = start; 23 | 24 | while (i < (start + length)) : (i += 1) { 25 | self.mark(i); 26 | } 27 | } 28 | 29 | fn clear(self: *Bitmap, bit: usize) void { 30 | self.bits[bit / 8] &= ~(@as(u8, 1) << @intCast(u3, bit % 8)); 31 | } 32 | 33 | fn clearRange(self: *Bitmap, start: usize, length: usize) void { 34 | var i: usize = start; 35 | 36 | while (i < (start + length)) : (i += 1) { 37 | self.clear(i); 38 | } 39 | } 40 | 41 | pub fn findFreeRange(self: *Bitmap, count: usize, step_size: usize) ?u64 { 42 | var i: usize = std.mem.alignBackward(self.last_free, step_size); 43 | 44 | while (i < self.size * 8) : (i += step_size) { 45 | if (!self.check(i)) { 46 | var found = find_pages: { 47 | var j: usize = 1; 48 | while (j < count) : (j += 1) { 49 | if (self.check(i + j)) { 50 | break :find_pages false; 51 | } 52 | } 53 | break :find_pages true; 54 | }; 55 | 56 | if (found) { 57 | self.last_free = i + count; 58 | return i; 59 | } 60 | } 61 | } 62 | 63 | if (self.last_free != 0) { 64 | self.last_free = 0; 65 | return self.findFreeRange(count, step_size); 66 | } else { 67 | return null; 68 | } 69 | } 70 | }; 71 | 72 | pub const PageAllocator = struct { 73 | base: u64 = 0xFFFF_EA00_0000_0000, 74 | 75 | pub fn alloc(ctx: *anyopaque, len: usize, ptr_align: u8, ret_addr: usize) ?[*]u8 { 76 | _ = ptr_align; 77 | _ = ret_addr; 78 | 79 | const pages = std.mem.alignForward(len, PAGE_SIZE); 80 | const self = @ptrCast(*PageAllocator, @alignCast(8, ctx)); 81 | const old_base = self.base; 82 | 83 | var i: usize = 0; 84 | var map_flags = vmm.MapFlags{ .write = true }; 85 | 86 | while (i < pages) : (i += 1) { 87 | const page = allocPages(1) orelse return null; 88 | 89 | vmm.kernel_pagemap.mapPage( 90 | map_flags, 91 | old_base + i * PAGE_SIZE, 92 | page, 93 | false, 94 | ); 95 | } 96 | 97 | self.base += pages * PAGE_SIZE; 98 | 99 | return @ptrCast([*]u8, @intToPtr(*u8, old_base)); 100 | } 101 | 102 | pub fn resize(ctx: *anyopaque, buf: []u8, buf_align: u8, new_len: usize, ret_addr: usize) bool { 103 | _ = ctx; 104 | _ = buf; 105 | _ = buf_align; 106 | _ = new_len; 107 | _ = ret_addr; 108 | 109 | return false; 110 | } 111 | 112 | pub fn free(ctx: *anyopaque, buf: []u8, buf_align: u8, ret_addr: usize) void { 113 | _ = ctx; 114 | _ = buf; 115 | _ = buf_align; 116 | _ = ret_addr; 117 | } 118 | }; 119 | 120 | pub export var memmap_request: limine.MemoryMapRequest = .{}; 121 | pub var page_allocator = PageAllocator{}; 122 | var global_bitmap: Bitmap = undefined; 123 | var pmm_lock = smp.SpinLock{}; 124 | 125 | fn getKindName(kind: anytype) []const u8 { 126 | return switch (kind) { 127 | .usable => "usable", 128 | .reserved => "reserved", 129 | .acpi_reclaimable => "reclaimable (acpi)", 130 | .acpi_nvs => "acpi nvs", 131 | .bad_memory => "bad memory", 132 | .bootloader_reclaimable => "reclaimable (bootloader)", 133 | .kernel_and_modules => "kernel and modules", 134 | .framebuffer => "framebuffer", 135 | }; 136 | } 137 | 138 | pub fn init() !void { 139 | var highest_addr: u64 = 0; 140 | var resp = memmap_request.response orelse return error.MissingBootInfo; 141 | 142 | sink.info("dumping memory map entries...", .{}); 143 | 144 | // find highest addr (and dump memory map) 145 | for (resp.entries()) |ent| { 146 | sink.info( 147 | "\tBase: {X:0>16}, Length: {X:0>8}, Type: {s}", 148 | .{ ent.base, ent.length, getKindName(ent.kind) }, 149 | ); 150 | highest_addr = std.math.max(highest_addr, ent.base + ent.length); 151 | } 152 | 153 | // find the size of the bitmap 154 | var n_bits = highest_addr / PAGE_SIZE; 155 | var n_bytes = std.mem.alignForward((n_bits / 8), PAGE_SIZE); 156 | 157 | // find a entry that can hold the bitmap 158 | for (resp.entries()) |ent| { 159 | if (ent.length > n_bytes and ent.kind == .usable) { 160 | ent.base += n_bytes; 161 | ent.length -= n_bytes; 162 | 163 | global_bitmap.bits = @intToPtr([*]u8, vmm.toHigherHalf(ent.base)); 164 | global_bitmap.size = n_bytes; 165 | } 166 | } 167 | 168 | // clear the bitmap to all 0xFFs (reserved) 169 | @memset(global_bitmap.bits, 0xFF, n_bytes); 170 | 171 | // mark usable ranges in the global_bitmap 172 | for (resp.entries()) |ent| { 173 | if (ent.kind == .usable) { 174 | global_bitmap.clearRange(ent.base / PAGE_SIZE, ent.length / PAGE_SIZE); 175 | } 176 | } 177 | 178 | // finally, mark the bitmap itself as used 179 | global_bitmap.markRange(vmm.fromHigherHalf(@ptrToInt(global_bitmap.bits)) / PAGE_SIZE, n_bytes / PAGE_SIZE); 180 | } 181 | 182 | pub fn allocPages(count: usize) ?u64 { 183 | pmm_lock.acq(); 184 | defer pmm_lock.rel(); 185 | 186 | return result: { 187 | if (global_bitmap.findFreeRange(count, 1)) |free_bit| { 188 | @memset(@intToPtr([*]u8, vmm.toHigherHalf(free_bit * PAGE_SIZE)), 0, 0x1000 * count); 189 | break :result free_bit * PAGE_SIZE; 190 | } else { 191 | break :result null; 192 | } 193 | }; 194 | } 195 | 196 | pub fn allocHugePages(count: usize) ?u64 { 197 | pmm_lock.acq(); 198 | defer pmm_lock.rel(); 199 | 200 | return result: { 201 | if (global_bitmap.findFreeRange(count * 0x200, 0x200)) |free_bit| { 202 | @memset(@intToPtr([*]u8, vmm.toHigherHalf(free_bit * PAGE_SIZE)), 0, 0x200000 * count); 203 | break :result free_bit * PAGE_SIZE; 204 | } else { 205 | break :result null; 206 | } 207 | }; 208 | } 209 | 210 | pub fn freePages(ptr: usize, count: usize) void { 211 | pmm_lock.acq(); 212 | defer pmm_lock.rel(); 213 | 214 | global_bitmap.clearRange(ptr / PAGE_SIZE, count); 215 | } 216 | -------------------------------------------------------------------------------- /kernel/src/process.zig: -------------------------------------------------------------------------------- 1 | const sched = @import("root").sched; 2 | const paging = @import("root").arch.paging; 3 | const vmm = @import("root").vmm; 4 | const pmm = @import("root").pmm; 5 | const vfs = @import("root").vfs; 6 | const std = @import("std"); 7 | 8 | const allocator = @import("root").allocator; 9 | const sink = std.log.scoped(.proc); 10 | const MAX_PID_COUNT = 8192; 11 | 12 | pub const Process = struct { 13 | parent: ?*Process, 14 | pid: u32 = 0, 15 | pagemap: *paging.PageMap, 16 | cwd: *vfs.VfsNode, 17 | threads: std.ArrayList(*sched.Thread), 18 | children: std.ArrayList(*Process), 19 | tid_counter: u32 = 0, 20 | }; 21 | 22 | pub var kernel_process: Process = undefined; 23 | var process_table: std.AutoHashMap(u32, *Process) = undefined; 24 | var pid_bitmap: pmm.Bitmap = undefined; 25 | 26 | const ElfImage = struct { 27 | dyld_path: ?[]u8 = null, 28 | phdr: u64 = 0, 29 | entry: u64 = 0, 30 | phnum: usize = 0, 31 | }; 32 | 33 | fn loadImage(proc: *Process, image_path: []const u8, base: u64) !ElfImage { 34 | var image = try vfs.resolve(proc.cwd, image_path); 35 | var image_file = vfs.VStream.init(image); 36 | 37 | var elf_header = try std.elf.Header.read(&image_file); 38 | var phdrs = elf_header.program_header_iterator(&image_file); 39 | var result: ElfImage = .{}; 40 | 41 | while (try phdrs.next()) |p| { 42 | switch (p.p_type) { 43 | std.elf.PT_INTERP => { 44 | var linker_path = try allocator().alloc(u8, p.p_filesz); 45 | const termed_str = @ptrCast([*:0]u8, linker_path); 46 | _ = try image.vtable.read(image, @ptrCast([*]u8, linker_path), p.p_offset, p.p_filesz); 47 | 48 | result.dyld_path = termed_str[0..std.mem.len(termed_str)]; 49 | }, 50 | std.elf.PT_PHDR => result.phdr = p.p_vaddr + base, 51 | std.elf.PT_LOAD => { 52 | var misalign = p.p_vaddr & (std.mem.page_size - 1); 53 | var map_flags: vmm.MapFlags = .{ .read = true, .user = true }; 54 | 55 | if (p.p_flags & std.elf.PF_W != 0) 56 | map_flags.write = true; 57 | 58 | if (p.p_flags & std.elf.PF_X != 0) 59 | map_flags.exec = true; 60 | 61 | const n_pages = std.mem.alignForward(p.p_memsz + misalign, std.mem.page_size) / std.mem.page_size; 62 | const pbase = pmm.allocPages(n_pages) orelse return error.OutOfMemory; 63 | const vbase = std.mem.alignBackward(p.p_vaddr, std.mem.page_size) + base; 64 | 65 | var i: u64 = 0; 66 | while (i < n_pages) : (i += 1) { 67 | proc.pagemap.mapPage( 68 | map_flags, 69 | vbase + (i * std.mem.page_size), 70 | pbase + (i * std.mem.page_size), 71 | false, 72 | ); 73 | } 74 | 75 | const mem = @intToPtr([*]u8, vmm.toHigherHalf(pbase + misalign)); 76 | _ = try image.vtable.read(image, mem, p.p_offset, p.p_filesz); 77 | }, 78 | else => continue, 79 | } 80 | } 81 | 82 | result.entry = elf_header.entry + base; 83 | result.phnum = elf_header.phnum; 84 | return result; 85 | } 86 | 87 | pub fn createProcess(parent: ?*Process, exe_path: []const u8, starting_dir: *vfs.VfsNode) !*Process { 88 | var process = try allocator().create(Process); 89 | var pagemap = try vmm.createPagemap(); 90 | 91 | errdefer allocator().destroy(process); 92 | errdefer allocator().destroy(pagemap); 93 | 94 | process.* = .{ 95 | .parent = parent, 96 | .pagemap = pagemap, 97 | .cwd = starting_dir, 98 | .threads = std.ArrayList(*sched.Thread).init(allocator()), 99 | .children = std.ArrayList(*Process).init(allocator()), 100 | }; 101 | 102 | if (pid_bitmap.findFreeRange(1, 1)) |pid| { 103 | process.pid = @truncate(u32, pid); 104 | try process_table.put(@truncate(u32, pid), process); 105 | if (parent) |p| { 106 | try p.children.append(process); 107 | } 108 | } else { 109 | return error.OutOfPIDs; 110 | } 111 | 112 | var file = try loadImage(process, exe_path, 0); 113 | if (file.dyld_path != null) { 114 | var ld_file = try loadImage(process, file.dyld_path.?, 0x4000_0000); 115 | _ = ld_file; 116 | } 117 | 118 | return process; 119 | } 120 | 121 | pub fn init() !void { 122 | process_table = std.AutoHashMap(u32, *Process).init(allocator()); 123 | kernel_process = .{ 124 | .parent = null, 125 | .pagemap = &vmm.kernel_pagemap, 126 | .cwd = &vfs.root, 127 | .threads = std.ArrayList(*sched.Thread).init(allocator()), 128 | .children = std.ArrayList(*Process).init(allocator()), 129 | }; 130 | 131 | var pid_bitmap_mem = try allocator().alloc(u8, MAX_PID_COUNT / 8); 132 | pid_bitmap = .{ 133 | .bits = @ptrCast([*]u8, pid_bitmap_mem), 134 | .size = MAX_PID_COUNT, 135 | }; 136 | 137 | pid_bitmap.mark(0); 138 | } 139 | -------------------------------------------------------------------------------- /kernel/src/sched.zig: -------------------------------------------------------------------------------- 1 | const trap = @import("root").arch.trap; 2 | const arch = @import("root").arch; 3 | const proc = @import("root").proc; 4 | const smp = @import("root").smp; 5 | const pmm = @import("root").pmm; 6 | const vmm = @import("root").vmm; 7 | const std = @import("std"); 8 | 9 | const allocator = @import("root").allocator; 10 | 11 | pub const Thread = struct { 12 | link: Node, 13 | context: trap.TrapFrame, 14 | kernel_stack: u64, 15 | id: usize = 0, 16 | proc: *proc.Process, 17 | }; 18 | 19 | pub const Node = struct { 20 | next: ?*Node = undefined, 21 | }; 22 | 23 | pub fn Queue(comptime T: type, comptime member_name: []const u8) type { 24 | return struct { 25 | head: ?*Node = null, 26 | tail: ?*Node = null, 27 | lock: smp.SpinLock = .{}, 28 | 29 | fn refToNode(ref: *T) *Node { 30 | return &@field(ref, member_name); 31 | } 32 | 33 | fn nodeToRef(node: *Node) *T { 34 | return @fieldParentPtr(T, member_name, node); 35 | } 36 | 37 | pub fn enqueue(self: *@This(), node: *T) void { 38 | self.lock.acq(); 39 | defer self.lock.rel(); 40 | 41 | const hook = refToNode(node); 42 | hook.next = null; 43 | 44 | if (self.tail) |tail_nonnull| { 45 | tail_nonnull.next = hook; 46 | self.tail = hook; 47 | } else { 48 | @import("std").debug.assert(self.head == null); 49 | self.head = hook; 50 | self.tail = hook; 51 | } 52 | } 53 | 54 | pub fn dequeue(self: *@This()) ?*T { 55 | self.lock.acq(); 56 | defer self.lock.rel(); 57 | 58 | if (self.head) |head_nonnull| { 59 | if (head_nonnull.next) |next| { 60 | self.head = next; 61 | } else { 62 | self.head = null; 63 | self.tail = null; 64 | } 65 | return nodeToRef(head_nonnull); 66 | } 67 | return null; 68 | } 69 | }; 70 | } 71 | 72 | pub const TIMER_VECTOR = 0x30; 73 | var thread_list = Queue(Thread, "link"){}; 74 | var sched_lock = smp.SpinLock{}; 75 | 76 | pub fn exit() noreturn { 77 | smp.getCoreInfo().cur_thread = null; 78 | 79 | arch.ic.oneshot(TIMER_VECTOR, 1); 80 | while (true) {} 81 | } 82 | 83 | pub fn createKernelStack() ?u64 { 84 | if (pmm.allocPages(4)) |page| { 85 | return vmm.toHigherHalf(page + 4 * std.mem.page_size); 86 | } else { 87 | return null; 88 | } 89 | } 90 | 91 | fn getNextThread() *Thread { 92 | sched_lock.acq(); 93 | defer sched_lock.rel(); 94 | 95 | if (thread_list.dequeue()) |elem| { 96 | return elem; 97 | } else { 98 | // set a new timer for later 99 | sched_lock.rel(); 100 | arch.ic.submitEoi(TIMER_VECTOR); 101 | arch.ic.oneshot(TIMER_VECTOR, 20); 102 | 103 | vmm.kernel_pagemap.load(); 104 | asm volatile ("sti"); 105 | while (true) {} 106 | } 107 | } 108 | 109 | pub fn reschedule(frame: *trap.TrapFrame) callconv(.C) void { 110 | if (smp.getCoreInfo().cur_thread) |old_thread| { 111 | old_thread.context = frame.*; 112 | smp.getCoreInfo().cur_thread = null; 113 | 114 | sched_lock.acq(); 115 | thread_list.enqueue(old_thread); 116 | sched_lock.rel(); 117 | } 118 | 119 | var thread = getNextThread(); 120 | smp.getCoreInfo().cur_thread = thread; 121 | smp.getCoreInfo().tss.rsp0 = thread.kernel_stack; 122 | 123 | frame.* = thread.context; 124 | thread.proc.pagemap.load(); 125 | 126 | arch.ic.submitEoi(TIMER_VECTOR); 127 | arch.ic.oneshot(TIMER_VECTOR, 20); 128 | } 129 | 130 | pub fn spawnKernelThread(func: *const fn (u64) noreturn, arg: ?u64) !*Thread { 131 | const target = @import("builtin").target.cpu.arch; 132 | const mem = @import("std").mem; 133 | var thread = try allocator().create(Thread); 134 | errdefer allocator().destroy(thread); 135 | 136 | thread.kernel_stack = createKernelStack() orelse return error.OutOfMemory; 137 | thread.context = mem.zeroes(trap.TrapFrame); 138 | thread.proc = &proc.kernel_process; 139 | 140 | switch (target) { 141 | .x86_64 => { 142 | thread.context.rip = @ptrToInt(func); 143 | thread.context.rsp = thread.kernel_stack; 144 | thread.context.ss = 0x30; 145 | thread.context.cs = 0x28; 146 | thread.context.rflags = 0x202; 147 | 148 | if (arg) |elem| { 149 | thread.context.rdi = elem; 150 | } 151 | }, 152 | else => { 153 | @panic("unsupported architecture " ++ @tagName(target) ++ "!"); 154 | }, 155 | } 156 | 157 | sched_lock.acq(); 158 | thread_list.enqueue(thread); 159 | sched_lock.rel(); 160 | 161 | return thread; 162 | } 163 | 164 | pub fn enable() !void { 165 | smp.getCoreInfo().tss.ist1 = createKernelStack() orelse return error.OutOfMemory; 166 | 167 | arch.ic.oneshot(TIMER_VECTOR, 20); 168 | asm volatile ("sti"); 169 | } 170 | -------------------------------------------------------------------------------- /kernel/src/smp.zig: -------------------------------------------------------------------------------- 1 | const limine = @import("limine"); 2 | const target = @import("builtin").target; 3 | const atomic = @import("std").atomic; 4 | const sched = @import("root").sched; 5 | const arch = @import("root").arch; 6 | const vmm = @import("root").vmm; 7 | 8 | const sink = @import("std").log.scoped(.smp); 9 | const zeroInit = @import("std").mem.zeroInit; 10 | const allocator = @import("root").allocator; 11 | const AtomicType = atomic.Atomic; 12 | 13 | pub const SpinLock = struct { 14 | lock_bits: AtomicType(u32) = .{ .value = 0 }, 15 | refcount: AtomicType(usize) = .{ .value = 0 }, 16 | intr_mode: bool = false, 17 | 18 | pub fn acq(self: *SpinLock) void { 19 | _ = self.refcount.fetchAdd(1, .Monotonic); 20 | 21 | var current = arch.intrEnabled(); 22 | arch.setIntrMode(false); 23 | 24 | while (true) { 25 | // ------------------------------------------------ 26 | // x86 Instruction | Micro ops | Base Latency 27 | // ------------------------------------------------ 28 | // XCHG 8 23 29 | // LOCK XADD 9 18 30 | // LOCK CMPXCHG 10 18 31 | // LOCK CMPXCHG8B 20 19 32 | // ------------------------------------------------ 33 | // We're optimizing for micro ops, since base 34 | // latency isn't consistent across CPU families. 35 | // Therefore, we go with the XCHG instruction... 36 | // ------------------------------------------------ 37 | // Source: https://agner.org/optimize/instruction_tables.pdf 38 | // 39 | if (self.lock_bits.swap(1, .Acquire) == 0) { 40 | // 'self.lock_bits.swap' translates to a XCHG 41 | break; 42 | } 43 | 44 | while (self.lock_bits.fetchAdd(0, .Monotonic) != 0) { 45 | // IRQs can be recived while waiting 46 | // for the lock to be available... 47 | arch.setIntrMode(current); 48 | atomic.spinLoopHint(); 49 | arch.setIntrMode(false); 50 | } 51 | } 52 | 53 | _ = self.refcount.fetchSub(1, .Monotonic); 54 | atomic.compilerFence(.Acquire); 55 | self.intr_mode = current; 56 | } 57 | 58 | pub fn rel(self: *SpinLock) void { 59 | self.lock_bits.store(0, .Release); 60 | atomic.compilerFence(.Release); 61 | arch.setIntrMode(self.intr_mode); 62 | } 63 | 64 | // wrappers for zig stdlib 65 | pub inline fn lock(self: *SpinLock) void { 66 | self.acq(); 67 | } 68 | pub inline fn unlock(self: *SpinLock) void { 69 | self.rel(); 70 | } 71 | }; 72 | 73 | pub const CoreInfo = struct { 74 | processor_id: u32, 75 | lapic_id: u32, 76 | ticks_per_ms: u64 = 0, 77 | user_stack: u64 = 0, 78 | tss: arch.TSS = .{}, 79 | is_bsp: bool = false, 80 | cur_thread: ?*sched.Thread = null, 81 | }; 82 | 83 | pub inline fn isBsp() bool { 84 | switch (target.cpu.arch) { 85 | .x86_64 => { 86 | // Since this function is called before 87 | // IA32_GS_BASE is set, make sure it exists 88 | // or assume we're the BSP 89 | if (arch.rdmsr(0xC0000101) == 0) 90 | return true; 91 | 92 | return getCoreInfo().is_bsp; 93 | }, 94 | else => { 95 | @compileError("unsupported arch " ++ @tagName(target.cpu.arch) ++ "!"); 96 | }, 97 | } 98 | } 99 | 100 | pub inline fn getCoreInfo() *CoreInfo { 101 | switch (target.cpu.arch) { 102 | .x86_64 => { 103 | return @intToPtr(*CoreInfo, arch.rdmsr(0xC0000101)); 104 | }, 105 | else => { 106 | @compileError("unsupported arch " ++ @tagName(target.cpu.arch) ++ "!"); 107 | }, 108 | } 109 | } 110 | 111 | pub inline fn setCoreInfo(ptr: *CoreInfo) void { 112 | switch (target.cpu.arch) { 113 | .x86_64 => { 114 | arch.wrmsr(0xC0000101, @ptrToInt(ptr)); 115 | }, 116 | else => { 117 | @compileError("unsupported arch " ++ @tagName(target.cpu.arch) ++ "!"); 118 | }, 119 | } 120 | } 121 | 122 | pub export var smp_request: limine.SmpRequest = .{}; 123 | var booted_cores: AtomicType(u16) = .{ .value = 1 }; 124 | 125 | fn createCoreInfo(info: *limine.SmpInfo) void { 126 | var coreinfo = allocator().create(CoreInfo) catch unreachable; 127 | 128 | coreinfo.* = zeroInit(CoreInfo, .{ 129 | .lapic_id = info.lapic_id, 130 | .processor_id = info.processor_id, 131 | }); 132 | 133 | setCoreInfo(coreinfo); 134 | } 135 | 136 | pub export fn ap_entry(info: *limine.SmpInfo) callconv(.C) noreturn { 137 | // setup the important stuff 138 | vmm.kernel_pagemap.load(); 139 | createCoreInfo(info); 140 | arch.setupCpu(); 141 | arch.ic.enable(); 142 | 143 | // load the TSS 144 | getCoreInfo().tss = zeroInit(arch.TSS, arch.TSS{ 145 | .rsp0 = sched.createKernelStack().?, 146 | }); 147 | arch.loadTSS(&getCoreInfo().tss); 148 | 149 | // let BSP know we're done, then off we go! 150 | _ = booted_cores.fetchAdd(1, .Monotonic); 151 | sched.enable() catch unreachable; 152 | while (true) {} 153 | } 154 | 155 | pub fn init() void { 156 | if (smp_request.response) |resp| { 157 | sink.info("booting {} cores...", .{resp.cpu_count}); 158 | 159 | for (resp.cpus()) |cpu| { 160 | if (cpu.lapic_id == resp.bsp_lapic_id) { 161 | createCoreInfo(cpu); 162 | getCoreInfo().is_bsp = true; 163 | 164 | // load the TSS 165 | getCoreInfo().tss = zeroInit(arch.TSS, arch.TSS{}); 166 | getCoreInfo().tss.rsp0 = sched.createKernelStack().?; 167 | arch.loadTSS(&getCoreInfo().tss); 168 | 169 | arch.ic.setup(); 170 | continue; 171 | } 172 | 173 | cpu.goto_address = &ap_entry; 174 | } 175 | 176 | while (booted_cores.load(.Monotonic) != resp.cpu_count) {} 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /kernel/src/util/libc.zig: -------------------------------------------------------------------------------- 1 | pub usingnamespace @cImport({ 2 | @cInclude("sys/stat.h"); 3 | }); 4 | 5 | pub const TimeSpec = extern struct { 6 | tv_sec: i64, 7 | tv_nsec: i64, 8 | }; 9 | 10 | pub const Stat = extern struct { 11 | st_dev: u64, 12 | st_mode: i32, 13 | st_ino: u64, 14 | st_nlink: i32, 15 | st_uid: i32, 16 | st_gid: i32, 17 | st_rdev: u64, 18 | st_atim: TimeSpec, 19 | st_mtim: TimeSpec, 20 | st_ctim: TimeSpec, 21 | st_size: i64, 22 | st_blocks: i64, 23 | st_blksize: i64, 24 | }; 25 | -------------------------------------------------------------------------------- /kernel/src/vfs.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const limine = @import("limine"); 3 | const smp = @import("root").smp; 4 | const zeroes = std.mem.zeroes; 5 | const sink = std.log.scoped(.vfs); 6 | const allocator = @import("root").allocator; 7 | 8 | // modules 9 | const tmpfs = @import("fs/tmpfs.zig"); 10 | const libc = @import("util/libc.zig"); 11 | 12 | pub const VfsError = error{ 13 | OutOfMemory, 14 | InvalidPath, 15 | InvalidParams, 16 | FileNotDir, 17 | FileNotExist, 18 | }; 19 | 20 | pub const VTable = struct { 21 | read: *const fn (node: *VfsNode, buffer: [*]u8, offset: u64, length: usize) VfsError!usize, 22 | write: *const fn (node: *VfsNode, buffer: [*]const u8, offset: u64, length: usize) VfsError!usize, 23 | close: *const fn (node: *VfsNode) void, 24 | }; 25 | 26 | pub const FsVTable = struct { 27 | create: *const fn (parent: *VfsNode, name: []const u8, st: libc.Stat) VfsError!*VfsNode, 28 | }; 29 | 30 | pub const Filesystem = struct { 31 | vtable: *const FsVTable, 32 | context: *anyopaque, 33 | }; 34 | 35 | pub const VfsNode = struct { 36 | name: []const u8 = undefined, 37 | stat: libc.Stat = undefined, 38 | children: std.ArrayList(*VfsNode) = undefined, 39 | parent: *VfsNode = undefined, 40 | 41 | vtable: *const VTable = undefined, 42 | fs: *const Filesystem = undefined, 43 | mountpoint: ?*VfsNode = null, 44 | inode: ?*anyopaque = null, 45 | lock: smp.SpinLock = .{}, 46 | 47 | pub fn isDir(self: *VfsNode) bool { 48 | if ((self.stat.st_mode & libc.S_IFDIR) != 0) { 49 | return true; 50 | } else { 51 | return false; 52 | } 53 | } 54 | 55 | pub fn flatten(self: *VfsNode) *VfsNode { 56 | if (self.mountpoint) |new_root| { 57 | return new_root; 58 | } else { 59 | return self; 60 | } 61 | } 62 | 63 | pub fn find(self: *VfsNode, path: []const u8) ?*VfsNode { 64 | std.debug.assert(self.isDir()); 65 | 66 | if (std.mem.eql(u8, path, ".")) { 67 | return self; 68 | } else if (std.mem.eql(u8, path, "..")) { 69 | return self.parent; 70 | } 71 | 72 | self.lock.acq(); 73 | defer self.lock.rel(); 74 | 75 | for (self.children.items) |file| { 76 | if (std.mem.eql(u8, file.name, path)) { 77 | // TODO(cleanbaja): handle symlinks 78 | return file; 79 | } 80 | } 81 | 82 | return null; 83 | } 84 | }; 85 | 86 | pub const VStream = struct { 87 | node: *VfsNode, 88 | offset: u64, 89 | 90 | pub const ReaderError = VfsError || std.os.PReadError || error{OutOfMemory} || error{NotImplemented}; 91 | pub const SeekError = error{}; 92 | pub const GetSeekPosError = error{}; 93 | 94 | pub const SeekableStream = std.io.SeekableStream( 95 | *VStream, 96 | SeekError, 97 | GetSeekPosError, 98 | VStream.seekTo, 99 | VStream.seekBy, 100 | VStream.getPosFn, 101 | VStream.getEndPosFn, 102 | ); 103 | pub const Reader = std.io.Reader( 104 | *VStream, 105 | ReaderError, 106 | VStream.read, 107 | ); 108 | 109 | pub fn init(nd: *VfsNode) @This() { 110 | return @This(){ 111 | .node = nd, 112 | .offset = 0, 113 | }; 114 | } 115 | 116 | fn seekTo(self: *VStream, offset: u64) SeekError!void { 117 | self.offset = offset; 118 | } 119 | 120 | fn seekBy(self: *VStream, offset: i64) SeekError!void { 121 | self.offset +%= @bitCast(u64, offset); 122 | } 123 | 124 | fn getPosFn(self: *VStream) GetSeekPosError!u64 { 125 | return self.offset; 126 | } 127 | 128 | fn getEndPosFn(self: *VStream) GetSeekPosError!u64 { 129 | _ = self; 130 | 131 | return 0; 132 | } 133 | 134 | fn read(self: *VStream, buffer: []u8) ReaderError!usize { 135 | return try self.node.vtable.read( 136 | self.node, 137 | @ptrCast([*]u8, buffer), 138 | self.offset, 139 | buffer.len, 140 | ); 141 | } 142 | 143 | pub fn seekableStream(self: *VStream) SeekableStream { 144 | return .{ .context = self }; 145 | } 146 | 147 | pub fn reader(self: *VStream) Reader { 148 | return .{ .context = self }; 149 | } 150 | }; 151 | 152 | pub fn createNode( 153 | parent_dir: ?*VfsNode, 154 | pathname: []const u8, 155 | st: libc.Stat, 156 | fs: *const Filesystem, 157 | vtable: *const VTable, 158 | add: bool, 159 | ) !*VfsNode { 160 | var new_node = try allocator().create(VfsNode); 161 | errdefer allocator().destroy(new_node); 162 | 163 | var parent: *VfsNode = root.flatten(); 164 | if (parent_dir) |p| { 165 | parent = p.flatten(); 166 | } 167 | 168 | new_node.* = .{ 169 | .name = pathname, 170 | .parent = parent, 171 | .stat = st, 172 | .fs = fs, 173 | .vtable = vtable, 174 | }; 175 | 176 | if (new_node.isDir()) { 177 | new_node.children = std.ArrayList(*VfsNode).init(allocator()); 178 | 179 | var dot_dir = try allocator().create(VfsNode); 180 | var dotdot_dir = try allocator().create(VfsNode); 181 | 182 | dot_dir.* = .{ 183 | .name = ".", 184 | .stat = st, 185 | .fs = fs, 186 | .vtable = vtable, 187 | .parent = new_node, 188 | }; 189 | 190 | dotdot_dir.* = .{ 191 | .name = "..", 192 | .stat = parent.stat, 193 | .fs = fs, 194 | .vtable = vtable, 195 | .parent = new_node, 196 | }; 197 | 198 | try new_node.children.append(dot_dir); 199 | try new_node.children.append(dotdot_dir); 200 | } 201 | 202 | if (add) { 203 | parent.lock.acq(); 204 | defer parent.lock.rel(); 205 | 206 | try parent.children.append(new_node); 207 | } 208 | 209 | return new_node; 210 | } 211 | 212 | pub fn resolve(parent: ?*VfsNode, path: []const u8) !*VfsNode { 213 | if (path.len == 0) { 214 | return error.InvalidParams; 215 | } 216 | 217 | var cur: *VfsNode = undefined; 218 | var iter = std.mem.split(u8, path, "/"); 219 | 220 | if (parent == null or path[0] == '/') { 221 | cur = root.flatten(); 222 | } else { 223 | cur = parent.?.flatten(); 224 | } 225 | 226 | while (iter.next()) |elem| { 227 | if (elem.len == 0) { 228 | continue; 229 | } 230 | 231 | if (!cur.isDir()) { 232 | return error.InvalidPath; 233 | } 234 | 235 | if (cur.find(elem)) |next| { 236 | cur = next.flatten(); 237 | } else { 238 | return error.FileNotFound; 239 | } 240 | } 241 | 242 | return cur; 243 | } 244 | 245 | pub fn createDeepNode( 246 | parent_dir: ?*VfsNode, 247 | pathname: []const u8, 248 | st: libc.Stat, 249 | fs: *Filesystem, 250 | vtable: *const VTable, 251 | ) !*VfsNode { 252 | var cur: *VfsNode = undefined; 253 | if (parent_dir) |p| { 254 | cur = p.flatten(); 255 | } else { 256 | cur = root.flatten(); 257 | } 258 | 259 | var iter = std.mem.split(u8, pathname, "/"); 260 | var stat = std.mem.zeroes(libc.Stat); 261 | 262 | while (iter.next()) |elem| { 263 | if (!cur.isDir()) { 264 | return error.InvalidPath; 265 | } 266 | 267 | var result = cur.find(elem); 268 | 269 | if (result == null) { 270 | if (iter.rest().len > 0) { 271 | stat.st_mode = cur.stat.st_mode; 272 | result = try createNode(cur, elem, stat, cur.fs, cur.vtable, true); 273 | } else { 274 | result = try createNode(cur, elem, st, fs, vtable, true); 275 | } 276 | } 277 | 278 | cur = result.?.flatten(); 279 | } 280 | 281 | return cur; 282 | } 283 | 284 | pub fn mount(target: *VfsNode, st: libc.Stat, fs: *const Filesystem, vtable: *const VTable) !void { 285 | if (!target.isDir()) { 286 | return error.FileNotDir; 287 | } 288 | 289 | target.mountpoint = try createNode(target.parent, target.name, st, fs, vtable, false); 290 | } 291 | 292 | pub const CpioReader = struct { 293 | bytes: []u8 = undefined, 294 | offset: u64 = 0, 295 | complete: bool = false, 296 | 297 | pub const Entry = struct { 298 | dev: u32 = 0, 299 | devmajor: u32, 300 | devminor: u32, 301 | ino: u32, 302 | mode: u32, 303 | uid: u32, 304 | gid: u32, 305 | nlink: u32, 306 | rdev: u32 = 0, 307 | rdevmajor: u32, 308 | rdevminor: u32, 309 | mtime: u64, 310 | filesize: usize, 311 | name: []const u8 = undefined, 312 | file: []u8 = undefined, 313 | }; 314 | 315 | const Version = enum(u8) { 316 | old, 317 | portable_ascii, 318 | new_ascii, 319 | crc, 320 | }; 321 | 322 | pub fn init(file: []u8) CpioReader { 323 | return .{ 324 | .bytes = file, 325 | }; 326 | } 327 | 328 | fn peek16(self: *CpioReader) u16 { 329 | return self.bytes[self.offset] | (@as(u16, self.bytes[self.offset + 1]) << 8); 330 | } 331 | 332 | fn readStr32(self: *CpioReader) u32 { 333 | var ret = std.fmt.parseInt(u32, self.bytes[self.offset .. self.offset + 8], 16) catch unreachable; 334 | self.offset += 8; 335 | return ret; 336 | } 337 | 338 | pub fn getVersion(self: *CpioReader) Version { 339 | const OLD_MAGIC: u16 = 0o070_707; 340 | const PORTABLE_MAGIC: []const u8 = "070707"; 341 | const NEW_ASCII_MAGIC: []const u8 = "070701"; 342 | const CRC_MAGIC: []const u8 = "070702"; 343 | 344 | // NOTE: we don't support old format encoded in big endian 345 | if (self.peek16() == OLD_MAGIC) { 346 | return .old; 347 | } else if (std.mem.eql(u8, self.bytes[self.offset .. self.offset + 6], PORTABLE_MAGIC)) { 348 | return .portable_ascii; 349 | } else if (std.mem.eql(u8, self.bytes[self.offset .. self.offset + 6], NEW_ASCII_MAGIC)) { 350 | return .new_ascii; 351 | } else if (std.mem.eql(u8, self.bytes[self.offset .. self.offset + 6], CRC_MAGIC)) { 352 | return .crc; 353 | } else { 354 | @panic("unable to find CPIO version!"); 355 | } 356 | } 357 | 358 | pub fn parseNewAsciiOrCrc(self: *CpioReader) Entry { 359 | self.offset += 6; 360 | 361 | var ent: Entry = .{ 362 | .ino = self.readStr32(), 363 | .mode = self.readStr32(), 364 | .uid = self.readStr32(), 365 | .gid = self.readStr32(), 366 | .nlink = self.readStr32(), 367 | .mtime = @as(u64, self.readStr32()), 368 | .filesize = self.readStr32(), 369 | .devmajor = self.readStr32(), 370 | .devminor = self.readStr32(), 371 | .rdevmajor = self.readStr32(), 372 | .rdevminor = self.readStr32(), 373 | }; 374 | 375 | var namesize = self.readStr32(); 376 | _ = self.readStr32(); // skip crc 377 | 378 | ent.name = self.bytes[self.offset .. self.offset + namesize - 1]; 379 | self.offset += namesize; 380 | self.offset += ((4 - self.offset % 4) % 4); 381 | 382 | ent.file = self.bytes[self.offset .. self.offset + ent.filesize]; 383 | self.offset += ent.filesize; 384 | self.offset += ((4 - self.offset % 4) % 4); 385 | 386 | return ent; 387 | } 388 | 389 | pub fn next(self: *CpioReader) ?Entry { 390 | if (self.complete) { 391 | return null; 392 | } 393 | 394 | var entry = ent: { 395 | switch (self.getVersion()) { 396 | .old, .portable_ascii => { 397 | sink.warn("parsing \"old\" or \"portable_ascii\" CPIO variants is unsupported!", .{}); 398 | return null; 399 | }, 400 | .new_ascii, .crc => { 401 | break :ent self.parseNewAsciiOrCrc(); 402 | }, 403 | } 404 | }; 405 | 406 | if (std.mem.eql(u8, entry.name, "TRAILER!!!")) { 407 | self.complete = true; 408 | return null; 409 | } else { 410 | return entry; 411 | } 412 | } 413 | }; 414 | 415 | pub export var mods_request: limine.ModuleRequest = .{}; 416 | pub var root: VfsNode = undefined; 417 | 418 | pub fn init() void { 419 | // fill in the root vfs node 420 | root.name = "/"; 421 | root.stat = zeroes(libc.Stat); 422 | root.stat.st_mode = libc.S_IFDIR; 423 | 424 | // create tmpfs context 425 | var context = allocator().create(tmpfs.TmpfsContext) catch unreachable; 426 | var filesystem = allocator().create(Filesystem) catch unreachable; 427 | filesystem.context = context; 428 | filesystem.vtable = &tmpfs.fs_vtable; 429 | context.device = 1; 430 | 431 | // mount tmpfs to '/' 432 | mount(&root, root.stat, filesystem, &tmpfs.vtable) catch unreachable; 433 | 434 | if (mods_request.response) |resp| { 435 | var mod = resp.modules()[0]; 436 | var reader = CpioReader.init(mod.address[0..mod.size]); 437 | var stat = std.mem.zeroes(libc.Stat); 438 | 439 | sink.info("initrd format is \"{s}\"", .{@tagName(reader.getVersion())}); 440 | 441 | while (reader.next()) |file| { 442 | stat.st_mode = @intCast(i32, file.mode); 443 | stat.st_nlink = @intCast(i32, file.nlink); 444 | stat.st_size = @intCast(i64, file.filesize); 445 | var node = createDeepNode(null, file.name, stat, filesystem, &tmpfs.vtable) catch unreachable; 446 | 447 | var inode = allocator().create(tmpfs.TmpfsInode) catch unreachable; 448 | inode.base = file.file; 449 | inode.bytes = file.filesize; 450 | node.inode = inode; 451 | } 452 | } 453 | } 454 | -------------------------------------------------------------------------------- /kernel/src/vmm.zig: -------------------------------------------------------------------------------- 1 | const limine = @import("limine"); 2 | const paging = @import("root").arch.paging; 3 | const allocator = @import("root").allocator; 4 | const pmm = @import("root").pmm; 5 | const std = @import("std"); 6 | 7 | pub const CacheMode = enum(u4) { uncached, write_combining, write_protect, write_back }; 8 | 9 | pub const MapFlags = packed struct { 10 | read: bool = false, 11 | write: bool = false, 12 | exec: bool = false, 13 | user: bool = false, 14 | 15 | cache_type: CacheMode = .write_back, 16 | _padding: u24 = 0, 17 | }; 18 | 19 | pub export var kaddr_request: limine.KernelAddressRequest = .{}; 20 | pub const DEFAULT_HIGHER_HALF: u64 = 0xFFFF800000000000; 21 | pub var kernel_pagemap = paging.PageMap{}; 22 | 23 | pub fn toHigherHalf(ptr: usize) usize { 24 | return ptr + DEFAULT_HIGHER_HALF; 25 | } 26 | 27 | pub fn fromHigherHalf(ptr: usize) usize { 28 | return ptr - DEFAULT_HIGHER_HALF; 29 | } 30 | 31 | pub fn createPagemap() !*paging.PageMap { 32 | var result = try allocator().create(paging.PageMap); 33 | result.* = .{ .root = pmm.allocPages(1) orelse return error.OutOfMemory }; 34 | 35 | // copy over the higher half 36 | var higher_half = @intToPtr([*]u64, toHigherHalf(result.root + (256 * @sizeOf(u64)))); 37 | var kernel_half = @intToPtr([*]u64, toHigherHalf(kernel_pagemap.root + (256 * @sizeOf(u64)))); 38 | var i: u64 = 256; 39 | 40 | while (i < 512) : (i += 1) { 41 | higher_half[i] = kernel_half[i]; 42 | } 43 | 44 | return result; 45 | } 46 | 47 | pub fn init() !void { 48 | kernel_pagemap.root = pmm.allocPages(1) orelse return error.OutOfMemory; 49 | var map_flags: MapFlags = .{ .read = true, .write = true, .exec = true }; 50 | 51 | // map some simple stuff 52 | var resp = kaddr_request.response orelse return error.MissingBootInfo; 53 | var pbase: usize = resp.physical_base; 54 | var vbase: usize = resp.virtual_base; 55 | var i: usize = 0; 56 | 57 | while (i < (0x400 * 0x1000)) : (i += 0x1000) { 58 | kernel_pagemap.mapPage(map_flags, vbase + i, pbase + i, false); 59 | } 60 | 61 | i = 0; 62 | map_flags.exec = false; 63 | while (i < @intCast(usize, (0x800 * 0x200000))) : (i += 0x200000) { 64 | kernel_pagemap.mapPage(map_flags, toHigherHalf(i), i, true); 65 | } 66 | 67 | // them map everything else 68 | for (pmm.memmap_request.response.?.entries()) |ent| { 69 | if (ent.base + ent.length < @intCast(usize, (0x800 * 0x200000))) { 70 | continue; 71 | } 72 | 73 | var base: usize = std.mem.alignBackward(ent.base, 0x200000); 74 | i = 0; 75 | 76 | while (i < std.mem.alignForward(ent.length, 0x200000)) : (i += 0x200000) { 77 | kernel_pagemap.mapPage(map_flags, toHigherHalf(base + i), base + i, true); 78 | } 79 | } 80 | 81 | kernel_pagemap.load(); 82 | } 83 | -------------------------------------------------------------------------------- /kernel/src/x86_64/arch.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const smp = @import("root").smp; 3 | const lapic = @import("lapic.zig"); 4 | const logger = std.log.scoped(.arch); 5 | 6 | // modules 7 | pub const trap = @import("trap.zig"); 8 | pub const paging = @import("paging.zig"); 9 | pub const cpu = @import("cpu.zig"); 10 | 11 | // globals 12 | pub var ic = lapic.LapicController{}; 13 | var gdt_lock = smp.SpinLock{}; 14 | var gdt_table = GDT{}; 15 | 16 | pub const Descriptor = extern struct { 17 | size: u16 align(1), 18 | ptr: u64 align(1), 19 | }; 20 | 21 | const CpuidResult = struct { 22 | eax: u32, 23 | ebx: u32, 24 | ecx: u32, 25 | edx: u32, 26 | }; 27 | 28 | const TSSDescriptor = extern struct { 29 | length: u16 align(1), 30 | base_low: u16 align(1), 31 | base_mid: u8 align(1), 32 | flags: u16 align(1), 33 | base_high: u8 align(1), 34 | base_ext: u32 align(1), 35 | reserved: u32 align(1) = 0, 36 | }; 37 | 38 | pub const TSS = extern struct { 39 | unused0: u32 align(1) = 0, 40 | rsp0: u64 align(1) = 0, 41 | rsp1: u64 align(1) = 0, 42 | rsp2: u64 align(1) = 0, 43 | unused1: u64 align(1) = 0, 44 | ist1: u64 align(1) = 0, 45 | ist2: u64 align(1) = 0, 46 | ist3: u64 align(1) = 0, 47 | ist4: u64 align(1) = 0, 48 | ist5: u64 align(1) = 0, 49 | ist6: u64 align(1) = 0, 50 | ist7: u64 align(1) = 0, 51 | unused2: u64 align(1) = 0, 52 | iopb: u32 align(1) = 0, 53 | }; 54 | 55 | const GDT = extern struct { 56 | entries: [9]u64 align(1) = .{ 57 | // null entry 58 | 0x0000000000000000, 59 | 60 | // 16-bit kernel code/data 61 | 0x00009a000000ffff, 62 | 0x000093000000ffff, 63 | 64 | // 32-bit kernel code/data 65 | 0x00cf9a000000ffff, 66 | 0x00cf93000000ffff, 67 | 68 | // 64-bit kernel code/data 69 | 0x00af9b000000ffff, 70 | 0x00af93000000ffff, 71 | 72 | // 64-bit user code/data 73 | 0x00AFFA000000FFFF, 74 | 0x008FF2000000FFFF, 75 | }, 76 | tss_desc: TSSDescriptor = .{ 77 | .length = 104, 78 | .base_low = 0, 79 | .base_mid = 0, 80 | .flags = 0b10001001, 81 | .base_high = 0, 82 | .base_ext = 0, 83 | .reserved = 0, 84 | }, 85 | 86 | pub fn load(self: *const GDT) void { 87 | const gdtr = Descriptor{ 88 | .size = @sizeOf(GDT) - 1, 89 | .ptr = @ptrToInt(self), 90 | }; 91 | 92 | // Reloading the GDT clears the GS base, so take 93 | // note of the current value here for later... 94 | var gs_base = rdmsr(0xC0000101); 95 | 96 | asm volatile ( 97 | \\lgdt %[gdtr] 98 | \\push $0x28 99 | \\lea 1f(%%rip), %%rax 100 | \\push %%rax 101 | \\lretq 102 | \\1: 103 | \\mov $0x30, %%eax 104 | \\mov %%eax, %%ds 105 | \\mov %%eax, %%es 106 | \\mov %%eax, %%fs 107 | \\mov %%eax, %%gs 108 | \\mov %%eax, %%ss 109 | : 110 | : [gdtr] "*p" (&gdtr), 111 | : "rax", "rcx", "memory" 112 | ); 113 | 114 | wrmsr(0xC0000101, gs_base); 115 | } 116 | }; 117 | 118 | pub fn intrEnabled() bool { 119 | var eflags = asm volatile ( 120 | \\pushf 121 | \\pop %[result] 122 | : [result] "=r" (-> u64), 123 | ); 124 | 125 | return ((eflags & 0x200) != 0); 126 | } 127 | 128 | pub fn setIntrMode(enabled: bool) void { 129 | if (enabled) { 130 | asm volatile ("sti"); 131 | } else { 132 | asm volatile ("cli"); 133 | } 134 | } 135 | 136 | pub fn loadTSS(tss: *TSS) void { 137 | gdt_lock.acq(); 138 | defer gdt_lock.rel(); 139 | 140 | var addr: u64 = @ptrToInt(tss); 141 | 142 | gdt_table.tss_desc.base_low = @truncate(u16, addr); 143 | gdt_table.tss_desc.base_mid = @truncate(u8, addr >> 16); 144 | gdt_table.tss_desc.flags = 0b10001001; 145 | gdt_table.tss_desc.base_high = @truncate(u8, addr >> 24); 146 | gdt_table.tss_desc.base_ext = @truncate(u32, addr >> 32); 147 | 148 | asm volatile ("ltr %[tss]" 149 | : 150 | : [tss] "r" (@as(u16, 0x48)), 151 | : "memory" 152 | ); 153 | } 154 | 155 | pub fn rdtsc() u64 { 156 | var low: u32 = undefined; 157 | var high: u32 = undefined; 158 | 159 | asm volatile ("rdtsc" 160 | : [_] "={eax}" (low), 161 | [_] "={edx}" (high), 162 | ); 163 | 164 | return @as(u64, low) | (@as(u64, high) << 32); 165 | } 166 | 167 | pub fn wrmsr(reg: u64, val: u64) void { 168 | asm volatile ("wrmsr" 169 | : 170 | : [_] "{eax}" (val & 0xFFFFFFFF), 171 | [_] "{edx}" (val >> 32), 172 | [_] "{ecx}" (reg), 173 | ); 174 | } 175 | 176 | pub fn rdmsr(reg: u64) u64 { 177 | var low: u32 = undefined; 178 | var high: u32 = undefined; 179 | 180 | asm volatile ("rdmsr" 181 | : [_] "={eax}" (low), 182 | [_] "={edx}" (high), 183 | : [_] "{ecx}" (reg), 184 | ); 185 | 186 | return @as(u64, low) | (@as(u64, high) << 32); 187 | } 188 | 189 | pub inline fn wrcr4(value: usize) void { 190 | asm volatile ("mov %[val], %%cr4" 191 | : 192 | : [val] "r" (value), 193 | : "memory" 194 | ); 195 | } 196 | 197 | pub inline fn rdcr4() usize { 198 | return asm volatile ("mov %%cr4, %[result]" 199 | : [result] "=r" (-> u64), 200 | : 201 | : "memory" 202 | ); 203 | } 204 | 205 | pub inline fn wrcr0(value: usize) void { 206 | asm volatile ("mov %[val], %%cr4" 207 | : 208 | : [val] "r" (value), 209 | : "memory" 210 | ); 211 | } 212 | 213 | pub inline fn rdcr0() usize { 214 | return asm volatile ("mov %%cr4, %[result]" 215 | : [result] "=r" (-> u64), 216 | : 217 | : "memory" 218 | ); 219 | } 220 | 221 | pub fn cpuid(leaf: u32, subleaf: u32) CpuidResult { 222 | var eax: u32 = 0; 223 | var ebx: u32 = 0; 224 | var ecx: u32 = 0; 225 | var edx: u32 = 0; 226 | 227 | asm volatile ( 228 | \\cpuid 229 | : [eax] "={eax}" (eax), 230 | [ebx] "={ebx}" (ebx), 231 | [ecx] "={ecx}" (ecx), 232 | [edx] "={edx}" (edx), 233 | : [leaf] "{eax}" (leaf), 234 | [subleaf] "{ecx}" (subleaf), 235 | : "memory" 236 | ); 237 | 238 | return .{ 239 | .eax = eax, 240 | .ebx = ebx, 241 | .ecx = ecx, 242 | .edx = edx, 243 | }; 244 | } 245 | 246 | pub fn setupCpu() void { 247 | gdt_table.load(); 248 | trap.load(); 249 | cpu.init(); 250 | } 251 | 252 | //pub fn setupCpu() void { 253 | // logger.info("performing early cpu init...", .{}); 254 | // trap.init(); 255 | // 256 | // setupAP(); 257 | //} 258 | -------------------------------------------------------------------------------- /kernel/src/x86_64/cpu.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const arch = @import("root").arch; 3 | const smp = @import("root").smp; 4 | const sink = std.log.scoped(.cpu); 5 | const trap = arch.trap; 6 | 7 | const SaveType = enum { 8 | fxsave, 9 | xsave, 10 | xsaveopt, 11 | xsavec, 12 | xsaves, 13 | }; 14 | 15 | const FpuState = extern struct { 16 | // legacy x87 fpu context 17 | ctrl: u16, 18 | status: u16, 19 | tag: u16, 20 | fop: u16, 21 | ip: u64, 22 | dp: u64, 23 | 24 | // mxcsr control double-words 25 | mxcsr: u32, 26 | mxcsr_mask: u32, 27 | 28 | // x87 floating point regs 29 | st_regs: [32]u32, 30 | 31 | // SSE simd regs and padding 32 | xmm_regs: [64]u32, 33 | padding: [24]u32, 34 | }; 35 | 36 | const XSaveState = extern struct { 37 | fpu_state: FpuState, 38 | xfeatures: u64, 39 | xcomp_bv: u64, 40 | reserved: [6]u64, 41 | }; 42 | 43 | comptime { 44 | std.debug.assert(@sizeOf(FpuState) == 512); 45 | std.debug.assert(@sizeOf(XSaveState) == 512 + 64); 46 | } 47 | 48 | // the combined bits of every supported XCR0 extension 49 | const supported_mask = 0x602e7; 50 | 51 | var fpu_storage_size: usize = 0; 52 | var fpu_storage_align: usize = 0; 53 | var fpu_mode: SaveType = undefined; 54 | 55 | inline fn wrxcr(comptime reg: usize, value: u64) void { 56 | var edx: u32 = @truncate(u32, value >> 32); 57 | var eax: u32 = @truncate(u32, value); 58 | 59 | asm volatile ("xsetbv" 60 | : 61 | : [eax] "{eax}" (eax), 62 | [edx] "{edx}" (edx), 63 | [ecx] "{ecx}" (reg), 64 | : "memory" 65 | ); 66 | } 67 | 68 | pub export fn handleSyscall(frame: *trap.TrapFrame) callconv(.C) void { 69 | sink.err("unsupported syscall #{}!", .{frame.rax}); 70 | while (true) {} 71 | } 72 | 73 | pub fn fpuRestore(save_area: []const u8) void { 74 | std.debug.assert(@ptrToInt(&save_area) % fpu_storage_align == 0); 75 | 76 | var rbfm: u32 = 0xffffffff; 77 | var rbfm_high: u32 = 0xffffffff; 78 | 79 | switch (fpu_mode) { 80 | .xsave, .xsavec, .xsaveopt => { 81 | asm volatile ("xrstorq (%[context])" 82 | : 83 | : [context] "r" (save_area), 84 | [eax] "{eax}" (rbfm), 85 | [edx] "{edx}" (rbfm_high), 86 | : "memory" 87 | ); 88 | }, 89 | .xsaves => { 90 | asm volatile ("xrstorsq (%[context])" 91 | : 92 | : [context] "r" (save_area), 93 | [eax] "{eax}" (rbfm), 94 | [edx] "{edx}" (rbfm_high), 95 | : "memory" 96 | ); 97 | }, 98 | .fxsave => { 99 | asm volatile ("fxrstorq (%[context])" 100 | : 101 | : [context] "r" (save_area), 102 | : "memory" 103 | ); 104 | }, 105 | } 106 | } 107 | 108 | pub fn fpuSave(save_area: []const u8) void { 109 | std.debug.assert(@ptrToInt(&save_area) % fpu_storage_align == 0); 110 | 111 | var rbfm: u32 = 0xffffffff; 112 | var rbfm_high: u32 = 0xffffffff; 113 | 114 | switch (fpu_mode) { 115 | .xsave => { 116 | asm volatile ("xsaveq (%[context])" 117 | : 118 | : [context] "r" (@ptrToInt(&save_area)), 119 | [eax] "{eax}" (rbfm), 120 | [edx] "{edx}" (rbfm_high), 121 | : "memory" 122 | ); 123 | }, 124 | .xsavec => { 125 | asm volatile ("xsavecq (%[context])" 126 | : 127 | : [context] "r" (@ptrToInt(&save_area)), 128 | [eax] "{eax}" (rbfm), 129 | [edx] "{edx}" (rbfm_high), 130 | : "memory" 131 | ); 132 | }, 133 | .xsaves => { 134 | asm volatile ("xsavesq (%[context])" 135 | : 136 | : [context] "r" (@ptrToInt(&save_area)), 137 | [eax] "{eax}" (rbfm), 138 | [edx] "{edx}" (rbfm_high), 139 | : "memory" 140 | ); 141 | }, 142 | .xsaveopt => { 143 | asm volatile ("xsaveoptq (%[context])" 144 | : 145 | : [context] "r" (@ptrToInt(&save_area)), 146 | [eax] "{eax}" (rbfm), 147 | [edx] "{edx}" (rbfm_high), 148 | : "memory" 149 | ); 150 | }, 151 | .fxsave => { 152 | asm volatile ("fxsaveq (%[context])" 153 | : 154 | : [context] "r" (@ptrToInt(&save_area)), 155 | : "memory" 156 | ); 157 | }, 158 | } 159 | } 160 | 161 | pub fn printBrandName() void { 162 | if (!smp.isBsp()) 163 | return; 164 | 165 | var brand: [12]u32 = undefined; 166 | var leaf1 = arch.cpuid(0x80000002, 0); 167 | var leaf2 = arch.cpuid(0x80000003, 0); 168 | var leaf3 = arch.cpuid(0x80000004, 0); 169 | 170 | brand[0..12].* = .{ 171 | leaf1.eax, leaf1.ebx, leaf1.ecx, leaf1.edx, 172 | leaf2.eax, leaf2.ebx, leaf2.ecx, leaf2.edx, 173 | leaf3.eax, leaf3.ebx, leaf3.ecx, leaf3.edx, 174 | }; 175 | 176 | sink.info("core name is {s}", .{@ptrCast([*]u8, &brand)[0 .. 12 * 4]}); 177 | } 178 | 179 | pub fn setupFpu() void { 180 | // enable SSE & FXSAVE/FXRSTOR 181 | arch.wrcr4(arch.rdcr4() | (3 << 9)); 182 | 183 | if (arch.cpuid(1, 0).ecx & (1 << 26) != 0) { 184 | arch.wrcr4(arch.rdcr4() | (1 << 18)); 185 | fpu_storage_align = 64; 186 | fpu_mode = .xsave; 187 | 188 | var result = arch.cpuid(0xD, 1); 189 | if (result.eax & (1 << 0) != 0) { 190 | fpu_mode = .xsaveopt; 191 | } 192 | if (result.eax & (1 << 1) != 0) { 193 | fpu_mode = .xsavec; 194 | } 195 | if (result.eax & (1 << 3) != 0) { 196 | fpu_mode = .xsaves; 197 | 198 | // clear XSS, since munix doesn't support any supervisor states 199 | arch.wrmsr(0xda0, 0); 200 | } 201 | 202 | wrxcr(0, @as(u64, arch.cpuid(0xD, 0).eax) & supported_mask); 203 | result = arch.cpuid(0xD, 0); 204 | 205 | if (smp.isBsp()) { 206 | sink.info("supported extensions bitmask: 0x{X}", .{result.eax}); 207 | } 208 | 209 | switch (fpu_mode) { 210 | .xsave, .xsaveopt => { 211 | fpu_storage_size = result.ecx; 212 | }, 213 | .xsavec => { 214 | fpu_storage_size = result.ebx; 215 | }, 216 | .xsaves => { 217 | fpu_storage_size = arch.cpuid(0xD, 1).ebx; 218 | }, 219 | else => {}, 220 | } 221 | } else { 222 | fpu_storage_size = 512; 223 | fpu_storage_align = 16; 224 | fpu_mode = .fxsave; 225 | } 226 | 227 | if (smp.isBsp()) { 228 | sink.info( 229 | "using \"{s}\" instruction (with size={}) for FPU context management", 230 | .{ @tagName(fpu_mode), fpu_storage_size }, 231 | ); 232 | } 233 | } 234 | 235 | pub fn init() void { 236 | // print CPU name and FPU information 237 | printBrandName(); 238 | setupFpu(); 239 | 240 | // set the CPU to a acceptable state 241 | arch.wrcr0((arch.rdcr0() & ~@as(u64, 1 << 2)) | 0b10); 242 | arch.wrcr4(arch.rdcr4() | (1 << 7)); 243 | 244 | // enable pkeys (if supported) 245 | if (arch.cpuid(7, 0).ecx & (1 << 3) != 0) { 246 | arch.wrcr4(arch.rdcr4() | (1 << 22)); 247 | } 248 | 249 | // enable umip (if supported) 250 | if (arch.cpuid(7, 0).ecx & (1 << 2) != 0) { 251 | arch.wrcr4(arch.rdcr4() | (1 << 11)); 252 | } 253 | 254 | // enable syscall 255 | arch.wrmsr(0xC0000081, (@as(u64, 0x30 | 0b11) << 48) | ((@as(u64, 0x28) << 32))); 256 | arch.wrmsr(0xC0000082, @ptrToInt(&syscallEntry)); 257 | arch.wrmsr(0xC0000080, arch.rdmsr(0xC0000080) | 1); 258 | arch.wrmsr(0xC0000084, ~@as(u32, 2)); 259 | } 260 | 261 | fn syscallEntry() callconv(.Naked) void { 262 | // zig fmt: off 263 | asm volatile ( 264 | // perform a swapgs and switch to the kernel stack 265 | \\swapgs 266 | \\movq %rsp, %%gs:16 267 | \\movq %%gs:28, %rsp 268 | \\sti 269 | 270 | // create a fake trapframe header 271 | \\pushq $0x38 272 | \\pushq %%gs:16 273 | \\pushq %r11 274 | \\pushq $0x40 275 | \\pushq %rcx 276 | \\pushq $0 277 | \\pushq $0 278 | 279 | // push remaining registers 280 | \\push %r15 281 | \\push %r14 282 | \\push %r13 283 | \\push %r12 284 | \\push %r11 285 | \\push %r10 286 | \\push %r9 287 | \\push %r8 288 | \\push %rbp 289 | \\push %rdi 290 | \\push %rsi 291 | \\push %rdx 292 | \\push %rcx 293 | \\push %rbx 294 | \\push %rax 295 | \\cld 296 | 297 | // call the syscall handler 298 | \\mov %rsp, %rdi 299 | \\xor %rbp, %rbp 300 | \\call handleSyscall 301 | 302 | // pop the trapframe back into place 303 | \\pop %rax 304 | \\pop %rbx 305 | \\pop %rcx 306 | \\pop %rdx 307 | \\pop %rsi 308 | \\pop %rdi 309 | \\pop %rbp 310 | \\pop %r8 311 | \\pop %r9 312 | \\pop %r10 313 | \\pop %r11 314 | \\pop %r12 315 | \\pop %r13 316 | \\pop %r14 317 | \\pop %r15 318 | \\add $16, %rsp 319 | 320 | // restore the context back to place 321 | \\cli 322 | \\mov %rsp, %%gs:16 323 | \\swapgs 324 | \\sysretq 325 | ); 326 | // zig fmt: on 327 | } 328 | -------------------------------------------------------------------------------- /kernel/src/x86_64/lapic.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const acpi = @import("root").acpi; 3 | const vmm = @import("root").vmm; 4 | const smp = @import("root").smp; 5 | const arch = @import("root").arch; 6 | const sink = std.log.scoped(.apic); 7 | 8 | const TimerMode = enum(u4) { 9 | tsc, 10 | lapic, 11 | unknown, 12 | }; 13 | 14 | pub const LapicController = struct { 15 | mmio_base: u64 = 0xFFFF8000FEE00000, 16 | tsc_mode: TimerMode = .unknown, 17 | 18 | // general regs 19 | const REG_VER = 0x30; 20 | const REG_EOI = 0xB0; 21 | const REG_SPURIOUS = 0xF0; 22 | 23 | // timer regs 24 | const REG_TIMER_LVT = 0x320; 25 | const REG_TIMER_INIT = 0x380; 26 | const REG_TIMER_CNT = 0x390; 27 | const REG_TIMER_DIV = 0x3E0; 28 | 29 | pub fn setup(self: *LapicController) void { 30 | var mmio_base = vmm.toHigherHalf(arch.rdmsr(0x1B) & 0xFFFFF000); 31 | if (mmio_base != self.mmio_base) { 32 | sink.warn("mmio base 0x{X:0>16} is not the x86 default!", .{mmio_base}); 33 | self.mmio_base = mmio_base; 34 | } 35 | 36 | // map the APIC as UC 37 | var aligned_base: u64 = std.mem.alignBackward(mmio_base, 0x200000); 38 | var map_flags = vmm.MapFlags{ .read = true, .write = true, .cache_type = .uncached }; 39 | vmm.kernel_pagemap.unmapPage(aligned_base); 40 | vmm.kernel_pagemap.mapPage(map_flags, aligned_base, vmm.fromHigherHalf(aligned_base), true); 41 | 42 | // enable the APIC 43 | self.enable(); 44 | 45 | // print TSC frequency (if we're using it) 46 | if (self.canUseTsc()) { 47 | var n = smp.getCoreInfo().ticks_per_ms / 1000; 48 | var d4 = (n % 10); 49 | var d3 = (n / 10) % 10; 50 | var d2 = (n / 100) % 10; 51 | var d1 = (n / 1000); 52 | 53 | sink.info("lapic: CPU frequency is {}.{}{}{} GHz", .{ d1, d2, d3, d4 }); 54 | } 55 | } 56 | 57 | pub fn read(self: *LapicController, reg: u32) u32 { 58 | return @intToPtr(*volatile u32, self.mmio_base + reg).*; 59 | } 60 | 61 | pub fn write(self: *LapicController, reg: u32, value: u32) void { 62 | @intToPtr(*volatile u32, self.mmio_base + reg).* = value; 63 | } 64 | 65 | inline fn canUseTsc(self: *LapicController) bool { 66 | if (self.tsc_mode == .lapic) { 67 | return false; 68 | } else if (self.tsc_mode == .tsc) { 69 | return true; 70 | } else { 71 | if (arch.cpuid(0x1, 0).ecx & (1 << 24) == 0 and 72 | arch.cpuid(0x80000007, 0).edx & (1 << 8) == 0) 73 | { 74 | self.tsc_mode = .tsc; 75 | return true; 76 | } else { 77 | self.tsc_mode = .lapic; 78 | return false; 79 | } 80 | } 81 | } 82 | 83 | pub fn enable(self: *LapicController) void { 84 | // enable the APIC 85 | arch.wrmsr(0x1B, arch.rdmsr(0x1B) | (1 << 11)); 86 | self.write(REG_SPURIOUS, self.read(REG_SPURIOUS) | (1 << 8) | 0xFF); 87 | 88 | if (self.canUseTsc()) { 89 | var initial = arch.rdtsc(); 90 | 91 | // since AMD requires a "mfence" instruction to serialize the 92 | // TSC, and Intel requires a "lfence", use both here (not a big 93 | // deal since this is the only place where we need a serializing TSC) 94 | asm volatile ("mfence; lfence" ::: "memory"); 95 | 96 | acpi.pmSleep(1000); 97 | var final = arch.rdtsc(); 98 | asm volatile ("mfence; lfence" ::: "memory"); 99 | 100 | smp.getCoreInfo().ticks_per_ms = final - initial; 101 | } else { 102 | // on certain platforms (simics and some KVM machines), the 103 | // timer starts counting as soon as the APIC is enabled. 104 | // therefore, we must stop the timer before calibration... 105 | self.write(REG_TIMER_INIT, 0); 106 | 107 | // calibrate the APIC timer (using a 10ms sleep) 108 | self.write(REG_TIMER_DIV, 0x3); 109 | self.write(REG_TIMER_LVT, 0xFF | (1 << 16)); 110 | self.write(REG_TIMER_INIT, std.math.maxInt(u32)); 111 | acpi.pmSleep(1000); 112 | 113 | // set the frequency, then set the timer back to a disabled state 114 | smp.getCoreInfo().ticks_per_ms = std.math.maxInt(u32) - self.read(REG_TIMER_CNT); 115 | self.write(REG_TIMER_INIT, 0); 116 | self.write(REG_TIMER_LVT, (1 << 16)); 117 | } 118 | } 119 | 120 | pub fn submitEoi(self: *LapicController, irq: u8) void { 121 | _ = irq; 122 | self.write(REG_EOI, 0); 123 | } 124 | 125 | pub fn oneshot(self: *LapicController, vec: u8, ms: u64) void { 126 | // stop the timer 127 | self.write(REG_TIMER_INIT, 0); 128 | self.write(REG_TIMER_LVT, (1 << 16)); 129 | 130 | // set the deadline, and off we go! 131 | var deadline = @truncate(u32, smp.getCoreInfo().ticks_per_ms * ms); 132 | 133 | if (self.canUseTsc()) { 134 | self.write(REG_TIMER_LVT, @as(u32, vec) | (1 << 18)); 135 | arch.wrmsr(0x6E0, deadline); 136 | } else { 137 | self.write(REG_TIMER_LVT, vec); 138 | self.write(REG_TIMER_INIT, deadline); 139 | } 140 | } 141 | }; 142 | -------------------------------------------------------------------------------- /kernel/src/x86_64/paging.zig: -------------------------------------------------------------------------------- 1 | const vmm = @import("root").vmm; 2 | const pmm = @import("root").pmm; 3 | const smp = @import("root").smp; 4 | 5 | pub const PageMap = struct { 6 | root: u64 = undefined, 7 | lock: smp.SpinLock = .{}, 8 | 9 | pub fn load(self: *PageMap) void { 10 | loadSpace(self.root); 11 | } 12 | 13 | pub fn save(self: *PageMap) void { 14 | self.root = saveSpace(); 15 | } 16 | 17 | pub fn mapPage(self: *PageMap, flags: vmm.MapFlags, virt: u64, phys: u64, huge: bool) void { 18 | var root: [*]u64 = @intToPtr([*]u64, vmm.toHigherHalf(self.root)); 19 | 20 | self.lock.acq(); 21 | defer self.lock.rel(); 22 | 23 | // zig fmt: off 24 | var indices: [4]u64 = [_]u64{ 25 | genIndex(virt, 39), genIndex(virt, 30), 26 | genIndex(virt, 21), genIndex(virt, 12) 27 | }; 28 | // zig fmt: on 29 | 30 | // perform translation to pte 31 | // TODO(cleanbaja): don't just unwrap (handle the case of a OOM) 32 | root = getNextLevel(root, indices[0], true).?; 33 | root = getNextLevel(root, indices[1], true).?; 34 | 35 | if (huge) { 36 | root[indices[2]] = createPte(flags, phys, true); 37 | } else { 38 | root = getNextLevel(root, indices[2], true).?; 39 | root[indices[3]] = createPte(flags, phys, false); 40 | } 41 | } 42 | 43 | pub fn unmapPage(self: *PageMap, virt: u64) void { 44 | var root: ?[*]u64 = @intToPtr([*]u64, vmm.toHigherHalf(self.root)); 45 | 46 | self.lock.acq(); 47 | defer self.lock.rel(); 48 | 49 | // zig fmt: off 50 | var indices: [4]u64 = [_]u64{ 51 | genIndex(virt, 39), genIndex(virt, 30), 52 | genIndex(virt, 21), genIndex(virt, 12) 53 | }; 54 | // zig fmt: on 55 | 56 | // perform translation to pte 57 | root = getNextLevel(root.?, indices[0], false); 58 | if (root == null) { 59 | return; 60 | } 61 | 62 | root = getNextLevel(root.?, indices[1], false); 63 | if (root == null) { 64 | return; 65 | } 66 | 67 | if ((root.?[indices[2]] & (1 << 7)) != 0) { 68 | root.?[indices[2]] &= ~@intCast(u64, 1); 69 | } else { 70 | if (getNextLevel(root.?, indices[2], false)) |final_root| { 71 | final_root[indices[3]] &= ~@intCast(u64, 1); 72 | } 73 | } 74 | 75 | invalidatePage(virt); 76 | } 77 | }; 78 | 79 | inline fn genIndex(virt: u64, comptime shift: usize) u64 { 80 | return ((virt & (0x1FF << shift)) >> shift); 81 | } 82 | 83 | fn getNextLevel(level: [*]u64, index: usize, create: bool) ?[*]u64 { 84 | if ((level[index] & 1) == 0) { 85 | if (!create) { 86 | return null; 87 | } 88 | 89 | if (pmm.allocPages(1)) |table_ptr| { 90 | level[index] = table_ptr; 91 | level[index] |= 0b111; 92 | } else { 93 | return null; 94 | } 95 | } 96 | 97 | return @intToPtr([*]u64, vmm.toHigherHalf(level[index] & ~@intCast(u64, 0x1ff))); 98 | } 99 | 100 | fn createPte(flags: vmm.MapFlags, phys_ptr: u64, huge: bool) u64 { 101 | var result: u64 = 1; // pages have to be readable to be present 102 | var pat_bit: u64 = blk: { 103 | if (huge) { 104 | break :blk (1 << 12); 105 | } else { 106 | break :blk (1 << 7); 107 | } 108 | }; 109 | 110 | if (flags.write) { 111 | result |= (1 << 1); 112 | } 113 | 114 | if (!(flags.exec)) { 115 | result |= (1 << 63); 116 | } 117 | 118 | if (flags.user) { 119 | result |= (1 << 2); 120 | } 121 | 122 | if (huge) { 123 | result |= (1 << 7); 124 | } 125 | 126 | switch (flags.cache_type) { 127 | .uncached => { 128 | result |= (1 << 4) | (1 << 3); 129 | result &= ~pat_bit; 130 | }, 131 | .write_combining => { 132 | result |= pat_bit | (1 << 4) | (1 << 3); 133 | }, 134 | .write_protect => { 135 | result |= pat_bit | (1 << 4); 136 | result &= ~@intCast(u64, (1 << 3)); 137 | }, 138 | else => {}, 139 | } 140 | 141 | result |= phys_ptr; 142 | return result; 143 | } 144 | 145 | pub inline fn invalidatePage(addr: u64) void { 146 | asm volatile ("invlpg (%[virt])" 147 | : 148 | : [virt] "r" (addr), 149 | : "memory" 150 | ); 151 | } 152 | 153 | pub inline fn loadSpace(ptr: usize) void { 154 | asm volatile ("mov %[root], %%cr3" 155 | : 156 | : [root] "r" (ptr), 157 | : "memory" 158 | ); 159 | } 160 | pub inline fn saveSpace() usize { 161 | return asm volatile ("mov %%cr3, %[old_cr3]" 162 | : [old_cr3] "=r" (-> u64), 163 | : 164 | : "memory" 165 | ); 166 | } 167 | -------------------------------------------------------------------------------- /kernel/src/x86_64/trap.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const sched = @import("root").sched; 3 | const arch = @import("root").arch; 4 | 5 | const TrapStub = *const fn () callconv(.Naked) void; 6 | const TrapHandler = *const fn (*TrapFrame) callconv(.C) void; 7 | const log = std.log.scoped(.trap).err; 8 | 9 | export var handlers = [_]TrapHandler{handleException} ** 32 ++ [_]TrapHandler{handleIrq} ** 224; 10 | var entries: [256]Entry = undefined; 11 | var entries_generated: bool = false; 12 | 13 | pub const TrapFrame = extern struct { 14 | rax: u64, 15 | rbx: u64, 16 | rcx: u64, 17 | rdx: u64, 18 | rsi: u64, 19 | rdi: u64, 20 | rbp: u64, 21 | r8: u64, 22 | r9: u64, 23 | r10: u64, 24 | r11: u64, 25 | r12: u64, 26 | r13: u64, 27 | r14: u64, 28 | r15: u64, 29 | vec: u64, 30 | error_code: u64, 31 | rip: u64, 32 | cs: u64, 33 | rflags: u64, 34 | rsp: u64, 35 | ss: u64, 36 | 37 | pub fn dump(self: TrapFrame, log_func: anytype) void { 38 | log_func("RAX: {X:0>16} - RBX: {X:0>16} - RCX: {X:0>16}", .{ self.rax, self.rbx, self.rcx }); 39 | log_func("RDX: {X:0>16} - RDI: {X:0>16} - RSI: {X:0>16}", .{ self.rdx, self.rdi, self.rsi }); 40 | log_func("RBP: {X:0>16} - R8: {X:0>16} - R9: {X:0>16}", .{ self.rbp, self.r8, self.r9 }); 41 | log_func("R10: {X:0>16} - R11: {X:0>16} - R12: {X:0>16}", .{ self.r10, self.r11, self.r12 }); 42 | log_func("R13: {X:0>16} - R14: {X:0>16} - R15: {X:0>16}", .{ self.r13, self.r14, self.r15 }); 43 | log_func("RSP: {X:0>16} - RIP: {X:0>16} - CS: {X:0>16}", .{ self.rsp, self.rip, self.cs }); 44 | 45 | var cr2 = asm volatile ("mov %%cr2, %[out]" 46 | : [out] "=r" (-> u64), 47 | : 48 | : "memory" 49 | ); 50 | log_func("Linear Address: 0x{X:0>16}, EC bits: 0x{X:0>8}", .{ cr2, self.error_code }); 51 | } 52 | }; 53 | 54 | const Entry = packed struct { 55 | offset_low: u16, 56 | selector: u16, 57 | ist: u8, 58 | flags: u8, 59 | offset_mid: u16, 60 | offset_high: u32, 61 | reserved: u32 = 0, 62 | 63 | fn fromPtr(ptr: u64, ist: u8) Entry { 64 | return Entry{ 65 | .offset_low = @truncate(u16, ptr), 66 | .selector = 0x28, 67 | .ist = ist, 68 | .flags = 0x8e, 69 | .offset_mid = @truncate(u16, ptr >> 16), 70 | .offset_high = @truncate(u32, ptr >> 32), 71 | }; 72 | } 73 | }; 74 | 75 | pub fn setHandler(func: anytype, vec: u8) void { 76 | handlers[vec] = func; 77 | } 78 | 79 | pub fn load() void { 80 | const idtr = arch.Descriptor{ 81 | .size = @as(u16, (@sizeOf(Entry) * 256) - 1), 82 | .ptr = @ptrToInt(&entries), 83 | }; 84 | 85 | if (!entries_generated) { 86 | for (genStubTable()) |stub, idx| { 87 | if (idx == sched.TIMER_VECTOR) { 88 | entries[idx] = Entry.fromPtr(@as(u64, @ptrToInt(stub)), 1); 89 | } else { 90 | entries[idx] = Entry.fromPtr(@as(u64, @ptrToInt(stub)), 0); 91 | } 92 | } 93 | 94 | entries_generated = true; 95 | } 96 | 97 | asm volatile ("lidt %[idtr]" 98 | : 99 | : [idtr] "*p" (&idtr), 100 | ); 101 | } 102 | 103 | fn handleIrq(frame: *TrapFrame) callconv(.C) void { 104 | log("CPU triggered IRQ #{}, which has no handler!", .{frame.vec}); 105 | 106 | while (true) { 107 | asm volatile ("hlt"); 108 | } 109 | } 110 | 111 | fn handleException(frame: *TrapFrame) callconv(.C) void { 112 | log("CPU Exception #{}: ", .{frame.vec}); 113 | frame.dump(log); 114 | 115 | while (true) { 116 | asm volatile ("hlt"); 117 | } 118 | } 119 | 120 | fn genStubTable() [256]TrapStub { 121 | var result = [1]TrapStub{undefined} ** 256; 122 | 123 | comptime var i: usize = 0; 124 | 125 | inline while (i < 256) : (i += 1) { 126 | result[i] = comptime makeStub(i); 127 | } 128 | 129 | return result; 130 | } 131 | 132 | fn makeStub(comptime vec: u8) TrapStub { 133 | return struct { 134 | fn stub() callconv(.Naked) void { 135 | const has_ec = switch (vec) { 136 | 0x8 => true, 137 | 0xA...0xE => true, 138 | 0x11 => true, 139 | 0x15 => true, 140 | 0x1D...0x1E => true, 141 | else => false, 142 | }; 143 | 144 | if (!comptime (has_ec)) { 145 | asm volatile ("push $0"); 146 | } 147 | 148 | asm volatile ("push %[vec]" 149 | : 150 | : [vec] "i" (vec), 151 | ); 152 | 153 | // zig fmt: off 154 | asm volatile ( 155 | // perform a swapgs (if we came from usermode) 156 | \\cmpq $0x3b, 16(%rsp) 157 | \\jne 1f 158 | \\swapgs 159 | 160 | // push the trapframe 161 | \\1: 162 | \\push %r15 163 | \\push %r14 164 | \\push %r13 165 | \\push %r12 166 | \\push %r11 167 | \\push %r10 168 | \\push %r9 169 | \\push %r8 170 | \\push %rbp 171 | \\push %rdi 172 | \\push %rsi 173 | \\push %rdx 174 | \\push %rcx 175 | \\push %rbx 176 | \\push %rax 177 | \\cld 178 | 179 | // setup C enviroment and index into the handler 180 | \\lea handlers(%rip), %rbx 181 | \\add %[vec_off], %rbx 182 | \\mov %rsp, %rdi 183 | \\xor %rbp, %rbp 184 | \\call *(%rbx) 185 | 186 | // pop the trapframe back into place 187 | \\pop %rax 188 | \\pop %rbx 189 | \\pop %rcx 190 | \\pop %rdx 191 | \\pop %rsi 192 | \\pop %rdi 193 | \\pop %rbp 194 | \\pop %r8 195 | \\pop %r9 196 | \\pop %r10 197 | \\pop %r11 198 | \\pop %r12 199 | \\pop %r13 200 | \\pop %r14 201 | \\pop %r15 202 | \\add $16, %rsp 203 | 204 | // swap back to user gs (if needed) 205 | \\cmpq $0x3b, 8(%rsp) 206 | \\jne 1f 207 | \\swapgs 208 | 209 | // and away we go :-) 210 | \\1: 211 | \\iretq 212 | : 213 | : [vec_off] "i" (@as(u64, vec) * 8), 214 | ); 215 | } 216 | }.stub; 217 | // zig fmt: on 218 | } 219 | -------------------------------------------------------------------------------- /limine.cfg: -------------------------------------------------------------------------------- 1 | TIMEOUT=3 2 | 3 | :Munix 4 | 5 | PROTOCOL=limine 6 | KERNEL_PATH=boot:///kernel 7 | MODULE_PATH=$boot:///initrd.img 8 | 9 | :Munix (lite) 10 | 11 | PROTOCOL=limine 12 | KERNEL_PATH=boot:///kernel 13 | -------------------------------------------------------------------------------- /user/Makefile: -------------------------------------------------------------------------------- 1 | .POSIX: 2 | .PHONY: distro-base 3 | 4 | BUILD_DIR = build 5 | 6 | all: initrd.img 7 | 8 | $(BUILD_DIR): 9 | mkdir -p $(BUILD_DIR) 10 | cd $(BUILD_DIR) && xbstrap init .. 11 | 12 | distro-base: $(BUILD_DIR) 13 | cd $(BUILD_DIR) && xbstrap install --all 14 | 15 | initrd.img: distro-base 16 | cd $(BUILD_DIR)/system-root/ && find . | cpio -F ../initrd.cpio -oH newc 17 | gzip $(BUILD_DIR)/initrd.cpio && mv $(BUILD_DIR)/initrd.cpio.gz initrd.img 18 | -------------------------------------------------------------------------------- /user/bootstrap.yml: -------------------------------------------------------------------------------- 1 | sources: 2 | - name: autoconf-v2.69 3 | subdir: bundled 4 | url: "https://ftp.gnu.org/gnu/autoconf/autoconf-2.69.tar.xz" 5 | format: "tar.xz" 6 | extract_path: "autoconf-2.69" 7 | patch-path-strip: 3 8 | version: "2.69" 9 | 10 | - name: automake-v1.16 11 | subdir: bundled 12 | git: "https://git.savannah.gnu.org/git/automake.git" 13 | tag: "v1.16.5" 14 | version: "1.16.5" 15 | tools_required: 16 | - host-autoconf-v2.69 17 | regenerate: 18 | - args: ["./bootstrap"] 19 | 20 | - name: libtool 21 | subdir: 'bundled' 22 | git: 'https://git.savannah.gnu.org/git/libtool.git' 23 | tag: 'v2.4.6' 24 | version: '2.4.6' 25 | tools_required: 26 | - host-autoconf-v2.69 27 | - host-automake-v1.16 28 | regenerate: 29 | # libtool's ./bootstrap does a shallow clone with insufficient depth. 30 | - args: ['git', 'submodule', 'update', '--init'] 31 | - args: ['./bootstrap'] 32 | 33 | - name: binutils 34 | subdir: bundled 35 | git: "https://github.com/bminor/binutils-gdb.git" 36 | tag: "binutils-2_38" 37 | version: "2.38" 38 | tools_required: 39 | - host-automake-v1.16 40 | 41 | - name: gcc 42 | subdir: 'bundled' 43 | git: 'https://github.com/gcc-mirror/gcc.git' 44 | tag: 'releases/gcc-12.1.0' 45 | version: '12.1.0' 46 | patch-path-strip: 1 47 | tools_required: 48 | - host-autoconf-v2.69 49 | - host-automake-v1.16 50 | regenerate: 51 | # download_prerequisites should probably move to some "post_checkout" step. 52 | - args: ['./contrib/download_prerequisites'] 53 | workdir: '@THIS_SOURCE_DIR@' 54 | - args: ['autoconf'] 55 | workdir: '@THIS_SOURCE_DIR@/gcc' 56 | - args: ['autoconf'] 57 | workdir: '@THIS_SOURCE_DIR@/libstdc++-v3' 58 | - args: ['cp', 59 | '@BUILD_ROOT@/tools/host-automake-v1.16/share/automake-1.16/config.sub', 60 | '@THIS_SOURCE_DIR@/'] 61 | - args: ['cp', 62 | '@BUILD_ROOT@/tools/host-automake-v1.16/share/automake-1.16/config.sub', 63 | '@THIS_SOURCE_DIR@/gmp-6.2.1/configfsf.sub'] 64 | - args: ['cp', 65 | '@BUILD_ROOT@/tools/host-automake-v1.16/share/automake-1.16/config.sub', 66 | '@THIS_SOURCE_DIR@/isl-0.24/config.sub'] 67 | - args: ['cp', '-f', 68 | '@BUILD_ROOT@/tools/host-automake-v1.16/share/automake-1.16/config.sub', 69 | '@THIS_SOURCE_DIR@/mpc-1.2.1/config.sub'] 70 | - args: ['cp', 71 | '@BUILD_ROOT@/tools/host-automake-v1.16/share/automake-1.16/config.sub', 72 | '@THIS_SOURCE_DIR@/mpfr-4.1.0/config.sub'] 73 | 74 | - name: musl 75 | subdir: ports 76 | git: "https://github.com/cleanbaja/musl.git" 77 | branch: "master" 78 | version: "1.2.3-munix" 79 | 80 | - name: oksh 81 | subdir: ports 82 | git: "https://github.com/ibara/oksh.git" 83 | tag: "oksh-7.2" 84 | version: "7.2" 85 | 86 | declare_options: 87 | - name: arch 88 | default: x86_64 89 | - name: arch-triple 90 | default: x86_64-munix 91 | 92 | tools: 93 | - name: host-autoconf-v2.69 94 | architecture: noarch 95 | from_source: autoconf-v2.69 96 | configure: 97 | - args: ["@THIS_SOURCE_DIR@/configure", "--prefix=@PREFIX@"] 98 | compile: 99 | - args: ["make", "-j@PARALLELISM@"] 100 | install: 101 | - args: ["make", "install"] 102 | 103 | - name: host-automake-v1.16 104 | architecture: noarch 105 | from_source: automake-v1.16 106 | tools_required: 107 | - host-autoconf-v2.69 108 | configure: 109 | - args: ["@THIS_SOURCE_DIR@/configure", "--prefix=@PREFIX@"] 110 | compile: 111 | - args: | 112 | set -e 113 | export PATH="`pwd`/bin:$PATH" 114 | make bin/aclocal-1.16 bin/automake-1.16 -j@PARALLELISM@ 115 | make -j@PARALLELISM@ 116 | install: 117 | - args: ["make", "install-strip"] 118 | - args: ["ln", "-sf", "@PREFIX@/share/aclocal-1.16", "@PREFIX@/share/aclocal"] 119 | 120 | - name: host-libtool 121 | architecture: noarch 122 | exports_aclocal: true 123 | from_source: libtool 124 | tools_required: 125 | - host-autoconf-v2.69 126 | - host-automake-v1.16 127 | configure: 128 | - args: ["@THIS_SOURCE_DIR@/configure", "--prefix=@PREFIX@"] 129 | compile: 130 | - args: ["make", "-j@PARALLELISM@"] 131 | install: 132 | - args: ["make", "install"] 133 | 134 | - name: host-binutils 135 | architecture: '@OPTION:arch@' 136 | from_source: binutils 137 | tools_required: 138 | - host-automake-v1.16 139 | configure: 140 | - args: 141 | - "@THIS_SOURCE_DIR@/configure" 142 | - "--prefix=@PREFIX@" 143 | - "--with-sysroot=@SYSROOT_DIR@" 144 | - "--target=@OPTION:arch-triple@" 145 | - "--disable-nls" 146 | - "--disable-werror" 147 | compile: 148 | - args: ["make", "-j@PARALLELISM@", 'all-binutils', 'all-gas', 'all-ld'] 149 | install: 150 | - args: ["make", 'install-binutils', 'install-gas', 'install-ld'] 151 | 152 | - name: bootstrap-host-gcc 153 | architecture: '@OPTION:arch@' 154 | from_source: gcc 155 | tools_required: 156 | - tool: host-binutils 157 | recursive: true 158 | configure: 159 | - args: 160 | - '@THIS_SOURCE_DIR@/configure' 161 | - '--prefix=@PREFIX@' 162 | - '--target=@OPTION:arch-triple@' 163 | - '--with-sysroot=@SYSROOT_DIR@' 164 | - '--enable-languages=c,c++' 165 | - '--disable-multilib' 166 | - '--disable-shared' 167 | - '--enable-initfini-array' 168 | # -g blows up GCC's binary size. 169 | - 'CFLAGS=-O2 -pipe' 170 | - 'CXXFLAGS=-O2 -pipe' 171 | stages: 172 | - name: compiler 173 | pkgs_required: 174 | - musl-headers 175 | compile: 176 | # GCC complains if the include directory is non-existant. 177 | - args: ['mkdir', '-p', '@SYSROOT_DIR@/usr/include'] 178 | - args: ['make', '-j@PARALLELISM@', 'inhibit_libc=true', 'all-gcc'] 179 | install: 180 | - args: ['make', 'install-gcc'] 181 | # GCC does *not* look for target-prefixed LD/AS. 182 | # Instead, it searches a list of prefix directories. Link AS/LD to make it happy. 183 | - args: ['mkdir', '-p', '@PREFIX@/@OPTION:arch-triple@/bin'] 184 | - args: ['ln', '-sf', '../../../host-binutils/@OPTION:arch-triple@/bin/as', 185 | '@PREFIX@/@OPTION:arch-triple@/bin/as'] 186 | - args: ['ln', '-sf', '../../../host-binutils/@OPTION:arch-triple@/bin/ld', 187 | '@PREFIX@/@OPTION:arch-triple@/bin/ld'] 188 | - name: libgcc 189 | tools_required: 190 | - tool: bootstrap-host-gcc 191 | stage_dependencies: [compiler] 192 | compile: 193 | - args: ['make', '-j@PARALLELISM@', 'inhibit_libc=true', 'all-target-libgcc'] 194 | install: 195 | - args: ['make', 'install-strip-target-libgcc'] 196 | 197 | - name: host-gcc 198 | architecture: '@OPTION:arch@' 199 | from_source: gcc 200 | tools_required: 201 | - tool: host-binutils 202 | recursive: true 203 | revision: 2 204 | configure: 205 | - args: 206 | - '@THIS_SOURCE_DIR@/configure' 207 | - '--prefix=@PREFIX@' 208 | - '--target=@OPTION:arch-triple@' 209 | - '--with-sysroot=@SYSROOT_DIR@' 210 | - '--enable-languages=c,c++' 211 | - '--disable-multilib' 212 | - '--enable-initfini-array' 213 | - '--enable-libstdcxx-filesystem-ts' 214 | # -g blows up GCC's binary size. 215 | - 'CFLAGS=-O2' 216 | - 'CXXFLAGS=-O2' 217 | stages: 218 | - name: compiler 219 | pkgs_required: 220 | - musl-headers 221 | compile: 222 | # GCC complains if the include directory is non-existant. 223 | - args: ['mkdir', '-p', '@SYSROOT_DIR@/usr/include'] 224 | - args: ['make', '-j@PARALLELISM@', 'all-gcc'] 225 | install: 226 | - args: ['make', 'install-gcc'] 227 | # GCC does *not* look for target-prefixed LD/AS. 228 | # Instead, it searches a list of prefix directories. Link AS/LD to make it happy. 229 | - args: ['mkdir', '-p', '@PREFIX@/@OPTION:arch-triple@/bin'] 230 | - args: ['ln', '-sf', '../../../host-binutils/@OPTION:arch-triple@/bin/as', 231 | '@PREFIX@/@OPTION:arch-triple@/bin/as'] 232 | - args: ['ln', '-sf', '../../../host-binutils/@OPTION:arch-triple@/bin/ld', 233 | '@PREFIX@/@OPTION:arch-triple@/bin/ld'] 234 | - name: libgcc 235 | tools_required: 236 | - tool: host-gcc 237 | stage_dependencies: [compiler] 238 | pkgs_required: 239 | - musl 240 | compile: 241 | - args: ['make', '-j@PARALLELISM@', 'all-target-libgcc'] 242 | install: 243 | - args: ['make', 'install-target-libgcc'] 244 | 245 | - name: libstdc++ 246 | tools_required: 247 | - tool: host-gcc 248 | stage_dependencies: [libgcc] 249 | compile: 250 | - args: ['make', '-j@PARALLELISM@', 'all-target-libstdc++-v3'] 251 | install: 252 | - args: ['make', 'install-target-libstdc++-v3'] 253 | 254 | packages: 255 | - name: musl 256 | from_source: musl 257 | implict_package: true 258 | tools_required: 259 | - bootstrap-host-gcc 260 | pkgs_required: 261 | - musl-headers 262 | configure: 263 | - args: ['@THIS_SOURCE_DIR@/configure', '--prefix=/usr', '--target=@OPTION:arch-triple@'] 264 | build: 265 | - args: ['make', '-j@PARALLELISM@'] 266 | - args: ['make', 'install'] 267 | environ: 268 | DESTDIR: '@THIS_COLLECT_DIR@' 269 | quiet: true 270 | - args: ['cp', '@THIS_COLLECT_DIR@/usr/lib/crt1.o', '@THIS_COLLECT_DIR@/usr/lib/crt0.o'] 271 | 272 | - name: musl-headers 273 | from_source: musl 274 | implict_package: true 275 | configure: 276 | # leave out the target, so musl doesn't try to use the target gcc (which isn't built yet) 277 | - args: ['@THIS_SOURCE_DIR@/configure', '--prefix=/usr'] 278 | build: 279 | - args: ['make', 'install-headers'] 280 | environ: 281 | DESTDIR: '@THIS_COLLECT_DIR@' 282 | quiet: true 283 | 284 | - name: oksh 285 | from_source: oksh 286 | tools_required: 287 | - host-gcc 288 | configure: 289 | - args: ['cp', '@THIS_SOURCE_DIR@/oksh.1', '.'] 290 | - args: ['@THIS_SOURCE_DIR@/configure', '--disable-curses', '--prefix=/usr', '--no-thanks'] 291 | environ: 292 | CC: '@OPTION:arch-triple@-gcc' 293 | build: 294 | - args: ['make', '-j@PARALLELISM@'] 295 | - args: ['make', 'install'] 296 | environ: 297 | DESTDIR: '@THIS_COLLECT_DIR@' 298 | quiet: true 299 | 300 | - name: util-munix 301 | source: 302 | subdir: '' 303 | tools_required: 304 | - host-gcc 305 | build: 306 | - args: | 307 | set -e 308 | cd "@THIS_SOURCE_DIR@" 309 | make CC=@OPTION:arch-triple@-gcc LD=x86_64-lyre-ld 310 | make install PREFIX=/boot DESTDIR="@THIS_COLLECT_DIR@" 311 | 312 | -------------------------------------------------------------------------------- /user/patches/automake-v1.16/0001-update-config-sub.patch: -------------------------------------------------------------------------------- 1 | From 796028567d8dadf794c6ba90809e58dde2d63d7a Mon Sep 17 00:00:00 2001 2 | From: cleanbaja 3 | Date: Thu, 5 Jan 2023 20:35:27 +0000 4 | Subject: [PATCH] update config.sub to recognize munix 5 | 6 | --- 7 | lib/config.sub | 2 +- 8 | 1 file changed, 1 insertion(+), 1 deletion(-) 9 | 10 | diff --git a/lib/config.sub b/lib/config.sub 11 | index d74fb6d..508e9fa 100755 12 | --- a/lib/config.sub 13 | +++ b/lib/config.sub 14 | @@ -1724,7 +1724,7 @@ case $os in 15 | | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ 16 | | hiux* | abug | nacl* | netware* | windows* \ 17 | | os9* | macos* | osx* | ios* \ 18 | - | mpw* | magic* | mmixware* | mon960* | lnews* \ 19 | + | mpw* | magic* | mmixware* | mon960* | lnews* | munix* \ 20 | | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ 21 | | aos* | aros* | cloudabi* | sortix* | twizzler* \ 22 | | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \ 23 | -- 24 | 2.36.3 25 | 26 | -------------------------------------------------------------------------------- /user/patches/binutils/0001-implement-support-for-munix.patch: -------------------------------------------------------------------------------- 1 | From ecaf58f045acca8cdc685636c456c129be71d757 Mon Sep 17 00:00:00 2001 2 | From: cleanbaja 3 | Date: Thu, 5 Jan 2023 19:58:52 +0000 4 | Subject: [PATCH] implement support for munix 5 | 6 | --- 7 | bfd/config.bfd | 5 +++++ 8 | config.sub | 2 +- 9 | gas/configure.tgt | 1 + 10 | ld/configure.tgt | 4 ++++ 11 | 4 files changed, 11 insertions(+), 1 deletion(-) 12 | 13 | diff --git a/bfd/config.bfd b/bfd/config.bfd 14 | index cfe58247..94a32790 100644 15 | --- a/bfd/config.bfd 16 | +++ b/bfd/config.bfd 17 | @@ -696,6 +696,11 @@ case "${targ}" in 18 | targ_selvecs="i386_elf32_vec" 19 | want64=true 20 | ;; 21 | + x86_64-*-munix*) 22 | + targ_defvec=x86_64_elf64_vec 23 | + targ_selvecs="i386_elf32_vec" 24 | + want64=true 25 | + ;; 26 | x86_64-*-netbsd* | x86_64-*-openbsd*) 27 | targ_defvec=x86_64_elf64_vec 28 | targ_selvecs="i386_elf32_vec iamcu_elf32_vec i386_coff_vec i386_pei_vec x86_64_pe_vec x86_64_pei_vec l1om_elf64_vec k1om_elf64_vec" 29 | diff --git a/config.sub b/config.sub 30 | index dba16e84..5fa2daf1 100755 31 | --- a/config.sub 32 | +++ b/config.sub 33 | @@ -1729,7 +1729,7 @@ case $os in 34 | | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ 35 | | hiux* | abug | nacl* | netware* | windows* \ 36 | | os9* | macos* | osx* | ios* \ 37 | - | mpw* | magic* | mmixware* | mon960* | lnews* \ 38 | + | mpw* | magic* | mmixware* | mon960* | lnews* | munix* \ 39 | | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ 40 | | aos* | aros* | cloudabi* | sortix* | twizzler* \ 41 | | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \ 42 | diff --git a/gas/configure.tgt b/gas/configure.tgt 43 | index 62f806bd..3d62b8bf 100644 44 | --- a/gas/configure.tgt 45 | +++ b/gas/configure.tgt 46 | @@ -227,6 +227,7 @@ case ${generic_target} in 47 | i386-*-elf*) fmt=elf ;; 48 | i386-*-fuchsia*) fmt=elf ;; 49 | i386-*-haiku*) fmt=elf em=haiku ;; 50 | + i386-*-munix*) fmt=elf ;; 51 | i386-*-genode*) fmt=elf ;; 52 | i386-*-bsd*) fmt=aout em=386bsd ;; 53 | i386-*-netbsd*-gnu* | \ 54 | diff --git a/ld/configure.tgt b/ld/configure.tgt 55 | index c7acf3f1..425c0c5b 100644 56 | --- a/ld/configure.tgt 57 | +++ b/ld/configure.tgt 58 | @@ -329,6 +329,10 @@ i[3-7]86-*-linux-*) targ_emul=elf_i386 59 | targ64_extra_emuls="elf_x86_64 elf32_x86_64 elf_l1om elf_k1om" 60 | targ64_extra_libpath="elf_x86_64 elf32_x86_64" 61 | ;; 62 | +x86_64-*-munix*) 63 | + targ_emul=elf_x86_64 64 | + targ_extra_emuls=elf_i386 65 | + ;; 66 | i[3-7]86-*-redox*) targ_emul=elf_i386 67 | targ_extra_emuls=elf_x86_64 68 | ;; 69 | -- 70 | 2.36.3 71 | 72 | -------------------------------------------------------------------------------- /user/patches/gcc/0001-create-munix-target.patch: -------------------------------------------------------------------------------- 1 | From e78f789db9333f6e46cd6f0cafc297205116c154 Mon Sep 17 00:00:00 2001 2 | From: cleanbaja 3 | Date: Wed, 11 Jan 2023 20:48:19 +0000 4 | Subject: [PATCH] Create munix target 5 | 6 | --- 7 | .gitignore | 3 +++ 8 | fixincludes/mkfixinc.sh | 1 + 9 | gcc/config.gcc | 12 ++++++++++++ 10 | gcc/config/munix.h | 29 +++++++++++++++++++++++++++++ 11 | libgcc/config.host | 9 +++++++++ 12 | libgcc/libgcov.h | 2 ++ 13 | libstdc++-v3/crossconfig.m4 | 12 ++++++++++++ 14 | libtool.m4 | 14 ++++++++++++++ 15 | 8 files changed, 82 insertions(+) 16 | create mode 100644 gcc/config/munix.h 17 | 18 | diff --git a/.gitignore b/.gitignore 19 | index 14ee03251..2c3b7f8d7 100644 20 | --- a/.gitignore 21 | +++ b/.gitignore 22 | @@ -62,6 +62,9 @@ REVISION 23 | stamp-* 24 | *.stamp 25 | 26 | +# ignore xbstrap artifacts 27 | +*.xbstrap 28 | + 29 | # ignore in-tree prerequisites 30 | /mpfr* 31 | /mpc* 32 | diff --git a/fixincludes/mkfixinc.sh b/fixincludes/mkfixinc.sh 33 | index df90720b7..df6c35ca8 100755 34 | --- a/fixincludes/mkfixinc.sh 35 | +++ b/fixincludes/mkfixinc.sh 36 | @@ -13,6 +13,7 @@ target=fixinc.sh 37 | case $machine in 38 | i?86-*-cygwin* | \ 39 | i?86-*-mingw32* | \ 40 | + x86_64-*-munix* | \ 41 | x86_64-*-mingw32* | \ 42 | powerpc-*-eabisim* | \ 43 | powerpc-*-eabi* | \ 44 | diff --git a/gcc/config.gcc b/gcc/config.gcc 45 | index c5064dd37..bc794b911 100644 46 | --- a/gcc/config.gcc 47 | +++ b/gcc/config.gcc 48 | @@ -827,6 +827,15 @@ case ${target} in 49 | *-*-fuchsia*) 50 | native_system_header_dir=/include 51 | ;; 52 | +*-*-munix) 53 | + extra_options="$extra_options gnu-user.opt" 54 | + gas=yes 55 | + gnu_ld=yes 56 | + default_use_cxa_atexit=yes 57 | + use_gcc_stdint=wrap 58 | + tmake_file="${tmake_file} t-slibgcc" 59 | + thread_file='posix' 60 | + ;; 61 | *-*-linux* | frv-*-*linux* | *-*-kfreebsd*-gnu | *-*-gnu* | *-*-kopensolaris*-gnu | *-*-uclinuxfdpiceabi) 62 | extra_options="$extra_options gnu-user.opt" 63 | gas=yes 64 | @@ -2246,6 +2255,9 @@ x86_64-*-fuchsia*) 65 | tmake_file="${tmake_file} i386/t-x86_64-elf" 66 | tm_file="${tm_file} i386/unix.h i386/att.h elfos.h newlib-stdint.h i386/i386elf.h i386/x86-64.h fuchsia.h" 67 | ;; 68 | +x86_64-*-munix*) 69 | + tm_file="${tm_file} i386/unix.h i386/att.h dbxelf.h elfos.h gnu-user.h glibc-stdint.h i386/x86-64.h i386/gnu-user-common.h i386/gnu-user64.h munix.h" 70 | + ;; 71 | ia64*-*-elf*) 72 | tm_file="${tm_file} dbxelf.h elfos.h newlib-stdint.h ia64/sysv4.h ia64/elf.h" 73 | tmake_file="ia64/t-ia64" 74 | diff --git a/gcc/config/munix.h b/gcc/config/munix.h 75 | new file mode 100644 76 | index 000000000..f740a7845 77 | --- /dev/null 78 | +++ b/gcc/config/munix.h 79 | @@ -0,0 +1,30 @@ 80 | +#undef TARGET_MUNIX 81 | +#define TARGET_MUNIX 1 82 | + 83 | +#undef LIB_SPEC 84 | +#define LIB_SPEC "-lc" 85 | + 86 | +#undef STARTFILE_SPEC 87 | +#define STARTFILE_SPEC "%{!shared:crt0.o%s} crti.o%s %{shared:crtbeginS.o%s;:crtbegin.o%s}" 88 | + 89 | +#undef ENDFILE_SPEC 90 | +#define ENDFILE_SPEC "%{shared:crtendS.o%s;:crtend.o%s} crtn.o%s" 91 | + 92 | +#define GNU_USER_LINK_EMULATION32 "elf_i386" 93 | +#define GNU_USER_LINK_EMULATION64 "elf_x86_64" 94 | +#define GNU_USER_LINK_EMULATIONX32 "elf32_x86_64" 95 | + 96 | +/* musl puts the dynamic linker within the libc... */ 97 | +#define GNU_USER_DYNAMIC_LINKER32 "/usr/lib/libc.so" 98 | +#define GNU_USER_DYNAMIC_LINKER64 "/usr/lib/libc.so" 99 | +#define GNU_USER_DYNAMIC_LINKERX32 "/usr/lib/libc.so" 100 | + 101 | +#undef TARGET_OS_CPP_BUILTINS 102 | +#define TARGET_OS_CPP_BUILTINS() \ 103 | + do { \ 104 | + builtin_define ("__munix__"); \ 105 | + builtin_define ("__unix__"); \ 106 | + builtin_assert ("system=munix"); \ 107 | + builtin_assert ("system=unix"); \ 108 | + builtin_assert ("system=posix"); \ 109 | + } while (0); 110 | diff --git a/libgcc/config.host b/libgcc/config.host 111 | index 8c56fcae5..caad02bb2 100644 112 | --- a/libgcc/config.host 113 | +++ b/libgcc/config.host 114 | @@ -248,6 +248,11 @@ case ${host} in 115 | tmake_file="$tmake_file t-slibgcc t-slibgcc-gld t-slibgcc-elf-ver" 116 | extra_parts="crtbegin.o crtend.o crtbeginS.o crtendS.o" 117 | ;; 118 | +*-*-munix*) 119 | + extra_parts="$extra_parts crti.o crtbegin.o crtbeginS.o crtend.o crtendS.o crtn.o" 120 | + tmake_file="$tmake_file t-crtstuff-pic" 121 | + tmake_file="$tmake_file t-slibgcc t-slibgcc-gld t-slibgcc-elf-ver t-libgcc-pic" 122 | + ;; 123 | *-*-freebsd*) 124 | # This is the generic ELF configuration of FreeBSD. Later 125 | # machine-specific sections may refine and add to this 126 | @@ -725,6 +730,10 @@ x86_64-*-elf* | x86_64-*-rtems*) 127 | x86_64-*-fuchsia*) 128 | tmake_file="$tmake_file t-libgcc-pic" 129 | ;; 130 | +x86_64-*-munix*) 131 | + extra_parts="$extra_parts crtprec32.o crtprec64.o crtprec80.o crtfastmath.o" 132 | + tmake_file="$tmake_file i386/t-crtpc t-crtfm i386/t-crtstuff t-dfprules" 133 | + ;; 134 | i[34567]86-*-dragonfly*) 135 | tmake_file="${tmake_file} i386/t-dragonfly i386/t-crtstuff" 136 | md_unwind_header=i386/dragonfly-unwind.h 137 | diff --git a/libgcc/libgcov.h b/libgcc/libgcov.h 138 | index 40e845ce3..cd4802bff 100644 139 | --- a/libgcc/libgcov.h 140 | +++ b/libgcc/libgcov.h 141 | @@ -37,6 +37,8 @@ 142 | /* About the target. */ 143 | /* This path will be used by libgcov runtime. */ 144 | 145 | +#include 146 | + 147 | #include "tconfig.h" 148 | #include "auto-target.h" 149 | #include "tsystem.h" 150 | diff --git a/libstdc++-v3/crossconfig.m4 b/libstdc++-v3/crossconfig.m4 151 | index ae5283b7a..07bf8ee93 100644 152 | --- a/libstdc++-v3/crossconfig.m4 153 | +++ b/libstdc++-v3/crossconfig.m4 154 | @@ -141,6 +141,18 @@ case "${host}" in 155 | AC_SUBST(SECTION_FLAGS) 156 | ;; 157 | 158 | + *-munix*) 159 | + GLIBCXX_CHECK_COMPILER_FEATURES 160 | + GLIBCXX_CHECK_LINKER_FEATURES 161 | + GLIBCXX_CHECK_MATH_SUPPORT 162 | + GLIBCXX_CHECK_STDLIB_SUPPORT 163 | + AC_DEFINE(_GLIBCXX_USE_DEV_RANDOM) 164 | + AC_DEFINE(_GLIBCXX_USE_RANDOM_TR1) 165 | + GCC_CHECK_TLS 166 | + AC_CHECK_FUNCS(aligned_alloc posix_memalign memalign _aligned_malloc) 167 | + AC_CHECK_FUNCS(timespec_get) 168 | + ;; 169 | + 170 | *-hpux*) 171 | SECTION_FLAGS='-ffunction-sections -fdata-sections' 172 | AC_SUBST(SECTION_FLAGS) 173 | diff --git a/libtool.m4 b/libtool.m4 174 | index 17f8e5f30..8a179f1ff 100644 175 | --- a/libtool.m4 176 | +++ b/libtool.m4 177 | @@ -2491,6 +2491,16 @@ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu* | uclinuxfdpiceabi) 178 | dynamic_linker='GNU/Linux ld.so' 179 | ;; 180 | 181 | +munix*) 182 | + version_type=linux 183 | + need_lib_prefix=no 184 | + need_version=no 185 | + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' 186 | + soname_spec='${libname}${release}${shared_ext}$major' 187 | + shlibpath_var=LD_LIBRARY_PATH 188 | + hardcode_into_libs=yes 189 | + ;; 190 | + 191 | netbsd*) 192 | version_type=sunos 193 | need_lib_prefix=no 194 | @@ -3090,6 +3100,10 @@ linux* | k*bsd*-gnu | kopensolaris*-gnu | uclinuxfdpiceabi) 195 | lt_cv_deplibs_check_method=pass_all 196 | ;; 197 | 198 | +munix*) 199 | + lt_cv_deplibs_check_method=pass_all 200 | + ;; 201 | + 202 | netbsd*) 203 | if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then 204 | lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' 205 | -- 206 | 2.36.3 207 | 208 | -------------------------------------------------------------------------------- /user/patches/gcc/0002-fixes-for-musl.patch: -------------------------------------------------------------------------------- 1 | From 3ebd8befb8b6f5fbf589d13613f9634fb4689315 Mon Sep 17 00:00:00 2001 2 | From: cleanbaja 3 | Date: Fri, 13 Jan 2023 01:29:40 +0000 4 | Subject: [PATCH] fixes for musl 5 | 6 | --- 7 | gcc/system.h | 3 ++- 8 | libcpp/system.h | 3 ++- 9 | 2 files changed, 4 insertions(+), 2 deletions(-) 10 | 11 | diff --git a/gcc/system.h b/gcc/system.h 12 | index c5562cc49..c607bcf87 100644 13 | --- a/gcc/system.h 14 | +++ b/gcc/system.h 15 | @@ -910,7 +910,8 @@ extern void fancy_abort (const char *, int, const char *) 16 | #undef calloc 17 | #undef strdup 18 | #undef strndup 19 | - #pragma GCC poison calloc strdup strndup 20 | + /* don't poison calloc, since it causes a error within sched.h */ 21 | + #pragma GCC poison strdup strndup 22 | 23 | #if !defined(FLEX_SCANNER) && !defined(YYBISON) 24 | #undef malloc 25 | diff --git a/libcpp/system.h b/libcpp/system.h 26 | index e80cf029d..ddf8cc4ec 100644 27 | --- a/libcpp/system.h 28 | +++ b/libcpp/system.h 29 | @@ -440,7 +440,8 @@ extern void fancy_abort (const char *, int, const char *) ATTRIBUTE_NORETURN; 30 | #undef strdup 31 | #undef malloc 32 | #undef realloc 33 | - #pragma GCC poison calloc strdup 34 | + /* don't poison calloc, since it causes a error within sched.h */ 35 | + #pragma GCC poison strdup 36 | #pragma GCC poison malloc realloc 37 | 38 | /* Libiberty macros that are no longer used in GCC. */ 39 | -- 40 | 2.36.3 41 | 42 | -------------------------------------------------------------------------------- /user/patches/oksh/0001-changes-for-munix.patch: -------------------------------------------------------------------------------- 1 | From ad917851737cad04cd832adbb83fc14d65415b90 Mon Sep 17 00:00:00 2001 2 | From: cleanbaja 3 | Date: Sat, 14 Jan 2023 00:33:27 +0000 4 | Subject: [PATCH] changes for munix 5 | 6 | --- 7 | configure | 5 +++++ 8 | edit.c | 2 ++ 9 | misc.c | 3 ++- 10 | portable.h | 8 ++++---- 11 | sh.h | 1 + 12 | 5 files changed, 14 insertions(+), 5 deletions(-) 13 | 14 | diff --git a/configure b/configure 15 | index 358de20..43f163c 100755 16 | --- a/configure 17 | +++ b/configure 18 | @@ -955,6 +955,11 @@ if [ $doconfigure -eq 0 ] ; then 19 | CC=gcc 20 | fi 21 | cflags="$cflags -std=gnu99" 22 | + 23 | + # munix always cross-compiles oksh 24 | + vpath="$(dirname $0)" 25 | + cflags="$cflags -I$(pwd)" 26 | + 27 | echo "OK, I trust you." 28 | echo "I'll assume you have gcc/clang and little else." 29 | echo "Edit Makefile and pconfig.h if needed." 30 | diff --git a/edit.c b/edit.c 31 | index ca648fc..cc72e83 100644 32 | --- a/edit.c 33 | +++ b/edit.c 34 | @@ -31,6 +31,8 @@ static int x_file_glob(int, const char *, int, char ***); 35 | static int x_command_glob(int, const char *, int, char ***); 36 | static int x_locate_word(const char *, int, int, int *, int *); 37 | 38 | +// musl doesn't define u_char, so do it here... 39 | +#define u_char uint8_t 40 | 41 | /* Called from main */ 42 | void 43 | diff --git a/misc.c b/misc.c 44 | index 428f183..19b5068 100644 45 | --- a/misc.c 46 | +++ b/misc.c 47 | @@ -297,7 +297,8 @@ change_flag(enum sh_flag f, 48 | gid_t gid = getgid(); 49 | 50 | setresgid(gid, gid, gid); 51 | - setgroups(1, &gid); 52 | + printf("oksh: WARNING!!! setgroups() isn't implemented!\n"); 53 | + // setgroups(1, &gid); 54 | setresuid(ksheuid, ksheuid, ksheuid); 55 | 56 | #ifdef HAVE_PLEDGE 57 | diff --git a/portable.h b/portable.h 58 | index 5c86edd..22cb90b 100644 59 | --- a/portable.h 60 | +++ b/portable.h 61 | @@ -9,14 +9,14 @@ 62 | * Includes 63 | */ 64 | 65 | -#if defined(__linux__) || defined(__CYGWIN__) || defined(__midipix__) 66 | +#if defined(__linux__) || defined(__CYGWIN__) || defined(__midipix__) || defined(__munix__) 67 | #include 68 | #include 69 | 70 | #include 71 | #include 72 | #include 73 | -#endif /* __linux__ || __CYGWIN__ || __midipix__ */ 74 | +#endif /* __linux__ || __CYGWIN__ || __midipix__ || __munix__ */ 75 | 76 | #include 77 | #include 78 | @@ -55,7 +55,7 @@ 79 | #endif /* !_PATH_BSHELL */ 80 | 81 | #ifndef _PW_NAME_LEN 82 | -#if defined(__linux__) || defined(__CYGWIN__) || defined(_AIX) || defined(__midipix__) || defined(__HAIKU__) 83 | +#if defined(__linux__) || defined(__CYGWIN__) || defined(_AIX) || defined(__midipix__) || defined(__HAIKU__) || defined(__munix__) 84 | #define _PW_NAME_LEN LOGIN_NAME_MAX 85 | #elif defined(__NetBSD__) 86 | #define _PW_NAME_LEN MAXLOGNAME 87 | @@ -65,7 +65,7 @@ 88 | #define _PW_NAME_LEN 8 89 | #else 90 | #define _PW_NAME_LEN MAXLOGNAME - 1 91 | -#endif /* __linux__ || __CYGWIN__ || _AIX || __NetBSD__ || __sun || __midipix__ || __HAIKU__ */ 92 | +#endif /* __linux__ || __CYGWIN__ || _AIX || __NetBSD__ || __sun || __midipix__ || __HAIKU__ || __munix__ */ 93 | #endif /* !_PW_NAME_LEN */ 94 | 95 | #ifndef LOCK_EX 96 | diff --git a/sh.h b/sh.h 97 | index 2d65808..fa9b5bf 100644 98 | --- a/sh.h 99 | +++ b/sh.h 100 | @@ -16,6 +16,7 @@ 101 | #include 102 | #include 103 | #include 104 | +#include 105 | 106 | /* end of common headers */ 107 | 108 | -- 109 | 2.36.3 110 | 111 | -------------------------------------------------------------------------------- /user/util-munix/Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -O2 3 | DESTDIR = 4 | 5 | .PHONY: all 6 | all: init 7 | 8 | init: init.c 9 | $(CC) $(CFLAGS) -o init init.c 10 | 11 | .PHONY: install 12 | install: init 13 | mkdir -p $(DESTDIR)/usr/bin/ 14 | install -p -s init $(DESTDIR)/usr/bin/ 15 | -------------------------------------------------------------------------------- /user/util-munix/init.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(int argc, char** argv) { 5 | syscall(SYS_debug_log, "init: hello, world!"); 6 | syscall(SYS_debug_log, "init: dumping argv..."); 7 | 8 | for (int i = 0; i < argc; i++) { 9 | syscall(SYS_debug_log, argv); 10 | } 11 | } 12 | --------------------------------------------------------------------------------