From 94aad4b8e106939e32f03c850325aa2b18cd76d1 Mon Sep 17 00:00:00 2001 From: Jozef Nagy Date: Mon, 20 Jan 2025 21:52:47 +0100 Subject: [PATCH] Initial import --- .github/CODEOWNERS | 1 + .github/FUNDING.yml | 2 + .github/ISSUE_TEMPLATE/01_BUG_REPORT.md | 37 + .github/ISSUE_TEMPLATE/02_FEATURE_REQUEST.md | 35 + .../ISSUE_TEMPLATE/03_CODEBASE_IMPROVEMENT.md | 7 + .github/ISSUE_TEMPLATE/04_SUPPORT_QUESTION.md | 9 + .github/ISSUE_TEMPLATE/config.yml | 2 + .github/PULL_REQUEST_TEMPLATE.md | 40 + .gitignore | 14 + .gitmodules | 3 + Brewfile | 36 + LICENSE | 21 + Makefile | 113 ++ README.md | 135 ++ boot/Makefile | 49 + boot/arch/aarch64/common/boot.S | 69 + boot/arch/aarch64/linker.ld | 57 + boot/arch/aarch64/uefi/.gitkeep | 0 boot/arch/i686/boot-cd.asm | 80 ++ boot/arch/i686/boot-hdd.asm | 72 ++ boot/arch/i686/stage1/strings.inc | 21 + boot/arch/i686/uefi/.gitkeep | 0 boot/arch/x86_64/common/cpu/gdt.c | 35 + boot/arch/x86_64/common/debug/serial.c | 60 + boot/arch/x86_64/stage1/boot-cd.asm | 79 ++ boot/arch/x86_64/stage1/boot-hdd.asm | 71 + boot/arch/x86_64/stage1/strings.inc | 21 + boot/arch/x86_64/uefi/.gitkeep | 0 boot/base/axboot.cfg | 12 + boot/common/config/config.c | 78 ++ boot/common/lib/string.c | 128 ++ boot/common/print.c | 64 + boot/include/arch/aarch64/arch/mm/paging.h | 38 + boot/include/arch/aarch64/uefi/.gitkeep | 0 boot/include/arch/i686/arch/.gitkeep | 0 boot/include/arch/i686/uefi/.gitkeep | 0 boot/include/arch/x86_64/arch/cpu/cpu.h | 116 ++ boot/include/arch/x86_64/arch/cpu/gdt.h | 67 + boot/include/arch/x86_64/arch/mm/paging.h | 29 + boot/include/arch/x86_64/uefi/.gitkeep | 0 boot/include/axboot.h | 32 + boot/include/nanoprintf.h | 1137 +++++++++++++++++ boot/include/print.h | 32 + boot/include/utils.h | 28 + boot/platform/pc-bios/Makefile | 64 + boot/platform/raspi4/Makefile | 85 ++ boot/platform/uefi/Makefile | 116 ++ boot/platform/uefi/entry.c | 46 + boot/platform/uefi/libefi | 1 + boot/platform/uefi/print.c | 31 + docs/CODE_OF_CONDUCT.md | 45 + docs/CONTRIBUTING.md | 46 + docs/SECURITY.md | 16 + docs/boot/BOOTPROTOCOL.md | 117 ++ docs/boot/CONFIG.md | 51 + docs/boot/PHILOSOPHY.md | 10 + docs/images/logo.png | Bin 0 -> 61466 bytes kernel/Makefile | 135 ++ kernel/arch/aarch64/config.mk | 23 + kernel/arch/aarch64/linker.ld | 60 + kernel/arch/x86_64/config.mk | 33 + kernel/arch/x86_64/linker.ld | 78 ++ kernel/include/arch/aarch64/arch/cpu/cpu.h | 23 + kernel/include/arch/x86_64/arch/cpu/cpu.h | 116 ++ kernel/include/debug/uart.h | 30 + .../platform/generic-pc/platform/debug/uart.h | 32 + .../platform/raspi4/platform/debug/uart.h | 23 + kernel/kinit.c | 34 + kernel/platform/generic-pc/debug/uart/uart.c | 63 + kernel/platform/raspi4/debug/uart/uart.c | 34 + machine/i686/qemu.mk | 1 + machine/x86_64/qemu.mk | 1 + utils/arch/i686/generate-hdd.sh | 85 ++ utils/arch/i686/generate-iso.sh | 39 + utils/arch/x86_64/generate-hdd.sh | 87 ++ utils/arch/x86_64/generate-iso.sh | 39 + utils/download-ovmf.sh | 20 + 77 files changed, 4414 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/FUNDING.yml create mode 100644 .github/ISSUE_TEMPLATE/01_BUG_REPORT.md create mode 100644 .github/ISSUE_TEMPLATE/02_FEATURE_REQUEST.md create mode 100644 .github/ISSUE_TEMPLATE/03_CODEBASE_IMPROVEMENT.md create mode 100644 .github/ISSUE_TEMPLATE/04_SUPPORT_QUESTION.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 Brewfile create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 boot/Makefile create mode 100644 boot/arch/aarch64/common/boot.S create mode 100644 boot/arch/aarch64/linker.ld create mode 100644 boot/arch/aarch64/uefi/.gitkeep create mode 100644 boot/arch/i686/boot-cd.asm create mode 100644 boot/arch/i686/boot-hdd.asm create mode 100644 boot/arch/i686/stage1/strings.inc create mode 100644 boot/arch/i686/uefi/.gitkeep create mode 100644 boot/arch/x86_64/common/cpu/gdt.c create mode 100644 boot/arch/x86_64/common/debug/serial.c create mode 100644 boot/arch/x86_64/stage1/boot-cd.asm create mode 100644 boot/arch/x86_64/stage1/boot-hdd.asm create mode 100644 boot/arch/x86_64/stage1/strings.inc create mode 100644 boot/arch/x86_64/uefi/.gitkeep create mode 100644 boot/base/axboot.cfg create mode 100644 boot/common/config/config.c create mode 100644 boot/common/lib/string.c create mode 100644 boot/common/print.c create mode 100644 boot/include/arch/aarch64/arch/mm/paging.h create mode 100644 boot/include/arch/aarch64/uefi/.gitkeep create mode 100644 boot/include/arch/i686/arch/.gitkeep create mode 100644 boot/include/arch/i686/uefi/.gitkeep create mode 100644 boot/include/arch/x86_64/arch/cpu/cpu.h create mode 100644 boot/include/arch/x86_64/arch/cpu/gdt.h create mode 100644 boot/include/arch/x86_64/arch/mm/paging.h create mode 100644 boot/include/arch/x86_64/uefi/.gitkeep create mode 100644 boot/include/axboot.h create mode 100644 boot/include/nanoprintf.h create mode 100644 boot/include/print.h create mode 100644 boot/include/utils.h create mode 100644 boot/platform/pc-bios/Makefile create mode 100644 boot/platform/raspi4/Makefile create mode 100644 boot/platform/uefi/Makefile create mode 100644 boot/platform/uefi/entry.c create mode 160000 boot/platform/uefi/libefi create mode 100644 boot/platform/uefi/print.c create mode 100644 docs/CODE_OF_CONDUCT.md create mode 100644 docs/CONTRIBUTING.md create mode 100644 docs/SECURITY.md create mode 100644 docs/boot/BOOTPROTOCOL.md create mode 100644 docs/boot/CONFIG.md create mode 100644 docs/boot/PHILOSOPHY.md create mode 100644 docs/images/logo.png create mode 100644 kernel/Makefile create mode 100644 kernel/arch/aarch64/config.mk create mode 100644 kernel/arch/aarch64/linker.ld create mode 100644 kernel/arch/x86_64/config.mk create mode 100644 kernel/arch/x86_64/linker.ld create mode 100644 kernel/include/arch/aarch64/arch/cpu/cpu.h create mode 100644 kernel/include/arch/x86_64/arch/cpu/cpu.h create mode 100644 kernel/include/debug/uart.h create mode 100644 kernel/include/platform/generic-pc/platform/debug/uart.h create mode 100644 kernel/include/platform/raspi4/platform/debug/uart.h create mode 100644 kernel/kinit.c create mode 100644 kernel/platform/generic-pc/debug/uart/uart.c create mode 100644 kernel/platform/raspi4/debug/uart/uart.c create mode 100644 machine/i686/qemu.mk create mode 100644 machine/x86_64/qemu.mk create mode 100755 utils/arch/i686/generate-hdd.sh create mode 100755 utils/arch/i686/generate-iso.sh create mode 100755 utils/arch/x86_64/generate-hdd.sh create mode 100755 utils/arch/x86_64/generate-iso.sh create mode 100755 utils/download-ovmf.sh diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..f4561ab --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @schkwve diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..3878b84 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: schkwve + diff --git a/.github/ISSUE_TEMPLATE/01_BUG_REPORT.md b/.github/ISSUE_TEMPLATE/01_BUG_REPORT.md new file mode 100644 index 0000000..eaa6fa6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/01_BUG_REPORT.md @@ -0,0 +1,37 @@ +--- +name: Bug Report +about: Create a report to help AurixOS to improve +title: "bug: " +labels: "bug" +assignees: "" +--- + +# Bug Report + +**AurixOS version:** + + + +**Current behavior:** + + + +**Expected behavior:** + + + +**Steps to reproduce:** + + + +**Related code:** + + + +``` +insert short code snippets here +``` + +**Other information:** + + diff --git a/.github/ISSUE_TEMPLATE/02_FEATURE_REQUEST.md b/.github/ISSUE_TEMPLATE/02_FEATURE_REQUEST.md new file mode 100644 index 0000000..5719147 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/02_FEATURE_REQUEST.md @@ -0,0 +1,35 @@ +--- +name: Feature Request +about: Suggest an idea for this project +title: "feat: " +labels: "enhancement" +assignees: "" +--- + +# Feature Request + +**Describe the Feature Request** + + + +**Describe Preferred Solution** + + + +**Describe Alternatives** + + + +**Related Code** + + + +**Additional Context** + + + +**If the feature request is approved, would you be willing to submit a PR?** +_(Help can be provided if you need assistance submitting a PR)_ + +- [ ] Yes +- [ ] No diff --git a/.github/ISSUE_TEMPLATE/03_CODEBASE_IMPROVEMENT.md b/.github/ISSUE_TEMPLATE/03_CODEBASE_IMPROVEMENT.md new file mode 100644 index 0000000..bcd6cb7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/03_CODEBASE_IMPROVEMENT.md @@ -0,0 +1,7 @@ +--- +name: Codebase improvement +about: Provide your feedback for the existing codebase. Suggest a better solution for algorithms, development tools, etc. +title: "dev: " +labels: "enhancement" +assignees: "" +--- diff --git a/.github/ISSUE_TEMPLATE/04_SUPPORT_QUESTION.md b/.github/ISSUE_TEMPLATE/04_SUPPORT_QUESTION.md new file mode 100644 index 0000000..ca89bc0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/04_SUPPORT_QUESTION.md @@ -0,0 +1,9 @@ +--- +name: Support Question +about: Question on how to use this project +title: "support: " +labels: "question" +assignees: "" +--- + +# Support Question diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..bd9dfe4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,2 @@ +--- +blank_issues_enabled: false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..5d0b0b3 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,40 @@ + + +## Pull Request type + + + +Please check the type of change your PR introduces: + +- [ ] Bugfix +- [ ] Feature +- [ ] Code style update (formatting, renaming) +- [ ] Refactoring (no functional changes, no API changes) +- [ ] Build-related changes +- [ ] Documentation content changes +- [ ] Other (please describe): + +## What is the current behavior? + + + +Issue Number: N/A + +## What is the new behavior? + + + +- +- +- + +## Does this introduce a breaking change? + +- [ ] Yes +- [ ] No + + + +## Other information + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3765d1d --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +build/ +release/ +sysroot/ +ovmf/ +.vscode/ +.ccls-cache/ + +.DS_Store +Brewfile.lock.json +*.o +*.d +*.iso +*.img +*.log diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..d00defe --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "boot/platform/uefi/libefi"] + path = boot/platform/uefi/libefi + url = https://github.com/aurixos/efi diff --git a/Brewfile b/Brewfile new file mode 100644 index 0000000..1a3d26d --- /dev/null +++ b/Brewfile @@ -0,0 +1,36 @@ +#### +# COMMON PACKAGES +## +# These packages are required for building and running AurixOS +# regardless of the target architecture. +### + +brew "git" +brew "make" +brew "gptfdisk" +brew "xorriso" +brew "qemu" +brew "zig" +brew "util-linux" if OS.mac? +brew "gsed" if OS.mac? + +#### +# x86_64-SPECIFIC PACKAGES +## +# These packages are required for building and running AurixOS +# ONLY for the x86_64 architecture. +### + +brew "x86_64-elf-binutils" +brew "x86_64-elf-gcc" +brew "nasm" + +#### +# aarch64-SPECIFIC PACKAGES +## +# These packages are required for building and running AurixOS +# ONLY for the aarch64 architecture. +### + +brew "aarch64-elf-binutils" +brew "aarch64-elf-gcc" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..701a92a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024-2025, Jozef Nagy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3a6fa9b --- /dev/null +++ b/Makefile @@ -0,0 +1,113 @@ +################################################################################### +## Module Name: Makefile ## +## Project: AurixOS ## +## ## +## Copyright (c) 2024-2025 Jozef Nagy ## +## ## +## This source is subject to the MIT License. ## +## See License.txt in the root of this repository. ## +## All other rights reserved. ## +## ## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## +## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## +## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## +## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## +## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## +## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ## +## SOFTWARE. ## +################################################################################### + +export ARCH ?= x86_64 +export PLATFORM ?= generic-pc + +export ROOT_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +export BUILD_DIR ?= $(ROOT_DIR)/build +export SYSROOT_DIR ?= $(ROOT_DIR)/sysroot +export RELEASE_DIR ?= $(ROOT_DIR)/release + +export GITREV := $(shell git rev-parse --short HEAD) + +NOUEFI ?= y + +LIVECD := $(RELEASE_DIR)/aurix-$(GITREV)-livecd_$(ARCH)-$(PLATFORM).iso +LIVEHDD := $(RELEASE_DIR)/aurix-$(GITREV)-livehdd_$(ARCH)-$(PLATFORM).img +LIVESD := $(RELEASE_DIR)/aurix-$(GITREV)-livesd_$(ARCH)-$(PLATFORM).img + +QEMU_FLAGS := -m 2G -smp 4 -serial stdio + +.PHONY: boot +boot: + @printf ">>> Building bootloader...\n" +ifneq (,$(filter $(ARCH),i686 x86_64)) + @$(MAKE) -C boot PLATFORM=pc-bios +else + @$(MAKE) -C boot +endif +ifneq (,$(filter $(ARCH),i686 x86_64 arm32 aarch64)) +ifeq ($(NOUEFI),n) + @$(MAKE) -C boot PLATFORM=uefi +endif +endif + +.PHONY: kernel +kernel: + @printf ">>> Building kernel...\n" + @$(MAKE) -C kernel + +.PHONY: install +install: boot kernel + @printf ">>> Building sysroot...\n" + @mkdir -p $(SYSROOT_DIR) +ifneq (,$(filter $(ARCH),i686 x86_64)) + @$(MAKE) -C boot install PLATFORM=pc-bios +else + @$(MAKE) -C boot install +endif +ifneq (,$(filter $(ARCH),i686 x86_64 arm32 aarch64)) +ifeq ($(NOUEFI),n) + @$(MAKE) -C boot install PLATFORM=uefi +endif +endif + @$(MAKE) -C kernel install + +ovmf: + @printf ">>> Downloading OVMF images...\n" + @utils/download-ovmf.sh + +.PHONY: livecd +livecd: install + @printf ">>> Generating Live CD..." + @mkdir -p $(RELEASE_DIR) + @utils/arch/$(ARCH)/generate-iso.sh $(LIVECD) + +.PHONY: livehdd +livehdd: install + @printf ">>> Generating Live HDD..." + @mkdir -p $(RELEASE_DIR) + @utils/arch/$(ARCH)/generate-hdd.sh $(LIVEHDD) + +.PHONY: livesd +livesd: install + @$(error SD Card Generation is not supported yet!) + @printf ">>> Generating Live SD Card..." + @mkdir -p $(RELEASE_DIR) + @utils/arch/$(ARCH)/generate-sd.sh $(LIVESD) + +.PHONY: run +run: livecd + @printf ">>> Running QEMU...\n" + @qemu-system-$(ARCH) $(QEMU_FLAGS) $(QEMU_MACHINE_FLAGS) -cdrom $(LIVECD) + +.PHONY: run-uefi +run-uefi: livecd ovmf + @printf ">>> Running QEMU (UEFI)...\n" + @qemu-system-$(ARCH) $(QEMU_FLAGS) $(QEMU_MACHINE_FLAGS) -bios ovmf/ovmf-$(ARCH).fd -cdrom $(LIVECD) + +.PHONY: clean +clean: + @rm -rf $(BUILD_DIR) $(SYSROOT_DIR) + +.PHONY: distclean +distclean: + @rm -rf $(BUILD_DIR) $(SYSROOT_DIR) $(RELEASE_DIR) ovmf/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..2bc89eb --- /dev/null +++ b/README.md @@ -0,0 +1,135 @@ +

+ + Logo + +

+ +
+ AurixOS +
+
+ Report a Bug + ยท + Request a Feature + . + Ask a Question +
+
+
+ +[![Project license](https://img.shields.io/github/license/aurixos/os.svg?style=flat-square)](LICENSE) +[![Discord Chat](https://img.shields.io/discord/1234051470182055988)](https://discord.com/invite/hgbZ6wFP4n) +
+ +
+Table of Contents + +- [About](#about) +- [Getting Started](#getting-started) + - [Prerequisites](#prerequisites) + - [Building](#building) +- [Running](#running) +- [Support](#support) +- [Contributing](#contributing) +- [Authors & contributors](#authors--contributors) +- [Security](#security) +- [License](#license) +- [Acknowledgements](#acknowledgements) + +
+ +--- + +## About + +AurixOS is a general-purpose operating system, meant to take security, performance and usability to the next level. + + + +## Getting Started + +Latest official releases of AurixOS are available for download [here](https://github.com/aurixos/os/releases). + +### Prerequisites + +If you have [Homebrew](https://brew.sh) installed, simply run `brew bundle` to install **all** required build dependencies (incl. packages to build AurixOS for every supported architecture). +Alternatively, read the [Brewfile](Brewfile) in the root of this repository and install the required (and optional) packages manually. + +### Building + +To build AurixOS, simply run `make` in the root directory of this repository. +To change the build target, run `make ARCH=arch PLATFORM=platform`, where `arch` is the target architecture, and `platform` is the target platform (Default is set to `x86_64`/`generic-pc`). + +#### Possible/planned targets: +##### i686 / x86_64 +- `generic-pc` + +##### arm32 (not available yet) +- `raspi2` + +##### aarch64 +- `raspi3` (not available yet) +- `raspi4` (limited support) +- `raspi5` (not available yet) + +> [!NOTE] +> On x86(_64) architectures, a UEFI bootloader will be automatically built and packaged together together with a legacy stub. On arm32 and aarch64, invoke Make with `NOUEFI=n` modifier to create a UEFI-capable image. + +Inputting an invalid architecture/platform combination will result in an error. + +Building AurixOS images is also supported with Make recipes: +- `livecd` builds a CD-ROM image (.iso) +- `livehdd` builds a HDD image (.img) +- `livesd` builds an SD Card image (.img) + +Please note that not all bootable images can be created for all platforms (eg. `rpi4` only supports creating an SD Card image). Any attempt at creating an unsupported image will result in an error. + +All generated images reside in the `release/` directory and follow this naming scheme: +`aurixos-live[MEDIA]-[GIT_REV]_[ARCH]-[PLATFORM].iso` + +`make help` shows you all available build options + +## Running + +To run AurixOS on QEMU, run `make run` (optionally, append `ARCH=arch PLATFORM=platform` if you're building/running AurixOS on an architecture other than x86_64). + +Alternatively, you can boot AurixOS on a physical machine by running `dd if=release/.img of=/dev/ bs=1M` and booting off of this USB drive. + +## Support + +If you have any questions, feel free to open a [GitHub issue](https://github.com/aurixos/os/issues/new?assignees=&labels=question&template=04_SUPPORT_QUESTION.md&title=support%3A+). +Alternatively, you can visit us: +- on our [Discord server](https://discord.com/invite/hgbZ6wFP4n) +- on #aurixos at [irc.libera.chat](https://libera.chat/) ([Kiwi IRC](https://kiwiirc.com/client/irc.libera.chat/?&theme=cli#aurixos)) + +## Contributing + +First off, thanks for taking the time to contribute! Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make will benefit everybody else and are **greatly appreciated**. + +Please read [our contribution guidelines](docs/CONTRIBUTING.md), and thank you for being involved! + +## Authors & contributors + +For a full list of all authors and contributors, see [the contributors page](https://github.com/aurixos/os/contributors). + +## Security + +AurixOS follows good practices of security, but 100% security cannot be assured. +AurixOS is provided **"as is"** without any **warranty**. Use at your own risk. + +_For more information and to report security issues, please refer to our [security documentation](docs/SECURITY.md)._ + +## License + +This project is licensed under the **MIT license**. + +See [LICENSE](LICENSE) for more information. diff --git a/boot/Makefile b/boot/Makefile new file mode 100644 index 0000000..5eb67d9 --- /dev/null +++ b/boot/Makefile @@ -0,0 +1,49 @@ +################################################################################### +## Module Name: Makefile ## +## Project: AurixOS ## +## ## +## Copyright (c) 2024-2025 Jozef Nagy ## +## ## +## This source is subject to the MIT License. ## +## See License.txt in the root of this repository. ## +## All other rights reserved. ## +## ## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## +## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## +## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## +## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## +## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## +## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ## +## SOFTWARE. ## +################################################################################### + +.DEFAULT_GOAL := all + +export INCLUDE_DIRS := $(BOOT_ROOT)/include + +export BUILD_DIR ?= build +export SYSROOT_DIR ?= sysroot + +export ASFLAGS := +export CFLAGS := -D__$(ARCH) -D_AXBOOT -ffreestanding -fno-stack-protector -fno-stack-check -MMD -MP +export LDFLAGS := -nostdlib + +export BOOT_ROOT := $(ROOT_DIR)/boot + +ifeq ($(DEBUG),yes) +CFLAGS += -O0 -g3 +else +CFLAGS += -O2 +endif + +.PHONY: all +all: + @$(MAKE) -C platform/$(PLATFORM) all + +.PHONY: install +install: + @$(MAKE) -C platform/$(PLATFORM) install + +.PHONY: clean +clean: + @$(MAKE) -C platform/$(PLATFORM) clean diff --git a/boot/arch/aarch64/common/boot.S b/boot/arch/aarch64/common/boot.S new file mode 100644 index 0000000..e133ea3 --- /dev/null +++ b/boot/arch/aarch64/common/boot.S @@ -0,0 +1,69 @@ +/*********************************************************************************/ +/* Module Name: boot.S */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +.section ".text.boot" + +.global _start + +_start: + // + // hello, am i on the main core? + // + mrs x1, mpidr_el1 + and x1, x1, #3 + cbz x1, 2f + + // + // no? alright then, sorry for interrupting. *leaves* + // +1: + wfe + b 1b + + // + // ok cool now execute this huge pile of horrible code + // thanks :> + // +2: + // + // let the stack live below our code + // + ldr x1, =_start + mov sp, x1 + + // + // no junk allowed in .bss! + // + ldr x1, =__bss_start + ldr w2, =__bss_size +3: cbz w2, 4f + str xzr, [x1], #8 + sub w2, w2, #1 + cbnz w2, 3b + +4: + bl menu_main + + // + // crazy? i was crazy once. + // they locked me in a room. a rubber room. a rubber room with rats. + // and rats make me crazy. + // (bootloader returned, just halt the whole thing) + // + b 1b diff --git a/boot/arch/aarch64/linker.ld b/boot/arch/aarch64/linker.ld new file mode 100644 index 0000000..ad58a34 --- /dev/null +++ b/boot/arch/aarch64/linker.ld @@ -0,0 +1,57 @@ +/*********************************************************************************/ +/* Module Name: linker.ld */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +SECTIONS +{ + . = 0x80000; + .text : { + *(.text .text.*) + *(.gnu.linkonce.t*) + } + + .rodata : { + *(.rodata .rodata.*) + *(.gnu.linkonce.r*) + } + + PROVIDE(_data = .); + + .data : { + *(.data .data.*) + *(.gnu.linkonce.d*) + } + + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; + + /DISCARD/ : { + *(.comment) + *(.gnu*) + *(.note*) + *(.eh_frame*) + } +} + +/*__bss_size = (__bss_end - __bss_start) >> 3;*/ diff --git a/boot/arch/aarch64/uefi/.gitkeep b/boot/arch/aarch64/uefi/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/boot/arch/i686/boot-cd.asm b/boot/arch/i686/boot-cd.asm new file mode 100644 index 0000000..2948a3d --- /dev/null +++ b/boot/arch/i686/boot-cd.asm @@ -0,0 +1,80 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Module Name: boot-cd.asm ;; +;; Project: AurixOS ;; +;; ;; +;; Copyright (c) 2024-2025 Jozef Nagy ;; +;; ;; +;; This source is subject to the MIT License. ;; +;; See License.txt in the root of this repository. ;; +;; All other rights reserved. ;; +;; ;; +;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ;; +;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ;; +;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ;; +;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ;; +;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ;; +;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ;; +;; SOFTWARE. ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +[bits 16] +[org 0x7c00] + +jmp 0x0000:AxBootEntry + +times 8-($-$$) db 0 + +bi_PrimaryVolumeDescriptor dd 0 +bi_BootFileLocation dd 0 +bi_BootFileLength dd 0 +bi_Checksum dd 0 +bi_Reserved times 40 db 0 + +;; +;; BIOS bootloader on i686 is just a placeholder +;; incase it ever becomes a thing... for now it just +;; notifies the user that you can't boot AurixOS +;; without UEFI on i686. +;; + +%include "arch/strings.inc" + +AxBootEntry: + ;; + ;; Set 80x50 text mode and clear the screen + ;; + mov ax, 0x03 + int 0x10 + xor bx, bx + mov ax, 0x1112 + int 0x10 + mov ah, 0 + int 0x10 + + ;; + ;; Display an error message and halt + ;; + mov si, sErrorUnbootable + call PrintString + mov si, sPressToReboot + call PrintString + jmp AxBootHalt + +PrintString: + lodsb + or al, al + jz .done + mov ah, 0x0e + mov bx, 0x0007 + int 0x10 + jmp PrintString + .done: + ret + +AxBootHalt: + cli + hlt + jmp AxBootHalt + +times 510-($-$$) db 0 +dw 0xaa55 \ No newline at end of file diff --git a/boot/arch/i686/boot-hdd.asm b/boot/arch/i686/boot-hdd.asm new file mode 100644 index 0000000..8ae32a2 --- /dev/null +++ b/boot/arch/i686/boot-hdd.asm @@ -0,0 +1,72 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Module Name: boot-hdd.asm ;; +;; Project: AurixOS ;; +;; ;; +;; Copyright (c) 2024-2025 Jozef Nagy ;; +;; ;; +;; This source is subject to the MIT License. ;; +;; See License.txt in the root of this repository. ;; +;; All other rights reserved. ;; +;; ;; +;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ;; +;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ;; +;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ;; +;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ;; +;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ;; +;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ;; +;; SOFTWARE. ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +[bits 16] +[org 0x7c00] + +jmp 0x0000:AxBootEntry + +;; +;; BIOS bootloader on i686 is just a placeholder +;; incase it ever becomes a thing... for now it just +;; notifies the user that you can't boot AurixOS +;; without UEFI on i686. +;; + +%include "arch/strings.inc" + +AxBootEntry: + ;; + ;; Set 80x50 text mode and clear the screen + ;; + mov ax, 0x03 + int 0x10 + xor bx, bx + mov ax, 0x1112 + int 0x10 + mov ah, 0 + int 0x10 + + ;; + ;; Display an error message and halt + ;; + mov si, sErrorUnbootable + call PrintString + mov si, sPressToReboot + call PrintString + jmp AxBootHalt + +PrintString: + lodsb + or al, al + jz .done + mov ah, 0x0e + mov bx, 0x0007 + int 0x10 + jmp PrintString + .done: + ret + +AxBootHalt: + cli + hlt + jmp AxBootHalt + +times 510-($-$$) db 0 +dw 0xaa55 \ No newline at end of file diff --git a/boot/arch/i686/stage1/strings.inc b/boot/arch/i686/stage1/strings.inc new file mode 100644 index 0000000..03f6dda --- /dev/null +++ b/boot/arch/i686/stage1/strings.inc @@ -0,0 +1,21 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Module Name: strings.inc ;; +;; Project: AurixOS ;; +;; ;; +;; Copyright (c) 2024-2025 Jozef Nagy ;; +;; ;; +;; This source is subject to the MIT License. ;; +;; See License.txt in the root of this repository. ;; +;; All other rights reserved. ;; +;; ;; +;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ;; +;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ;; +;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ;; +;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ;; +;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ;; +;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ;; +;; SOFTWARE. ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +sErrorUnbootable: db 0x0d, 0x0a, 0x0d, 0x0a, "Error: AurixOS can only be booted via UEFI on i686!", 0x0d, 0x0a, 0 +sPressToReboot: db "Press Ctrl+Alt+Del to reboot", 0 \ No newline at end of file diff --git a/boot/arch/i686/uefi/.gitkeep b/boot/arch/i686/uefi/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/boot/arch/x86_64/common/cpu/gdt.c b/boot/arch/x86_64/common/cpu/gdt.c new file mode 100644 index 0000000..7722fc4 --- /dev/null +++ b/boot/arch/x86_64/common/cpu/gdt.c @@ -0,0 +1,35 @@ +/*********************************************************************************/ +/* Module Name: gdt.c */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +#include +#include + +#include + +void gdt_set_entry(struct gdt_descriptor *entry, uint32_t base, uint32_t limit, uint8_t access, + uint8_t flags) +{ + entry->limit_low = (limit >> 8) & 0xffff; + entry->base_low = (base >> 8) & 0xffff; + entry->base_mid = (base >> 16) & 0xff; + entry->access = access; + entry->limit_high = (limit >> 16) & 0xf; + entry->flags = flags; + entry->base_high = (base >> 24) & 0xff; +} \ No newline at end of file diff --git a/boot/arch/x86_64/common/debug/serial.c b/boot/arch/x86_64/common/debug/serial.c new file mode 100644 index 0000000..690fa5c --- /dev/null +++ b/boot/arch/x86_64/common/debug/serial.c @@ -0,0 +1,60 @@ +/*********************************************************************************/ +/* Module Name: serial.c */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +#include +#include + +#include + +#define COM1 0x3f8 + +static int is_tx_empty(void) +{ + return inb(COM1 + 5) & 0x20; +} + +void serial_init(void) +{ + // TODO: Initialize all COM ports + outb(COM1 + 1, 0x00); + outb(COM1 + 3, 0x80); + outb(COM1, 0x03); + outb(COM1 + 1, 0x00); + outb(COM1 + 3, 0x03); + outb(COM1 + 2, 0xC7); + outb(COM1 + 4, 0x0B); + outb(COM1 + 4, 0x0F); +} + +void serial_send(char c) +{ + while (is_tx_empty() == 0); + outb(COM1, c); +} + +void serial_sendstr(char *s) +{ + while (*s != '\0') { + if (*s == '\r') { + s++; + continue; + } + serial_send(*s++); + } +} \ No newline at end of file diff --git a/boot/arch/x86_64/stage1/boot-cd.asm b/boot/arch/x86_64/stage1/boot-cd.asm new file mode 100644 index 0000000..4ee561e --- /dev/null +++ b/boot/arch/x86_64/stage1/boot-cd.asm @@ -0,0 +1,79 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Module Name: boot-cd.asm ;; +;; Project: AurixOS ;; +;; ;; +;; Copyright (c) 2024-2025 Jozef Nagy ;; +;; ;; +;; This source is subject to the MIT License. ;; +;; See License.txt in the root of this repository. ;; +;; All other rights reserved. ;; +;; ;; +;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ;; +;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ;; +;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ;; +;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ;; +;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ;; +;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ;; +;; SOFTWARE. ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +[bits 16] +[org 0x7c00] + +jmp 0x0000:AxBootEntry + +times 8-($-$$) db 0 + +bi_PrimaryVolumeDescriptor dd 0 +bi_BootFileLocation dd 0 +bi_BootFileLength dd 0 +bi_Checksum dd 0 +bi_Reserved times 40 db 0 + +;; +;; BIOS bootloader on x86_64 is just a placeholder +;; incase it ever becomes a thing... for now it just +;; notifies the user that you can't boot AurixOS +;; without UEFI on x86_64. +;; + +%include "strings.inc" + +AxBootEntry: + ;; + ;; Set 80x50 text mode and clear the screen + ;; + mov ax, 0x03 + int 0x10 + xor bx, bx + mov ax, 0x1112 + int 0x10 + mov ah, 0 + int 0x10 + + ;; + ;; Display an error message and halt + ;; + mov si, sErrorUnbootable + call PrintString + mov si, sPressToReboot + call PrintString + jmp AxBootHalt + +PrintString: + lodsb + or al, al + jz .done + mov ah, 0x0e + mov bx, 0x0007 + int 0x10 + jmp PrintString + .done: + ret + +AxBootHalt: + hlt + jmp AxBootHalt + +times 510-($-$$) db 0 +dw 0xaa55 \ No newline at end of file diff --git a/boot/arch/x86_64/stage1/boot-hdd.asm b/boot/arch/x86_64/stage1/boot-hdd.asm new file mode 100644 index 0000000..d784430 --- /dev/null +++ b/boot/arch/x86_64/stage1/boot-hdd.asm @@ -0,0 +1,71 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Module Name: boot-hdd.asm ;; +;; Project: AurixOS ;; +;; ;; +;; Copyright (c) 2024-2025 Jozef Nagy ;; +;; ;; +;; This source is subject to the MIT License. ;; +;; See License.txt in the root of this repository. ;; +;; All other rights reserved. ;; +;; ;; +;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ;; +;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ;; +;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ;; +;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ;; +;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ;; +;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ;; +;; SOFTWARE. ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +[bits 16] +[org 0x7c00] + +jmp 0x0000:AxBootEntry + +;; +;; BIOS bootloader on x86_64 is just a placeholder +;; incase it ever becomes a thing... for now it just +;; notifies the user that you can't boot AurixOS +;; without UEFI on x86_64. +;; + +%include "strings.inc" + +AxBootEntry: + ;; + ;; Set 80x50 text mode and clear the screen + ;; + mov ax, 0x03 + int 0x10 + xor bx, bx + mov ax, 0x1112 + int 0x10 + mov ah, 0 + int 0x10 + + ;; + ;; Display an error message and halt + ;; + mov si, sErrorUnbootable + call PrintString + mov si, sPressToReboot + call PrintString + jmp AxBootHalt + +PrintString: + lodsb + or al, al + jz .done + mov ah, 0x0e + mov bx, 0x0007 + int 0x10 + jmp PrintString + .done: + ret + +AxBootHalt: + hlt + jmp AxBootHalt + +times 510-($-$$) db 0 +dw 0xaa55 \ No newline at end of file diff --git a/boot/arch/x86_64/stage1/strings.inc b/boot/arch/x86_64/stage1/strings.inc new file mode 100644 index 0000000..9c10d65 --- /dev/null +++ b/boot/arch/x86_64/stage1/strings.inc @@ -0,0 +1,21 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Module Name: strings.inc ;; +;; Project: AurixOS ;; +;; ;; +;; Copyright (c) 2024-2025 Jozef Nagy ;; +;; ;; +;; This source is subject to the MIT License. ;; +;; See License.txt in the root of this repository. ;; +;; All other rights reserved. ;; +;; ;; +;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ;; +;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ;; +;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ;; +;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ;; +;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ;; +;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ;; +;; SOFTWARE. ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +sErrorUnbootable: db 0x0d, 0x0a, 0x0d, 0x0a, "Error: AurixOS can only be booted via UEFI on x86_64!", 0x0d, 0x0a, 0 +sPressToReboot: db "Press Ctrl+Alt+Del to reboot", 0 \ No newline at end of file diff --git a/boot/arch/x86_64/uefi/.gitkeep b/boot/arch/x86_64/uefi/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/boot/base/axboot.cfg b/boot/base/axboot.cfg new file mode 100644 index 0000000..ccfec46 --- /dev/null +++ b/boot/base/axboot.cfg @@ -0,0 +1,12 @@ +; This is a comment + +entry "AurixOS" { + PROTOCOL="aurix" + IMAGE_PATH="boot:///System/axkrnl.sys" +} + +;; UEFI only +entry "Windows" { + PROTOCOL="chainload" + IMAGE_PATH="boot:///EFI/Microsoft/bootmgfw.efi" +} diff --git a/boot/common/config/config.c b/boot/common/config/config.c new file mode 100644 index 0000000..9822aab --- /dev/null +++ b/boot/common/config/config.c @@ -0,0 +1,78 @@ +/*********************************************************************************/ +/* Module Name: config.c */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include + +char *config_paths[] = { + "\\axboot.cfg", + "\\System\\axboot.cfg", + "\\EFI\\axboot.cfg", + "\\EFI\\BOOT\\axboot.cfg", +}; + +void config_init(void) +{ + FILE *config_file; + char *config_buffer; + int filesize; + + for (size_t i = 0; i < ARRAY_LENGTH(config_paths); i++) { + config_file = fw_file_open(NULL, config_paths[i]); + if (config_file != NULL) { + break; + } + } + + if (config_file == NULL) { + //print("No configuration file found! Please refer to the AxBoot documentation.\n"); + //print("Entering console...\n\n"); + //console(); + } + + filesize = fw_file_size(config_file); + config_buffer = malloc(filesize); + if (config_buffer == NULL) { + log("Entering console...\r\n\r\n"); + //console(); + } + + fw_file_read(config_file, filesize, config_buffer); + + // TODO: parse configuration file + + free(config_buffer); + + /* + if (config_errors != 0 || config_get_menu_root() == NULL) { + //print("\nConfiguration invalid!\n"); + //print("Please correct your config file.\n"); + //print("Entering console...\n\n"); + //console(); + } + */ + + fw_file_close(config_file); +} \ No newline at end of file diff --git a/boot/common/lib/string.c b/boot/common/lib/string.c new file mode 100644 index 0000000..6eb5510 --- /dev/null +++ b/boot/common/lib/string.c @@ -0,0 +1,128 @@ +/*********************************************************************************/ +/* Module Name: string.c */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +#include + +#include +#include + +size_t mbstowcs(wchar_t *dest, const char **src, size_t len) +{ + char *lsrc = (char *)*src; + size_t count = len; + + if (dest == NULL) { + return 0; + } + + while (count) { + if ((*dest = *lsrc) == 0) { + lsrc = NULL; + break; + } + + if (*dest >= 0x80) { + return -1; + } + + lsrc++; + dest++; + count--; + } + + return len - count; +} + +size_t strlen(const char *str) +{ + size_t count = 0; + + if (str == NULL) { + return 0; + } + + do { + count++; + } while (str[count] != '\0'); + + return count; +} + +char *strcpy(char *dest, const char *src) +{ + if (dest == NULL || src == NULL) { + return NULL; + } + + char *pdest = dest; + + while (*src != '\0') { + *dest = *src; + dest++; + src++; + } + + *dest = '\0'; + return pdest; +} + +void *memset(void *dest, int val, size_t len) +{ + unsigned char *ptr = dest; + while (len-- > 0) { + *ptr++ = (unsigned char)val; + } + return dest; +} + +void *memcpy(void *dest, void *src, size_t len) +{ + char *d = (char *)dest; + const char *s = (const char *)src; + + while (len-- > 0) { + *d++ = *s++; + } + + return dest; +} + +int memcmp(const void *a, const void *b, size_t len) +{ + unsigned char *ap = (unsigned char *)a; + unsigned char *bp = (unsigned char *)b; + int ret = 0; + + if (a == b) { + return 0; + } + + while (len > 0) { + if (*ap != *bp) { + ret = (*ap > *bp) ? 1 : -1; + break; + } + + len--; + ap++; + bp++; + } + + return ret; +} \ No newline at end of file diff --git a/boot/common/print.c b/boot/common/print.c new file mode 100644 index 0000000..362aff8 --- /dev/null +++ b/boot/common/print.c @@ -0,0 +1,64 @@ +/*********************************************************************************/ +/* Module Name: print.c */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +#define NANOPRINTF_IMPLEMENTATION +#define NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS 1 +#define NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS 1 +#define NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS 1 +#define NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS 1 +#define NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS 0 +#define NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS 0 +#include + +#include +#include + +#include +#include +#include +#include + +int32_t _fltused = 0; +int32_t __eqdf2 = 0; +int32_t __ltdf2 = 0; + +void log(const char *fmt, ...) +{ + va_list args; + char buf[4096]; + + va_start(args, fmt); + npf_vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + //printstr(buf); +} + +void debug(const char *fmt, ...) +{ + va_list args; + char buf[4096]; + + va_start(args, fmt); + npf_vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + //serial_sendstr(buf); +} + diff --git a/boot/include/arch/aarch64/arch/mm/paging.h b/boot/include/arch/aarch64/arch/mm/paging.h new file mode 100644 index 0000000..99d3078 --- /dev/null +++ b/boot/include/arch/aarch64/arch/mm/paging.h @@ -0,0 +1,38 @@ +/*********************************************************************************/ +/* Module Name: paging.h */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +#ifndef _MM_PAGING_H +#define _MM_PAGING_H + +#include + +#include +#include + +#define PAGE_SIZE 0x1000 + +int paging_init(struct memory_map_info *memmap); + +void paging_identity_map(uint64_t addr); +void paging_map(uint64_t phys, uint64_t virt); +void paging_unmap(uint64_t virt); + +void *paging_allocate(size_t np); + +#endif /* _MM_PAGING_H */ \ No newline at end of file diff --git a/boot/include/arch/aarch64/uefi/.gitkeep b/boot/include/arch/aarch64/uefi/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/boot/include/arch/i686/arch/.gitkeep b/boot/include/arch/i686/arch/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/boot/include/arch/i686/uefi/.gitkeep b/boot/include/arch/i686/uefi/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/boot/include/arch/x86_64/arch/cpu/cpu.h b/boot/include/arch/x86_64/arch/cpu/cpu.h new file mode 100644 index 0000000..d4aa6f5 --- /dev/null +++ b/boot/include/arch/x86_64/arch/cpu/cpu.h @@ -0,0 +1,116 @@ +/*********************************************************************************/ +/* Module Name: cpu.h */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +#ifndef _ARCH_CPU_CPU_H +#define _ARCH_CPU_CPU_H + +#include + +//// +// Utilities +/// + +static inline void cpu_disable_interrupts(void) +{ + __asm__ volatile("cli" ::: "memory"); +} + +static inline uint64_t read_cr0() +{ + uint64_t val; + __asm__ volatile("mov %%cr0, %0" + : "=r"(val)); + return val; +} + +static inline uint64_t read_cr2() +{ + uint64_t val; + __asm__ volatile("mov %%cr2, %0" + : "=r"(val)); + return val; +} + +static inline uint64_t read_cr3() +{ + uint64_t val; + __asm__ volatile("mov %%cr3, %0" + : "=r"(val)); + return val; +} + +static inline uint64_t read_cr4() +{ + uint64_t val; + __asm__ volatile("mov %%cr4, %0" + : "=r"(val)); + return val; +} + +static inline uint64_t read_cr8() +{ + uint64_t val; + __asm__ volatile("mov %%cr8, %0" + : "=r"(val)); + return val; +} + +static inline void write_cr0(uint64_t val) +{ + __asm__ volatile("mov %0, %%cr0" + :: "r"(val)); +} + +static inline void write_cr2(uint64_t val) +{ + __asm__ volatile("mov %0, %%cr2" + :: "r"(val)); +} + +static inline void write_cr3(uint64_t val) +{ + __asm__ volatile("mov %0, %%cr3" + :: "r"(val) : "memory"); +} + +static inline void write_cr4(uint64_t val) +{ + __asm__ volatile("mov %0, %%cr4" + :: "r"(val)); +} + +static inline void write_cr8(uint64_t val) +{ + __asm__ volatile("mov %0, %%cr8" + :: "r"(val)); +} + +static inline uint8_t inb(uint16_t port) +{ + uint8_t ret; + __asm__ volatile("inb %w1, %b0" : "=a"(ret) : "Nd"(port) : "memory"); + return ret; +} + +static inline void outb(uint16_t port, uint8_t val) +{ + __asm__ volatile("outb %b0, %w1" :: "a"(val), "Nd"(port) : "memory"); +} + +#endif /* _ARCH_CPU_CPU_H */ \ No newline at end of file diff --git a/boot/include/arch/x86_64/arch/cpu/gdt.h b/boot/include/arch/x86_64/arch/cpu/gdt.h new file mode 100644 index 0000000..4faeb0f --- /dev/null +++ b/boot/include/arch/x86_64/arch/cpu/gdt.h @@ -0,0 +1,67 @@ +/*********************************************************************************/ +/* Module Name: gdt.h */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +#ifndef _ARCH_CPU_GDT_H +#define _ARCH_CPU_GDT_H + +#include + +struct gdt_descriptor { + uint16_t limit_low; + uint16_t base_low; + uint8_t base_mid; + uint8_t access; + uint8_t limit_high : 4; + uint8_t flags : 4; + uint8_t base_high; +} __attribute__((packed)); + +struct tss_descriptor { + struct gdt_descriptor gdt; + uint32_t base_high; + uint32_t reserved; +} __attribute__((packed)); + +struct tss { + uint32_t reserved; + uint32_t rsp0[2]; + uint32_t rsp1[2]; + uint32_t rsp2[2]; + uint32_t reserved0[2]; + uint32_t ist0[2]; + uint32_t ist1[2]; + uint32_t ist2[2]; + uint32_t ist3[2]; + uint32_t ist4[2]; + uint32_t ist5[2]; + uint32_t ist6[2]; + uint32_t ist7[2]; + uint32_t reserved1[4]; + uint16_t iomap_base; +} __attribute__((packed)); + +struct gdtr { + uint16_t limit; + uint64_t base; +} __attribute__((packed)); + +void gdt_set_entry(struct gdt_descriptor *entry, uint32_t base, uint32_t limit, uint8_t access, + uint8_t flags); + +#endif /* _ARCH_CPU_GDT_H */ \ No newline at end of file diff --git a/boot/include/arch/x86_64/arch/mm/paging.h b/boot/include/arch/x86_64/arch/mm/paging.h new file mode 100644 index 0000000..c6e9c23 --- /dev/null +++ b/boot/include/arch/x86_64/arch/mm/paging.h @@ -0,0 +1,29 @@ +/*********************************************************************************/ +/* Module Name: paging.h */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +#ifndef _MM_PAGING_H +#define _MM_PAGING_H + +#include + +#include + +#define PAGE_SIZE 0x1000 + +#endif /* _MM_PAGING_H */ \ No newline at end of file diff --git a/boot/include/arch/x86_64/uefi/.gitkeep b/boot/include/arch/x86_64/uefi/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/boot/include/axboot.h b/boot/include/axboot.h new file mode 100644 index 0000000..53deb39 --- /dev/null +++ b/boot/include/axboot.h @@ -0,0 +1,32 @@ +/*********************************************************************************/ +/* Module Name: axboot.h */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +#ifndef _AXBOOT_H +#define _AXBOOT_H + +#include + +#define BOOTLOADER_NAME_STR "AxBoot" +#define BOOTLOADER_VERSION_STR "0.1" + +#ifndef UNREACHABLE + #define UNREACHABLE() +#endif + +#endif /* _AXBOOT_H */ \ No newline at end of file diff --git a/boot/include/nanoprintf.h b/boot/include/nanoprintf.h new file mode 100644 index 0000000..95d9fd5 --- /dev/null +++ b/boot/include/nanoprintf.h @@ -0,0 +1,1137 @@ +/* nanoprintf v0.5.3: a tiny embeddable printf replacement written in C. + https://github.com/charlesnicholson/nanoprintf + charles.nicholson+nanoprintf@gmail.com + dual-licensed under 0bsd and unlicense, take your pick. see eof for details. */ + +#ifndef NANOPRINTF_H_INCLUDED +#define NANOPRINTF_H_INCLUDED + +#include +#include + +// Define this to fully sandbox nanoprintf inside of a translation unit. +#ifdef NANOPRINTF_VISIBILITY_STATIC + #define NPF_VISIBILITY static +#else + #define NPF_VISIBILITY extern +#endif + +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define NPF_PRINTF_ATTR(FORMAT_INDEX, VARGS_INDEX) \ + __attribute__((format(printf, FORMAT_INDEX, VARGS_INDEX))) +#else + #define NPF_PRINTF_ATTR(FORMAT_INDEX, VARGS_INDEX) +#endif + +// Public API + +#ifdef __cplusplus +extern "C" { +#endif + +// The npf_ functions all return the number of bytes required to express the +// fully-formatted string, not including the null terminator character. +// The npf_ functions do not return negative values, since the lack of 'l' length +// modifier support makes encoding errors impossible. + +NPF_VISIBILITY int npf_snprintf( + char *buffer, size_t bufsz, const char *format, ...) NPF_PRINTF_ATTR(3, 4); + +NPF_VISIBILITY int npf_vsnprintf( + char *buffer, size_t bufsz, char const *format, va_list vlist) NPF_PRINTF_ATTR(3, 0); + +typedef void (*npf_putc)(int c, void *ctx); +NPF_VISIBILITY int npf_pprintf( + npf_putc pc, void *pc_ctx, char const *format, ...) NPF_PRINTF_ATTR(3, 4); + +NPF_VISIBILITY int npf_vpprintf( + npf_putc pc, void *pc_ctx, char const *format, va_list vlist) NPF_PRINTF_ATTR(3, 0); + +#ifdef __cplusplus +} +#endif + +#endif // NANOPRINTF_H_INCLUDED + +/* The implementation of nanoprintf begins here, to be compiled only if + NANOPRINTF_IMPLEMENTATION is defined. In a multi-file library what follows would + be nanoprintf.c. */ + +#ifdef NANOPRINTF_IMPLEMENTATION + +#ifndef NANOPRINTF_IMPLEMENTATION_INCLUDED +#define NANOPRINTF_IMPLEMENTATION_INCLUDED + +#include +#include + +// The conversion buffer must fit at least UINT64_MAX in octal format with the leading '0'. +#ifndef NANOPRINTF_CONVERSION_BUFFER_SIZE + #define NANOPRINTF_CONVERSION_BUFFER_SIZE 23 +#endif +#if NANOPRINTF_CONVERSION_BUFFER_SIZE < 23 + #error The size of the conversion buffer must be at least 23 bytes. +#endif + +// Pick reasonable defaults if nothing's been configured. +#if !defined(NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS) + #define NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS 1 + #define NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS 1 + #define NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS 1 + #define NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS 0 + #define NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS 0 + #define NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS 0 +#endif + +// If anything's been configured, everything must be configured. +#ifndef NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif + +// Ensure flags are compatible. +#if (NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1) && \ + (NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 0) + #error Precision format specifiers must be enabled if float support is enabled. +#endif + +// intmax_t / uintmax_t require stdint from c99 / c++11 +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + #ifndef _MSC_VER + #ifdef __cplusplus + #if __cplusplus < 201103L + #error large format specifier support requires C++11 or later. + #endif + #else + #if __STDC_VERSION__ < 199409L + #error nanoprintf requires C99 or later. + #endif + #endif + #endif +#endif + +// Figure out if we can disable warnings with pragmas. +#ifdef __clang__ + #define NANOPRINTF_CLANG 1 + #define NANOPRINTF_GCC_PAST_4_6 0 +#else + #define NANOPRINTF_CLANG 0 + #if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 6))) + #define NANOPRINTF_GCC_PAST_4_6 1 + #else + #define NANOPRINTF_GCC_PAST_4_6 0 + #endif +#endif + +#if NANOPRINTF_CLANG || NANOPRINTF_GCC_PAST_4_6 + #define NANOPRINTF_HAVE_GCC_WARNING_PRAGMAS 1 +#else + #define NANOPRINTF_HAVE_GCC_WARNING_PRAGMAS 0 +#endif + +#if NANOPRINTF_HAVE_GCC_WARNING_PRAGMAS + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-function" + #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" + #ifdef __cplusplus + #pragma GCC diagnostic ignored "-Wold-style-cast" + #endif + #pragma GCC diagnostic ignored "-Wpadded" + #pragma GCC diagnostic ignored "-Wfloat-equal" + #if NANOPRINTF_CLANG + #pragma GCC diagnostic ignored "-Wc++98-compat-pedantic" + #pragma GCC diagnostic ignored "-Wcovered-switch-default" + #pragma GCC diagnostic ignored "-Wdeclaration-after-statement" + #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" + #ifndef __APPLE__ + #pragma GCC diagnostic ignored "-Wunsafe-buffer-usage" + #endif + #elif NANOPRINTF_GCC_PAST_4_6 + #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" + #endif +#endif + +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4619) // there is no warning number 'number' + // C4619 has to be disabled first! + #pragma warning(disable:4127) // conditional expression is constant + #pragma warning(disable:4505) // unreferenced local function has been removed + #pragma warning(disable:4514) // unreferenced inline function has been removed + #pragma warning(disable:4701) // potentially uninitialized local variable used + #pragma warning(disable:4706) // assignment within conditional expression + #pragma warning(disable:4710) // function not inlined + #pragma warning(disable:4711) // function selected for inline expansion + #pragma warning(disable:4820) // padding added after struct member + #pragma warning(disable:5039) // potentially throwing function passed to extern C function + #pragma warning(disable:5045) // compiler will insert Spectre mitigation for memory load + #pragma warning(disable:5262) // implicit switch fall-through + #pragma warning(disable:26812) // enum type is unscoped +#endif + +#if defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) + #define NPF_NOINLINE __attribute__((noinline)) +#elif defined(_MSC_VER) + #define NPF_NOINLINE __declspec(noinline) +#else + #define NPF_NOINLINE +#endif + +#if (NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1) || \ + (NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1) +enum { + NPF_FMT_SPEC_OPT_NONE, + NPF_FMT_SPEC_OPT_LITERAL, + NPF_FMT_SPEC_OPT_STAR, +}; +#endif + +enum { + NPF_FMT_SPEC_LEN_MOD_NONE, + NPF_FMT_SPEC_LEN_MOD_SHORT, // 'h' + NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE, // 'L' + NPF_FMT_SPEC_LEN_MOD_CHAR, // 'hh' + NPF_FMT_SPEC_LEN_MOD_LONG, // 'l' +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + NPF_FMT_SPEC_LEN_MOD_LARGE_LONG_LONG, // 'll' + NPF_FMT_SPEC_LEN_MOD_LARGE_INTMAX, // 'j' + NPF_FMT_SPEC_LEN_MOD_LARGE_SIZET, // 'z' + NPF_FMT_SPEC_LEN_MOD_LARGE_PTRDIFFT, // 't' +#endif +}; + +enum { + NPF_FMT_SPEC_CONV_NONE, + NPF_FMT_SPEC_CONV_PERCENT, // '%' + NPF_FMT_SPEC_CONV_CHAR, // 'c' + NPF_FMT_SPEC_CONV_STRING, // 's' + NPF_FMT_SPEC_CONV_SIGNED_INT, // 'i', 'd' +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + NPF_FMT_SPEC_CONV_BINARY, // 'b' +#endif + NPF_FMT_SPEC_CONV_OCTAL, // 'o' + NPF_FMT_SPEC_CONV_HEX_INT, // 'x', 'X' + NPF_FMT_SPEC_CONV_UNSIGNED_INT, // 'u' + NPF_FMT_SPEC_CONV_POINTER, // 'p' +#if NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS == 1 + NPF_FMT_SPEC_CONV_WRITEBACK, // 'n' +#endif +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + NPF_FMT_SPEC_CONV_FLOAT_DEC, // 'f', 'F' + NPF_FMT_SPEC_CONV_FLOAT_SCI, // 'e', 'E' + NPF_FMT_SPEC_CONV_FLOAT_SHORTEST, // 'g', 'G' + NPF_FMT_SPEC_CONV_FLOAT_HEX, // 'a', 'A' +#endif +}; + +typedef struct npf_format_spec { +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + int field_width; + uint8_t field_width_opt; + char left_justified; // '-' + char leading_zero_pad; // '0' +#endif +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + int prec; + uint8_t prec_opt; +#endif + char prepend; // ' ' or '+' + char alt_form; // '#' + char case_adjust; // 'a' - 'A' + uint8_t length_modifier; + uint8_t conv_spec; +} npf_format_spec_t; + +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 0 + typedef long npf_int_t; + typedef unsigned long npf_uint_t; +#else + typedef intmax_t npf_int_t; + typedef uintmax_t npf_uint_t; +#endif + +typedef struct npf_bufputc_ctx { + char *dst; + size_t len; + size_t cur; +} npf_bufputc_ctx_t; + +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + typedef char npf_size_is_ptrdiff[(sizeof(size_t) == sizeof(ptrdiff_t)) ? 1 : -1]; + typedef ptrdiff_t npf_ssize_t; +#endif + +#ifdef _MSC_VER + #include +#endif + +static int npf_max(int x, int y) { return (x > y) ? x : y; } + +static int npf_parse_format_spec(char const *format, npf_format_spec_t *out_spec) { + char const *cur = format; + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + out_spec->left_justified = 0; + out_spec->leading_zero_pad = 0; +#endif + out_spec->case_adjust = 'a' - 'A'; // lowercase + out_spec->prepend = 0; + out_spec->alt_form = 0; + + while (*++cur) { // cur points at the leading '%' character + switch (*cur) { // Optional flags +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + case '-': out_spec->left_justified = '-'; out_spec->leading_zero_pad = 0; continue; + case '0': out_spec->leading_zero_pad = !out_spec->left_justified; continue; +#endif + case '+': out_spec->prepend = '+'; continue; + case ' ': if (out_spec->prepend == 0) { out_spec->prepend = ' '; } continue; + case '#': out_spec->alt_form = '#'; continue; + default: break; + } + break; + } + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + out_spec->field_width_opt = NPF_FMT_SPEC_OPT_NONE; + if (*cur == '*') { + out_spec->field_width_opt = NPF_FMT_SPEC_OPT_STAR; + ++cur; + } else { + out_spec->field_width = 0; + while ((*cur >= '0') && (*cur <= '9')) { + out_spec->field_width_opt = NPF_FMT_SPEC_OPT_LITERAL; + out_spec->field_width = (out_spec->field_width * 10) + (*cur++ - '0'); + } + } +#endif + +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + out_spec->prec = 0; + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; + if (*cur == '.') { + ++cur; + if (*cur == '*') { + out_spec->prec_opt = NPF_FMT_SPEC_OPT_STAR; + ++cur; + } else { + if (*cur == '-') { + ++cur; + } else { + out_spec->prec_opt = NPF_FMT_SPEC_OPT_LITERAL; + } + while ((*cur >= '0') && (*cur <= '9')) { + out_spec->prec = (out_spec->prec * 10) + (*cur++ - '0'); + } + } + } +#endif + + uint_fast8_t tmp_conv = NPF_FMT_SPEC_CONV_NONE; + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_NONE; + switch (*cur++) { // Length modifier + case 'h': + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_SHORT; + if (*cur == 'h') { + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_CHAR; + ++cur; + } + break; + case 'l': + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LONG; +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + if (*cur == 'l') { + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_LONG_LONG; + ++cur; + } +#endif + break; +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + case 'L': out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE; break; +#endif +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + case 'j': out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_INTMAX; break; + case 'z': out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_SIZET; break; + case 't': out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_PTRDIFFT; break; +#endif + default: --cur; break; + } + + switch (*cur++) { // Conversion specifier + case '%': out_spec->conv_spec = NPF_FMT_SPEC_CONV_PERCENT; +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; +#endif + break; + + case 'c': out_spec->conv_spec = NPF_FMT_SPEC_CONV_CHAR; +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; +#endif + break; + + case 's': out_spec->conv_spec = NPF_FMT_SPEC_CONV_STRING; +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + out_spec->leading_zero_pad = 0; +#endif + break; + + case 'i': + case 'd': tmp_conv = NPF_FMT_SPEC_CONV_SIGNED_INT; + case 'o': + if (tmp_conv == NPF_FMT_SPEC_CONV_NONE) { tmp_conv = NPF_FMT_SPEC_CONV_OCTAL; } + case 'u': + if (tmp_conv == NPF_FMT_SPEC_CONV_NONE) { tmp_conv = NPF_FMT_SPEC_CONV_UNSIGNED_INT; } + case 'X': + if (tmp_conv == NPF_FMT_SPEC_CONV_NONE) { out_spec->case_adjust = 0; } + case 'x': + if (tmp_conv == NPF_FMT_SPEC_CONV_NONE) { tmp_conv = NPF_FMT_SPEC_CONV_HEX_INT; } + out_spec->conv_spec = (uint8_t)tmp_conv; +#if (NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1) && \ + (NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1) + if (out_spec->prec_opt != NPF_FMT_SPEC_OPT_NONE) { out_spec->leading_zero_pad = 0; } +#endif + break; + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + case 'F': out_spec->case_adjust = 0; + case 'f': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_DEC; + if (out_spec->prec_opt == NPF_FMT_SPEC_OPT_NONE) { out_spec->prec = 6; } + break; + + case 'E': out_spec->case_adjust = 0; + case 'e': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_SCI; + if (out_spec->prec_opt == NPF_FMT_SPEC_OPT_NONE) { out_spec->prec = 6; } + break; + + case 'G': out_spec->case_adjust = 0; + case 'g': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_SHORTEST; + if (out_spec->prec_opt == NPF_FMT_SPEC_OPT_NONE) { out_spec->prec = 6; } + break; + + case 'A': out_spec->case_adjust = 0; + case 'a': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_HEX; + if (out_spec->prec_opt == NPF_FMT_SPEC_OPT_NONE) { out_spec->prec = 6; } + break; +#endif + +#if NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS == 1 + case 'n': + // todo: reject string if flags or width or precision exist + out_spec->conv_spec = NPF_FMT_SPEC_CONV_WRITEBACK; +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; +#endif + break; +#endif + + case 'p': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_POINTER; +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; +#endif + break; + +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + case 'B': + out_spec->case_adjust = 0; + case 'b': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_BINARY; + break; +#endif + + default: return 0; + } + + return (int)(cur - format); +} + +static NPF_NOINLINE int npf_utoa_rev( + npf_uint_t val, char *buf, uint_fast8_t base, char case_adj) { + uint_fast8_t n = 0; + do { + int_fast8_t const d = (int_fast8_t)(val % base); + *buf++ = (char)(((d < 10) ? '0' : ('A' - 10 + case_adj)) + d); + ++n; + val /= base; + } while (val); + return (int)n; +} + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + +#include + +#if (DBL_MANT_DIG <= 11) && (DBL_MAX_EXP <= 16) + typedef uint_fast16_t npf_double_bin_t; + typedef int_fast8_t npf_ftoa_exp_t; +#elif (DBL_MANT_DIG <= 24) && (DBL_MAX_EXP <= 128) + typedef uint_fast32_t npf_double_bin_t; + typedef int_fast8_t npf_ftoa_exp_t; +#elif (DBL_MANT_DIG <= 53) && (DBL_MAX_EXP <= 1024) + typedef uint_fast64_t npf_double_bin_t; + typedef int_fast16_t npf_ftoa_exp_t; +#else + #error Unsupported width of the double type. +#endif + +// The floating point conversion code works with an unsigned integer type of any size. +#ifndef NANOPRINTF_CONVERSION_FLOAT_TYPE + #define NANOPRINTF_CONVERSION_FLOAT_TYPE unsigned int +#endif +typedef NANOPRINTF_CONVERSION_FLOAT_TYPE npf_ftoa_man_t; + +#if (NANOPRINTF_CONVERSION_BUFFER_SIZE <= UINT_FAST8_MAX) && (UINT_FAST8_MAX <= INT_MAX) + typedef uint_fast8_t npf_ftoa_dec_t; +#else + typedef int npf_ftoa_dec_t; +#endif + +enum { + NPF_DOUBLE_EXP_MASK = DBL_MAX_EXP * 2 - 1, + NPF_DOUBLE_EXP_BIAS = DBL_MAX_EXP - 1, + NPF_DOUBLE_MAN_BITS = DBL_MANT_DIG - 1, + NPF_DOUBLE_BIN_BITS = sizeof(npf_double_bin_t) * CHAR_BIT, + NPF_FTOA_MAN_BITS = sizeof(npf_ftoa_man_t) * CHAR_BIT, + NPF_FTOA_SHIFT_BITS = + ((NPF_FTOA_MAN_BITS < DBL_MANT_DIG) ? NPF_FTOA_MAN_BITS : DBL_MANT_DIG) - 1 +}; + +/* Generally, floating-point conversion implementations use + grisu2 (https://bit.ly/2JgMggX) and ryu (https://bit.ly/2RLXSg0) algorithms, + which are mathematically exact and fast, but require large lookup tables. + + This implementation was inspired by Wojciech Muล‚a's (zdjฤ™cia@garnek.pl) + algorithm (http://0x80.pl/notesen/2015-12-29-float-to-string.html) and + extended further by adding dynamic scaling and configurable integer width by + Oskars Rubenis (https://github.com/Okarss). */ + +static int npf_ftoa_rev(char *buf, npf_format_spec_t const *spec, double f) { + char const *ret = NULL; + npf_double_bin_t bin; { // Union-cast is UB pre-C11, compiler optimizes byte-copy loop. + char const *src = (char const *)&f; + char *dst = (char *)&bin; + for (uint_fast8_t i = 0; i < sizeof(f); ++i) { dst[i] = src[i]; } + } + + // Unsigned -> signed int casting is IB and can raise a signal but generally doesn't. + npf_ftoa_exp_t exp = + (npf_ftoa_exp_t)((npf_ftoa_exp_t)(bin >> NPF_DOUBLE_MAN_BITS) & NPF_DOUBLE_EXP_MASK); + + bin &= ((npf_double_bin_t)0x1 << NPF_DOUBLE_MAN_BITS) - 1; + if (exp == (npf_ftoa_exp_t)NPF_DOUBLE_EXP_MASK) { // special value + ret = (bin) ? "NAN" : "FNI"; + goto exit; + } + if (spec->prec > (NANOPRINTF_CONVERSION_BUFFER_SIZE - 2)) { goto exit; } + if (exp) { // normal number + bin |= (npf_double_bin_t)0x1 << NPF_DOUBLE_MAN_BITS; + } else { // subnormal number + ++exp; + } + exp = (npf_ftoa_exp_t)(exp - NPF_DOUBLE_EXP_BIAS); + + uint_fast8_t carry; carry = 0; + npf_ftoa_dec_t end, dec; dec = (npf_ftoa_dec_t)spec->prec; + if (dec || spec->alt_form) { + buf[dec++] = '.'; + } + + { // Integer part + npf_ftoa_man_t man_i; + + if (exp >= 0) { + int_fast8_t shift_i = + (int_fast8_t)((exp > NPF_FTOA_SHIFT_BITS) ? (int)NPF_FTOA_SHIFT_BITS : exp); + npf_ftoa_exp_t exp_i = (npf_ftoa_exp_t)(exp - shift_i); + shift_i = (int_fast8_t)(NPF_DOUBLE_MAN_BITS - shift_i); + man_i = (npf_ftoa_man_t)(bin >> shift_i); + + if (exp_i) { + if (shift_i) { + carry = (bin >> (shift_i - 1)) & 0x1; + } + exp = NPF_DOUBLE_MAN_BITS; // invalidate the fraction part + } + + // Scale the exponent from base-2 to base-10. + for (; exp_i; --exp_i) { + if (!(man_i & ((npf_ftoa_man_t)0x1 << (NPF_FTOA_MAN_BITS - 1)))) { + man_i = (npf_ftoa_man_t)(man_i << 1); + man_i = (npf_ftoa_man_t)(man_i | carry); carry = 0; + } else { + if (dec >= NANOPRINTF_CONVERSION_BUFFER_SIZE) { goto exit; } + buf[dec++] = '0'; + carry = (((uint_fast8_t)(man_i % 5) + carry) > 2); + man_i /= 5; + } + } + } else { + man_i = 0; + } + end = dec; + + do { // Print the integer + if (end >= NANOPRINTF_CONVERSION_BUFFER_SIZE) { goto exit; } + buf[end++] = (char)('0' + (char)(man_i % 10)); + man_i /= 10; + } while (man_i); + } + + { // Fraction part + npf_ftoa_man_t man_f; + npf_ftoa_dec_t dec_f = (npf_ftoa_dec_t)spec->prec; + + if (exp < NPF_DOUBLE_MAN_BITS) { + int_fast8_t shift_f = (int_fast8_t)((exp < 0) ? -1 : exp); + npf_ftoa_exp_t exp_f = (npf_ftoa_exp_t)(exp - shift_f); + npf_double_bin_t bin_f = + bin << ((NPF_DOUBLE_BIN_BITS - NPF_DOUBLE_MAN_BITS) + shift_f); + + // This if-else statement can be completely optimized at compile time. + if (NPF_DOUBLE_BIN_BITS > NPF_FTOA_MAN_BITS) { + man_f = (npf_ftoa_man_t)(bin_f >> ((unsigned)(NPF_DOUBLE_BIN_BITS - + NPF_FTOA_MAN_BITS) % + NPF_DOUBLE_BIN_BITS)); + carry = (uint_fast8_t)((bin_f >> ((unsigned)(NPF_DOUBLE_BIN_BITS - + NPF_FTOA_MAN_BITS - 1) % + NPF_DOUBLE_BIN_BITS)) & 0x1); + } else { + man_f = (npf_ftoa_man_t)((npf_ftoa_man_t)bin_f + << ((unsigned)(NPF_FTOA_MAN_BITS - + NPF_DOUBLE_BIN_BITS) % NPF_FTOA_MAN_BITS)); + carry = 0; + } + + // Scale the exponent from base-2 to base-10 and prepare the first digit. + for (uint_fast8_t digit = 0; dec_f && (exp_f < 4); ++exp_f) { + if ((man_f > ((npf_ftoa_man_t)-4 / 5)) || digit) { + carry = (uint_fast8_t)(man_f & 0x1); + man_f = (npf_ftoa_man_t)(man_f >> 1); + } else { + man_f = (npf_ftoa_man_t)(man_f * 5); + if (carry) { man_f = (npf_ftoa_man_t)(man_f + 3); carry = 0; } + if (exp_f < 0) { + buf[--dec_f] = '0'; + } else { + ++digit; + } + } + } + man_f = (npf_ftoa_man_t)(man_f + carry); + carry = (exp_f >= 0); + dec = 0; + } else { + man_f = 0; + } + + if (dec_f) { + // Print the fraction + for (;;) { + buf[--dec_f] = (char)('0' + (char)(man_f >> (NPF_FTOA_MAN_BITS - 4))); + man_f = (npf_ftoa_man_t)(man_f & ~((npf_ftoa_man_t)0xF << (NPF_FTOA_MAN_BITS - 4))); + if (!dec_f) { break; } + man_f = (npf_ftoa_man_t)(man_f * 10); + } + man_f = (npf_ftoa_man_t)(man_f << 4); + } + if (exp < NPF_DOUBLE_MAN_BITS) { + carry &= (uint_fast8_t)(man_f >> (NPF_FTOA_MAN_BITS - 1)); + } + } + + // Round the number + for (; carry; ++dec) { + if (dec >= NANOPRINTF_CONVERSION_BUFFER_SIZE) { goto exit; } + if (dec >= end) { buf[end++] = '0'; } + if (buf[dec] == '.') { continue; } + carry = (buf[dec] == '9'); + buf[dec] = (char)(carry ? '0' : (buf[dec] + 1)); + } + + return (int)end; +exit: + if (!ret) { ret = "RRE"; } + uint_fast8_t i; + for (i = 0; ret[i]; ++i) { buf[i] = (char)(ret[i] + spec->case_adjust); } + return (int)i; +} + +#endif // NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS + +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 +static int npf_bin_len(npf_uint_t u) { + // Return the length of the binary string format of 'u', preferring intrinsics. + if (!u) { return 1; } + +#ifdef _MSC_VER // Win64, use _BSR64 for everything. If x86, use _BSR when non-large. + #ifdef _M_X64 + #define NPF_HAVE_BUILTIN_CLZ + #define NPF_CLZ _BitScanReverse64 + #elif NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 0 + #define NPF_HAVE_BUILTIN_CLZ + #define NPF_CLZ _BitScanReverse + #endif + #ifdef NPF_HAVE_BUILTIN_CLZ + unsigned long idx; + NPF_CLZ(&idx, u); + return (int)(idx + 1); + #endif +#elif NANOPRINTF_CLANG || NANOPRINTF_GCC_PAST_4_6 + #define NPF_HAVE_BUILTIN_CLZ + #if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + #define NPF_CLZ(X) ((sizeof(long long) * CHAR_BIT) - (size_t)__builtin_clzll(X)) + #else + #define NPF_CLZ(X) ((sizeof(long) * CHAR_BIT) - (size_t)__builtin_clzl(X)) + #endif + return (int)NPF_CLZ(u); +#endif + +#ifndef NPF_HAVE_BUILTIN_CLZ + int n; + for (n = 0; u; ++n, u >>= 1); // slow but small software fallback + return n; +#else + #undef NPF_HAVE_BUILTIN_CLZ + #undef NPF_CLZ +#endif +} +#endif + +static void npf_bufputc(int c, void *ctx) { + npf_bufputc_ctx_t *bpc = (npf_bufputc_ctx_t *)ctx; + if (bpc->cur < bpc->len) { bpc->dst[bpc->cur++] = (char)c; } +} + +static void npf_bufputc_nop(int c, void *ctx) { (void)c; (void)ctx; } + +typedef struct npf_cnt_putc_ctx { + npf_putc pc; + void *ctx; + int n; +} npf_cnt_putc_ctx_t; + +static void npf_putc_cnt(int c, void *ctx) { + npf_cnt_putc_ctx_t *pc_cnt = (npf_cnt_putc_ctx_t *)ctx; + ++pc_cnt->n; + pc_cnt->pc(c, pc_cnt->ctx); // sibling-call optimization +} + +#define NPF_PUTC(VAL) do { npf_putc_cnt((int)(VAL), &pc_cnt); } while (0) + +#define NPF_EXTRACT(MOD, CAST_TO, EXTRACT_AS) \ + case NPF_FMT_SPEC_LEN_MOD_##MOD: val = (CAST_TO)va_arg(args, EXTRACT_AS); break + +#define NPF_WRITEBACK(MOD, TYPE) \ + case NPF_FMT_SPEC_LEN_MOD_##MOD: *(va_arg(args, TYPE *)) = (TYPE)pc_cnt.n; break + +int npf_vpprintf(npf_putc pc, void *pc_ctx, char const *format, va_list args) { + npf_format_spec_t fs; + char const *cur = format; + npf_cnt_putc_ctx_t pc_cnt; + pc_cnt.pc = pc; + pc_cnt.ctx = pc_ctx; + pc_cnt.n = 0; + + while (*cur) { + int const fs_len = (*cur != '%') ? 0 : npf_parse_format_spec(cur, &fs); + if (!fs_len) { NPF_PUTC(*cur++); continue; } + cur += fs_len; + + // Extract star-args immediately +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + if (fs.field_width_opt == NPF_FMT_SPEC_OPT_STAR) { + fs.field_width = va_arg(args, int); + if (fs.field_width < 0) { + fs.field_width = -fs.field_width; + fs.left_justified = 1; + } + } +#endif +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + if (fs.prec_opt == NPF_FMT_SPEC_OPT_STAR) { + fs.prec = va_arg(args, int); + if (fs.prec < 0) { fs.prec_opt = NPF_FMT_SPEC_OPT_NONE; } + } +#endif + + union { char cbuf_mem[NANOPRINTF_CONVERSION_BUFFER_SIZE]; npf_uint_t binval; } u; + char *cbuf = u.cbuf_mem, sign_c = 0; + int cbuf_len = 0, need_0x = 0; +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + int field_pad = 0; + char pad_c = 0; +#endif +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + int prec_pad = 0; +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + int zero = 0; +#endif +#endif + + // Extract and convert the argument to string, point cbuf at the text. + switch (fs.conv_spec) { + case NPF_FMT_SPEC_CONV_PERCENT: + *cbuf = '%'; + cbuf_len = 1; + break; + + case NPF_FMT_SPEC_CONV_CHAR: + *cbuf = (char)va_arg(args, int); + cbuf_len = 1; + break; + + case NPF_FMT_SPEC_CONV_STRING: { + cbuf = va_arg(args, char *); +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + for (char const *s = cbuf; + ((fs.prec_opt == NPF_FMT_SPEC_OPT_NONE) || (cbuf_len < fs.prec)) && *s; + ++s, ++cbuf_len); +#else + for (char const *s = cbuf; *s; ++s, ++cbuf_len); // strlen +#endif + } break; + + case NPF_FMT_SPEC_CONV_SIGNED_INT: { + npf_int_t val = 0; + switch (fs.length_modifier) { + NPF_EXTRACT(NONE, int, int); + NPF_EXTRACT(SHORT, short, int); + NPF_EXTRACT(LONG_DOUBLE, int, int); + NPF_EXTRACT(CHAR, char, int); + NPF_EXTRACT(LONG, long, long); +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + NPF_EXTRACT(LARGE_LONG_LONG, long long, long long); + NPF_EXTRACT(LARGE_INTMAX, intmax_t, intmax_t); + NPF_EXTRACT(LARGE_SIZET, npf_ssize_t, npf_ssize_t); + NPF_EXTRACT(LARGE_PTRDIFFT, ptrdiff_t, ptrdiff_t); +#endif + default: break; + } + + sign_c = (val < 0) ? '-' : fs.prepend; + +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + zero = !val; +#endif + // special case, if prec and value are 0, skip + if (!val && (fs.prec_opt != NPF_FMT_SPEC_OPT_NONE) && !fs.prec) { + cbuf_len = 0; + } else +#endif + { + npf_uint_t uval = (npf_uint_t)val; + if (val < 0) { uval = 0 - uval; } + cbuf_len = npf_utoa_rev(uval, cbuf, 10, fs.case_adjust); + } + } break; + +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + case NPF_FMT_SPEC_CONV_BINARY: +#endif + case NPF_FMT_SPEC_CONV_OCTAL: + case NPF_FMT_SPEC_CONV_HEX_INT: + case NPF_FMT_SPEC_CONV_UNSIGNED_INT: { + npf_uint_t val = 0; + + switch (fs.length_modifier) { + NPF_EXTRACT(NONE, unsigned, unsigned); + NPF_EXTRACT(SHORT, unsigned short, unsigned); + NPF_EXTRACT(LONG_DOUBLE, unsigned, unsigned); + NPF_EXTRACT(CHAR, unsigned char, unsigned); + NPF_EXTRACT(LONG, unsigned long, unsigned long); +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + NPF_EXTRACT(LARGE_LONG_LONG, unsigned long long, unsigned long long); + NPF_EXTRACT(LARGE_INTMAX, uintmax_t, uintmax_t); + NPF_EXTRACT(LARGE_SIZET, size_t, size_t); + NPF_EXTRACT(LARGE_PTRDIFFT, size_t, size_t); +#endif + default: break; + } + +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + zero = !val; +#endif + if (!val && (fs.prec_opt != NPF_FMT_SPEC_OPT_NONE) && !fs.prec) { + // Zero value and explicitly-requested zero precision means "print nothing". + if ((fs.conv_spec == NPF_FMT_SPEC_CONV_OCTAL) && fs.alt_form) { + fs.prec = 1; // octal special case, print a single '0' + } + } else +#endif +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + if (fs.conv_spec == NPF_FMT_SPEC_CONV_BINARY) { + cbuf_len = npf_bin_len(val); u.binval = val; + } else +#endif + { + uint_fast8_t const base = (fs.conv_spec == NPF_FMT_SPEC_CONV_OCTAL) ? + 8u : ((fs.conv_spec == NPF_FMT_SPEC_CONV_HEX_INT) ? 16u : 10u); + cbuf_len = npf_utoa_rev(val, cbuf, base, fs.case_adjust); + } + + if (val && fs.alt_form && (fs.conv_spec == NPF_FMT_SPEC_CONV_OCTAL)) { + cbuf[cbuf_len++] = '0'; // OK to add leading octal '0' immediately. + } + + if (val && fs.alt_form) { // 0x or 0b but can't write it yet. + if (fs.conv_spec == NPF_FMT_SPEC_CONV_HEX_INT) { need_0x = 'X'; } +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + else if (fs.conv_spec == NPF_FMT_SPEC_CONV_BINARY) { need_0x = 'B'; } +#endif + if (need_0x) { need_0x += fs.case_adjust; } + } + } break; + + case NPF_FMT_SPEC_CONV_POINTER: { + cbuf_len = + npf_utoa_rev((npf_uint_t)(uintptr_t)va_arg(args, void *), cbuf, 16, 'a' - 'A'); + need_0x = 'x'; + } break; + +#if NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS == 1 + case NPF_FMT_SPEC_CONV_WRITEBACK: + switch (fs.length_modifier) { + NPF_WRITEBACK(NONE, int); + NPF_WRITEBACK(SHORT, short); + NPF_WRITEBACK(LONG, long); + NPF_WRITEBACK(LONG_DOUBLE, double); + NPF_WRITEBACK(CHAR, signed char); +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + NPF_WRITEBACK(LARGE_LONG_LONG, long long); + NPF_WRITEBACK(LARGE_INTMAX, intmax_t); + NPF_WRITEBACK(LARGE_SIZET, size_t); + NPF_WRITEBACK(LARGE_PTRDIFFT, ptrdiff_t); +#endif + default: break; + } break; +#endif + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + case NPF_FMT_SPEC_CONV_FLOAT_DEC: + case NPF_FMT_SPEC_CONV_FLOAT_SCI: + case NPF_FMT_SPEC_CONV_FLOAT_SHORTEST: + case NPF_FMT_SPEC_CONV_FLOAT_HEX: { + double val; + if (fs.length_modifier == NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE) { + val = (double)va_arg(args, long double); + } else { + val = va_arg(args, double); + } + + sign_c = (val < 0.) ? '-' : fs.prepend; +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + zero = (val == 0.); +#endif + cbuf_len = npf_ftoa_rev(cbuf, &fs, val); + } break; +#endif + default: break; + } + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + // Compute the field width pad character + if (fs.field_width_opt != NPF_FMT_SPEC_OPT_NONE) { + if (fs.leading_zero_pad) { // '0' flag is only legal with numeric types + if ((fs.conv_spec != NPF_FMT_SPEC_CONV_STRING) && + (fs.conv_spec != NPF_FMT_SPEC_CONV_CHAR) && + (fs.conv_spec != NPF_FMT_SPEC_CONV_PERCENT)) { +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + if ((fs.prec_opt != NPF_FMT_SPEC_OPT_NONE) && !fs.prec && zero) { + pad_c = ' '; + } else +#endif + { pad_c = '0'; } + } + } else { pad_c = ' '; } + } +#endif + + // Compute the number of bytes to truncate or '0'-pad. +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + if (fs.conv_spec != NPF_FMT_SPEC_CONV_STRING) { +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + // float precision is after the decimal point + if (fs.conv_spec != NPF_FMT_SPEC_CONV_FLOAT_DEC) +#endif + { prec_pad = npf_max(0, fs.prec - cbuf_len); } + } +#endif + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + // Given the full converted length, how many pad bytes? + field_pad = fs.field_width - cbuf_len - !!sign_c; + if (need_0x) { field_pad -= 2; } +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + field_pad -= prec_pad; +#endif + field_pad = npf_max(0, field_pad); + + // Apply right-justified field width if requested + if (!fs.left_justified && pad_c) { // If leading zeros pad, sign goes first. + if (pad_c == '0') { + if (sign_c) { NPF_PUTC(sign_c); sign_c = 0; } + // Pad byte is '0', write '0x' before '0' pad chars. + if (need_0x) { NPF_PUTC('0'); NPF_PUTC(need_0x); } + } + while (field_pad-- > 0) { NPF_PUTC(pad_c); } + // Pad byte is ' ', write '0x' after ' ' pad chars but before number. + if ((pad_c != '0') && need_0x) { NPF_PUTC('0'); NPF_PUTC(need_0x); } + } else +#endif + { if (need_0x) { NPF_PUTC('0'); NPF_PUTC(need_0x); } } // no pad, '0x' requested. + + // Write the converted payload + if (fs.conv_spec == NPF_FMT_SPEC_CONV_STRING) { + for (int i = 0; i < cbuf_len; ++i) { NPF_PUTC(cbuf[i]); } + } else { + if (sign_c) { NPF_PUTC(sign_c); } +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + while (prec_pad-- > 0) { NPF_PUTC('0'); } // int precision leads. +#endif +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + if (fs.conv_spec == NPF_FMT_SPEC_CONV_BINARY) { + while (cbuf_len) { NPF_PUTC('0' + ((u.binval >> --cbuf_len) & 1)); } + } else +#endif + { while (cbuf_len-- > 0) { NPF_PUTC(cbuf[cbuf_len]); } } // payload is reversed + } + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + if (fs.left_justified && pad_c) { // Apply left-justified field width + while (field_pad-- > 0) { NPF_PUTC(pad_c); } + } +#endif + } + + return pc_cnt.n; +} + +#undef NPF_PUTC +#undef NPF_EXTRACT +#undef NPF_WRITEBACK + +int npf_pprintf(npf_putc pc, void *pc_ctx, char const *format, ...) { + va_list val; + va_start(val, format); + int const rv = npf_vpprintf(pc, pc_ctx, format, val); + va_end(val); + return rv; +} + +int npf_snprintf(char *buffer, size_t bufsz, const char *format, ...) { + va_list val; + va_start(val, format); + int const rv = npf_vsnprintf(buffer, bufsz, format, val); + va_end(val); + return rv; +} + +int npf_vsnprintf(char *buffer, size_t bufsz, char const *format, va_list vlist) { + npf_bufputc_ctx_t bufputc_ctx; + bufputc_ctx.dst = buffer; + bufputc_ctx.len = bufsz; + bufputc_ctx.cur = 0; + + npf_putc const pc = buffer ? npf_bufputc : npf_bufputc_nop; + int const n = npf_vpprintf(pc, &bufputc_ctx, format, vlist); + pc('\0', &bufputc_ctx); + + if (buffer && bufsz) { +#ifdef NANOPRINTF_SNPRINTF_SAFE_EMPTY_STRING_ON_OVERFLOW + if (n >= (int)bufsz) { buffer[0] = '\0'; } +#else + buffer[bufsz - 1] = '\0'; +#endif + } + + return n; +} + +#if NANOPRINTF_HAVE_GCC_WARNING_PRAGMAS + #pragma GCC diagnostic pop +#endif + +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +#endif // NANOPRINTF_IMPLEMENTATION_INCLUDED +#endif // NANOPRINTF_IMPLEMENTATION + +/* + nanoprintf is dual-licensed under both the "Unlicense" and the + "Zero-Clause BSD" (0BSD) licenses. The intent of this dual-licensing + structure is to make nanoprintf as consumable as possible in as many + environments / countries / companies as possible without any + encumberances. + + The text of the two licenses follows below: + + ============================== UNLICENSE ============================== + + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to + + ================================ 0BSD ================================= + + Copyright (C) 2019- by Charles Nicholson + + Permission to use, copy, modify, and/or distribute this software for + any purpose with or without fee is hereby granted. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ diff --git a/boot/include/print.h b/boot/include/print.h new file mode 100644 index 0000000..dd0add0 --- /dev/null +++ b/boot/include/print.h @@ -0,0 +1,32 @@ +/*********************************************************************************/ +/* Module Name: print.h */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +#ifndef _PRINT_H +#define _PRINT_H + +#include +#include +#include + +void log(const char *fmt, ...); +void debug(const char *fmt, ...); + +void printstr(const char *str); + +#endif /* _PRINT_H */ \ No newline at end of file diff --git a/boot/include/utils.h b/boot/include/utils.h new file mode 100644 index 0000000..4a07cee --- /dev/null +++ b/boot/include/utils.h @@ -0,0 +1,28 @@ +/*********************************************************************************/ +/* Module Name: utils.h */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +#ifndef _UTILS_H +#define _UTILS_H + +#define ARRAY_LENGTH(x) ((sizeof(x)) / (sizeof((x[0])))) + +#define ROUND_DOWN(x, a) (((x)) & (~((a) - 1))) +#define ROUND_UP(x, a) ((((x)) + (a) - 1) & (~((a) - 1))) + +#endif /* _UTILS_H */ \ No newline at end of file diff --git a/boot/platform/pc-bios/Makefile b/boot/platform/pc-bios/Makefile new file mode 100644 index 0000000..09281c0 --- /dev/null +++ b/boot/platform/pc-bios/Makefile @@ -0,0 +1,64 @@ +################################################################################### +## Module Name: Makefile ## +## Project: AurixOS ## +## ## +## Copyright (c) 2024-2025 Jozef Nagy ## +## ## +## This source is subject to the MIT License. ## +## See License.txt in the root of this repository. ## +## All other rights reserved. ## +## ## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## +## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## +## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## +## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## +## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## +## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ## +## SOFTWARE. ## +################################################################################### + +ARCH_COMMON := x86 +INCLUDE_DIRS += include/arch/$(ARCH) + +BOOT_AS := nasm +BOOT_CC := $(ARCH)-elf-gcc +BOOT_LD := $(ARCH)-elf-ld + +BOOT_ASFLAGS := $(ASFLAGS) \ + $(foreach d, $(INCLUDE_DIRS), -I$d) +BOOT_CFLAGS := $(CFLAGS) \ + -mno-red-zone \ + -mno-stack-arg-probe + +BOOT_LDFLAGS := $(LDFLAGS) + +$(shell find . -type d \( -name arch -o -name platform \) -prune -name '*.c') +BOOT_ASFILES := $(shell find $(BOOT_ROOT)/arch/$(ARCH) -type d -name arch/$(ARCH)/uefi -prune -name '*.S') +BOOT_CFILES := $(shell find $(BOOT_ROOT)/arch/$(ARCH) -type d -name arch/$(ARCH)/uefi -prune -name '*.c') + +BOOT_OBJ := $(BOOT_CFILES:%.c=$(BUILD_DIR)/boot/boot/%.c.o) \ + $(BOOT_ASFILES:%.S=$(BUILD_DIR)/boot/boot/%.S.o) \ + $(COMMON_CFILES:common/%.c=$(BUILD_DIR)/boot/boot/common/%.c.o) + +# stage 1 bootloader +STAGE1_HDD := $(BUILD_DIR)/boot/pc-bios/stage1-hdd.bin +STAGE1_CD := $(BUILD_DIR)/boot/pc-bios/stage1-cd.bin +STAGE1 := $(STAGE1_HDD) $(STAGE1_CD) + +.PHONY: all +all: $(STAGE1) + +.PHONY: install +install: + @: + +# stage 1 +$(STAGE1_HDD): $(BOOT_ROOT)/arch/$(ARCH)/stage1/boot-hdd.asm + @mkdir -p $(@D) + @printf " AS\tboot/arch/$(ARCH)/stage1/stage1-hdd.bin\n" + @nasm -fbin -I$(BOOT_ROOT)/arch/$(ARCH)/stage1 $< -o $@ + +$(STAGE1_CD): $(BOOT_ROOT)/arch/$(ARCH)/stage1/boot-cd.asm + @mkdir -p $(@D) + @printf " AS\tboot/arch/$(ARCH)/stage1/stage1-cd.bin\n" + @nasm -fbin -I$(BOOT_ROOT)/arch/$(ARCH)/stage1 $< -o $@ diff --git a/boot/platform/raspi4/Makefile b/boot/platform/raspi4/Makefile new file mode 100644 index 0000000..ce9e895 --- /dev/null +++ b/boot/platform/raspi4/Makefile @@ -0,0 +1,85 @@ +################################################################################### +## Module Name: Makefile ## +## Project: AurixOS ## +## ## +## Copyright (c) 2024-2025 Jozef Nagy ## +## ## +## This source is subject to the MIT License. ## +## See License.txt in the root of this repository. ## +## All other rights reserved. ## +## ## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## +## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## +## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## +## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## +## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## +## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ## +## SOFTWARE. ## +################################################################################### + +BOOTFILE := $(BUILD_DIR)/boot/raspi4/axboot.elf + +INCLUDE_DIRS += $(BOOT_ROOT)/include \ + $(BOOT_ROOT)/include/arch/$(ARCH) \ + $(BOOT_ROOT)/include/platform/$(PLATFORM) + +BOOT_AS := $(ARCH)-elf-gcc +BOOT_CC := $(ARCH)-elf-gcc +BOOT_LD := $(ARCH)-elf-ld + +BOOT_ASFLAGS := $(ASFLAGS) \ + $(foreach d, $(INCLUDE_DIRS), -I$d) + +BOOT_CFLAGS := $(CFLAGS) \ + $(foreach d, $(INCLUDE_DIRS), -I$d) + +BOOT_LDFLAGS := $(LDFLAGS) + +COMMON_CFILES := $(shell find $(BOOT_ROOT)/common -name '*.c') +COMMON_ARCH_CFILES := $(shell find $(BOOT_ROOT)/arch/$(ARCH)/common -name '*.c') +COMMON_ARCH_ASFILES := $(shell find $(BOOT_ROOT)/arch/$(ARCH)/common -name '*.S') +PLATFORM_CFILES := $(shell find $(BOOT_ROOT)/platform/$(PLATFORM) -name '*.c') +PLATFORM_ASFILES := $(shell find $(BOOT_ROOT)/platform/$(PLATFORM) -name '*.S') + +BOOT_OBJ := $(COMMON_CFILES:$(BOOT_ROOT)/common/%.c=$(BUILD_DIR)/boot/raspi4/common/%.c.o) \ + $(COMMON_ARCH_CFILES:$(BOOT_ROOT)/arch/$(ARCH)/common/%.c=$(BUILD_DIR)/boot/raspi4/arch/%.c.o) \ + $(COMMON_ARCH_ASFILES:$(BOOT_ROOT)/arch/$(ARCH)/common/%.S=$(BUILD_DIR)/boot/raspi4/arch/%.S.o) \ + $(PLATFORM_CFILES:$(BOOT_ROOT)/platform/$(PLATFORM)/%.c=$(BUILD_DIR)/boot/raspi4/%.c.o) \ + $(PLATFORM_ASFILES:$(BOOT_ROOT)/platform/$(PLATFORM)/%.S=$(BUILD_DIR)/boot/raspi4/%.S.o) + +.PHONY: all +all: $(BOOTFILE) + @: + +.PHONY: install +install: all + @mkdir -p $(SYSROOT_DIR) + @printf " INSTALL\t/kernel.elf\n" + @cp $(BOOTFILE) $(SYSROOT_DIR)/kernel.elf + +$(BOOTFILE): $(BOOT_OBJ) + @mkdir -p $(@D) + @printf " LD\t$(notdir $@)\n" + @$(BOOT_LD) $(BOOT_LDFLAGS) $^ -o $@ + +-include $(wildcard $(BUILD_DIR)/boot/*.d) + +$(BUILD_DIR)/boot/raspi4/%.c.o: $(BOOT_ROOT)/platform/$(PLATFORM)/%.c + @mkdir -p $(@D) + @printf " CC\t$(subst $(ROOT_DIR)/,,$<)\n" + @$(BOOT_CC) $(BOOT_CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/boot/raspi4/common/%.c.o: $(BOOT_ROOT)/common/%.c + @mkdir -p $(@D) + @printf " CC\t$(subst $(ROOT_DIR)/,,$<)\n" + @$(BOOT_CC) $(BOOT_CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/boot/raspi4/arch/%.c.o: $(BOOT_ROOT)/arch/$(ARCH)/common/%.c + @mkdir -p $(@D) + @printf " CC\t$(subst $(ROOT_DIR)/,,$<)\n" + @$(BOOT_CC) $(BOOT_CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/boot/raspi4/arch/%.S.o: $(BOOT_ROOT)/arch/$(ARCH)/common/%.S + @mkdir -p $(@D) + @printf " AS\t$(subst $(ROOT_DIR)/,,$<)\n" + @$(BOOT_AS) $(BOOT_ASFLAGS) -c $< -o $@ \ No newline at end of file diff --git a/boot/platform/uefi/Makefile b/boot/platform/uefi/Makefile new file mode 100644 index 0000000..4abcba3 --- /dev/null +++ b/boot/platform/uefi/Makefile @@ -0,0 +1,116 @@ +################################################################################### +## Module Name: Makefile ## +## Project: AurixOS ## +## ## +## Copyright (c) 2024-2025 Jozef Nagy ## +## ## +## This source is subject to the MIT License. ## +## See License.txt in the root of this repository. ## +## All other rights reserved. ## +## ## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## +## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## +## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## +## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## +## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## +## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ## +## SOFTWARE. ## +################################################################################### + +ifeq ($(ARCH),x86_64) +UEFISUF := X64 +else ifeq ($(ARCH),i686) +UEFISUF := IA32 +else ifeq ($(ARCH),arm) +UEFISUF := ARM +else ifeq ($(ARCH),aarch64) +UEFISUF := AA64 +else ifeq ($(ARCH),riscv64) +UEFISUF := RISCV64 +else +$(error Architecture not supported!) +endif + +UEFI_BOOTFILE := $(BUILD_DIR)/boot/uefi/BOOT$(UEFISUF).EFI + +INCLUDE_DIRS += $(BOOT_ROOT)/include \ + $(BOOT_ROOT)/include/arch/$(ARCH) \ + $(BOOT_ROOT)/include/arch/$(ARCH)/uefi \ + $(BOOT_ROOT)/include/platform/$(PLATFORM) \ + $(BOOT_ROOT)/platform/uefi/libefi + +UEFI_AS := $(ARCH)-w64-mingw32-as +UEFI_CC := clang +UEFI_LD := clang + +UEFI_ASFLAGS := $(ASFLAGS) \ + -DAXBOOT_UEFI=1 \ + $(foreach d, $(INCLUDE_DIRS), -I$d) + +UEFI_CFLAGS := $(CFLAGS) \ + -DAXBOOT_UEFI=1 \ + $(foreach d, $(INCLUDE_DIRS), -I$d) \ + -target $(ARCH)-unknown-windows \ + -fshort-wchar \ + -mno-red-zone \ + -mno-stack-arg-probe + +ifneq (,$(filter $(ARCH),i686 x86_64)) +UEFI_CFLAGS += -mno-80387 \ + -mno-mmx \ + -mno-sse \ + -mno-sse2 +endif + +UEFI_LDFLAGS := $(LDFLAGS) \ + -target $(ARCH)-unknown-windows \ + -fuse-ld=lld-link \ + -Wl,-subsystem:efi_application \ + -Wl,-entry:uefi_entry +COMMON_CFILES := $(shell find $(BOOT_ROOT)/common -name '*.c') +COMMON_ARCH_CFILES := $(shell find $(BOOT_ROOT)/arch/$(ARCH)/common -name '*.c') +COMMON_ARCH_ASFILES := $(shell find $(BOOT_ROOT)/arch/$(ARCH)/common -name '*.S') +UEFI_CFILES := $(shell find $(BOOT_ROOT)/platform/uefi -name '*.c') $(shell find $(BOOT_ROOT)/arch/$(ARCH)/uefi -name '*.c') +UEFI_ASFILES := $(shell find $(BOOT_ROOT)/platform/uefi -name '*.S') $(shell find $(BOOT_ROOT)/arch/$(ARCH)/uefi -name '*.S') + +UEFI_OBJ := $(UEFI_CFILES:$(BOOT_ROOT)/platform/uefi/%.c=$(BUILD_DIR)/boot/uefi/%.c.o) \ + $(UEFI_ASFILES:$(BOOT_ROOT)/platform/uefi/%.S=$(BUILD_DIR)/boot/uefi/%.S.o) \ + $(COMMON_CFILES:$(BOOT_ROOT)/common/%.c=$(BUILD_DIR)/boot/uefi/common/%.c.o) \ + $(COMMON_ARCH_CFILES:$(BOOT_ROOT)/arch/$(ARCH)/common/%.c=$(BUILD_DIR)/boot/uefi/arch/%.c.o) \ + $(COMMON_ARCH_ASFILES:$(BOOT_ROOT)/arch/$(ARCH)/common/%.asm=$(BUILD_DIR)/boot/uefi/arch/%.asm.o) + +.PHONY: all +all: $(UEFI_BOOTFILE) + +.PHONY: install +install: + @mkdir -p $(SYSROOT_DIR)/EFI/BOOT + @printf " INSTALL\t/EFI/BOOT/BOOT$(UEFISUF).EFI\n" + @cp $(UEFI_BOOTFILE) $(SYSROOT_DIR)/EFI/BOOT/ + +$(UEFI_BOOTFILE): $(UEFI_OBJ) + @mkdir -p $(@D) + @printf " LD\t$(notdir $@)\n" + @$(UEFI_LD) $(UEFI_LDFLAGS) $^ -o $@ + +-include $(wildcard $(BUILD_DIR)/boot/*.d) + +$(BUILD_DIR)/boot/uefi/%.c.o: $(BOOT_ROOT)/platform/uefi/%.c + @mkdir -p $(@D) + @printf " CC\t$(subst $(ROOT_DIR)/,,$<)\n" + @$(UEFI_CC) $(UEFI_CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/boot/uefi/common/%.c.o: $(BOOT_ROOT)/common/%.c + @mkdir -p $(@D) + @printf " CC\t$(subst $(ROOT_DIR)/,,$<)\n" + @$(UEFI_CC) $(UEFI_CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/boot/uefi/arch/%.c.o: $(BOOT_ROOT)/arch/$(ARCH)/common/%.c + @mkdir -p $(@D) + @printf " CC\t$(subst $(ROOT_DIR)/,,$<)\n" + @$(UEFI_CC) $(UEFI_CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/boot/uefi/arch/%.asm.o: $(BOOT_ROOT)/arch/$(ARCH)/common/%.asm + @mkdir -p $(@D) + @printf " AS\t$(subst $(ROOT_DIR)/,,$<)\n" + @$(UEFI_AS) $(UEFI_ASFLAGS) -c $< -o $@ \ No newline at end of file diff --git a/boot/platform/uefi/entry.c b/boot/platform/uefi/entry.c new file mode 100644 index 0000000..90be214 --- /dev/null +++ b/boot/platform/uefi/entry.c @@ -0,0 +1,46 @@ +/*********************************************************************************/ +/* Module Name: entry.c */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +#include +#include + +#include + +#include + +EFI_STATUS uefi_entry(EFI_HANDLE ImageHandle, + EFI_SYSTEM_TABLE *SystemTable) +{ + EFI_STATUS Status; + + gImageHandle = ImageHandle; + gSystemTable = SystemTable; + + // clear the screen + gSystemTable->ConOut->ClearScreen(gSystemTable->ConOut); + + // disable UEFI watchdog + Status = gSystemTable->BootServices->SetWatchdogTimer(0, 0, 0, NULL); + if (EFI_ERROR(Status)) { + debug("Couldn't disable UEFI watchdog!\n"); + } + + while(1); + return EFI_SUCCESS; +} \ No newline at end of file diff --git a/boot/platform/uefi/libefi b/boot/platform/uefi/libefi new file mode 160000 index 0000000..00ed5f5 --- /dev/null +++ b/boot/platform/uefi/libefi @@ -0,0 +1 @@ +Subproject commit 00ed5f5d53641d48024d8a1346edef07d1d79421 diff --git a/boot/platform/uefi/print.c b/boot/platform/uefi/print.c new file mode 100644 index 0000000..908f61e --- /dev/null +++ b/boot/platform/uefi/print.c @@ -0,0 +1,31 @@ +/*********************************************************************************/ +/* Module Name: print.c */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +#include +#include +#include +#include + +void printstr(const char *str) +{ + CHAR16 wstr[4096]; + mbstowcs(wstr, &str, strlen(str)); + wstr[strlen(str)] = '\0'; + gSystemTable->ConOut->OutputString(gSystemTable->ConOut, wstr); +} \ No newline at end of file diff --git a/docs/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..c62ab17 --- /dev/null +++ b/docs/CODE_OF_CONDUCT.md @@ -0,0 +1,45 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the project or its community in public spaces. Examples of representing a project or community include using an official project email address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project maintainer using any of the [private contact addresses](https://github.com/aurixos/os#support). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 1.4, available at + +For answers to common questions about this code of conduct, see diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md new file mode 100644 index 0000000..b8dc4ee --- /dev/null +++ b/docs/CONTRIBUTING.md @@ -0,0 +1,46 @@ +# Contributing + +When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change. +Please note we have a [code of conduct](CODE_OF_CONDUCT.md), please follow it in all your interactions with the project. + +## Development environment setup + +To set up a development environment, follow these steps: + +1. Clone the repo + + ```sh + git clone https://github.com/aurixos/os + ``` + +2. Install all required packages + + ```sh + brew bundle + ``` + + If you don't have Homebrew installed, or don't want to download all optional packages, look at the [Brewfile](../Brewfile) and install all packages manually. + +## Issues and feature requests + +You've found a bug in the source code, a mistake in the documentation or maybe you'd like a new feature? You can help us by [submitting an issue on GitHub](https://github.com/aurixos/os/issues). Before you create an issue, make sure to search the issue archive -- your issue may have already been addressed! + +Please try to create bug reports that are: + +- _Reproducible._ Include steps to reproduce the problem. +- _Specific._ Include as much detail as possible: which version, what environment, etc. +- _Unique._ Do not duplicate existing opened issues. +- _Scoped to a Single Bug._ One bug per report. + +**Even better: Submit a pull request with a fix or new feature!** + +### How to submit a Pull Request + +1. Search our repository for open or closed + [Pull Requests](https://github.com/aurixos/os/pulls) + that relate to your submission. You don't want to duplicate effort. +2. Fork the project +3. Create your feature branch (`git checkout -b feat/amazing_feature`) +4. Commit your changes (`git commit -m 'feat: add amazing_feature'`) AurixOS uses [conventional commits](https://www.conventionalcommits.org), so please follow the specification in your commit messages. +5. Push to the branch (`git push origin feat/amazing_feature`) +6. [Open a Pull Request](https://github.com/aurixos/os/compare?expand=1) diff --git a/docs/SECURITY.md b/docs/SECURITY.md new file mode 100644 index 0000000..bbf1ccb --- /dev/null +++ b/docs/SECURITY.md @@ -0,0 +1,16 @@ +# Security Policy + +## Reporting a Vulnerability + +If there are any vulnerabilities in **AurixOS**, don't hesitate to _report them_. + +1. Contact [schkwve@gmail.com](mailto:schkwve@gmail.com). +2. Describe the vulnerability. + + If you have a fix, that is most welcome -- please attach or summarize it in your message! + +3. We will evaluate the vulnerability and, if necessary, release a fix or mitigating steps to address it. We will contact you to let you know the outcome, and will credit you in the report. + + Please **do not disclose the vulnerability publicly** until a fix is released! + +4. Once we have either a) published a fix, or b) declined to address the vulnerability for whatever reason, you are free to publicly disclose it. diff --git a/docs/boot/BOOTPROTOCOL.md b/docs/boot/BOOTPROTOCOL.md new file mode 100644 index 0000000..eb305bc --- /dev/null +++ b/docs/boot/BOOTPROTOCOL.md @@ -0,0 +1,117 @@ +# Aurix Boot Protocol (revision 0.2) + +The Aurix Boot Protocol presents a simple and minimal protocol for booting the AurixOS kernel. + +> [!NOTE] +> This document is still a work in progress and may contain incomplete information. + +## Machine state + +- All general purpose registers are zeroed out +- Interrupts are disabled + +- Framebuffer is set to the best available video mode (graphics mode if available) + +### Architecture-specific + +#### x86_64 + +- Write Protection bit in CR0 is disabled + +- GDT is set up as follows: + +| Name | Base | Limit | Flags | +| :--------------------- | :----: | :----------: | :--------: | +| NULL Descriptor | `0x00` | `0x0000` | `0x00` | +| 32-bit Code Descriptor | `0x00` | `0xFFFFFFFF` | Read only | +| 32-bit Data Descriptor | `0x00` | `0xFFFFFFFF` | Read/Write | +| 64-bit Code Descriptor | `0x00` | `0x0000` | Read only | +| 64-bit Data Descriptor | `0x00` | `0x0000` | Read/Write | + +## Paging + +- ~~If available, 5-level paging is set up (see [Kernel Parameters](#kernel-parameters))~~ 5-level paging is not yet supported in AxBoot +- The memory map is identity mapped +- Kernel is mapped to the higher half if desired + +## Kernel parameters + +The bootloader passes `abp_boot_info` structure as a parameter to the kernel. + +A non-zero value in `lvl5_paging` indicates that 5-level paging has been set up and is available. + +```c +struct abp_boot_info { + // General + char *bootloader_name; + char *bootloader_version; + char *protocol_version; + + // ACPI and SMBIOS + struct acpi_info acpi; + struct smbios_info smbios; + + // Memory + struct memory_map *memmap; + int lvl5_paging; + + // Framebuffer + struct framebuffer_info framebuffer; +}; +``` + +## ACPI and SMBIOS + +These structures contain pointers to the Root System Description Pointer (ACPI) and System Management BIOS Entry Point (SMBIOS). +If `is_valid` is set to a non-zero value, the pointer is guaranteed to be valid. +Otherwise, the pointer is set to NULL and should not be used. + +```c +struct acpi_info { + uint8_t is_valid; + void *rsdp; +}; +``` + +```c +struct smbios_info { + uint8_t is_valid; + void *entry_point; +}; +``` + +## Memory map + +The memory map is a singly linked list containing the physical address of the entry, its length and type, as well as a pointer to the next entry. + +Entries are guaranteed to not overlap with each other, and sorted by base address from low to high. + +```c +#define ABP_MEMORY_RESERVED 0xf0 +#define ABP_MEMORY_USABLE 0xf1 +#define ABP_MEMORY_BOOTLOADER_RECLAIMABLE 0xf2 +#define ABP_MEMORY_MMIO 0xf3 +#define ABP_MEMORY_ACPI_NVS 0xf4 +#define ABP_MEMORY_ACPI_RECLAIMABLE 0xf5 +#define ABP_MEMORY_KERNEL 0xf7 +#define ABP_MEMORY_NOT_USABLE 0xff + +struct memory_map { + uint64_t base; + uint64_t length; + uint64_t type; + + struct memory_map *next; +}; +``` + +## Framebuffer + +```c +struct framebuffer_info { + void *addr; + uint32_t width; + uint32_t height; + uint16_t bpp; +}; +``` diff --git a/docs/boot/CONFIG.md b/docs/boot/CONFIG.md new file mode 100644 index 0000000..281ddf3 --- /dev/null +++ b/docs/boot/CONFIG.md @@ -0,0 +1,51 @@ +# Soapine configuration +To work and do what you told it to do, Soapine uses a configuration file. + +## Location +Soapine will search for the configuration in: +* `(boot partition)\soapine.cfg` +* `(boot partition)\soapine\soapine.cfg` +* `(boot partition)\EFI\BOOT\soapine.cfg` +* `(boot partition)\EFI\soapine.cfg` + +If Soapine finds the config file, he parses it and jump to his usual menu. Else, Soapine will display a stop screen saying that you need to fix your configuration + +## Accepted value types +* String literal (`"Hello, World!"`) +* Decimal (`2`) +* Hexadecimal (`0x2`) +* Hexadecimal color (`#FFFFFF`) +* Boolean (`true/false`) + +## Declarations +Declarations are values that components of Soapine will search for: +If you declare `VERBOSE` with a value of true, Soapine will itself put in verbose mode. + +You can do a declaration by writing the name + an equal sign + the value (using the accepted value types), that will make `NAME=VALUE`. + +If an unused declaration is provided (for example `FORCE_SOAPINE_TO_LIKE_ME=true`), Soapine will simply ignore it, but it will still be present. + +Here are some example declarations: +* `MENU_BRANDING="Raphaรซl's Custom Soapine!!"` (string literal) +* `MENU_HEADERBAR_BG=#FFFFFF` (Hexadecimal color) +* `MENU_HEADERBAR_MARGIN=1` (decimal) +* `VERBOSE=true` (boolean) +* `LOAD_ADDRESS=0x1000` (hexadecimal) + +## Menu entries +Menu entries allow you to show an operating system (that can be loaded with the supported protocols!) on the menu. +They are declared like that: + +```c +menu_entry "Project Jupiter" { + +}; +``` +*(yes i decided to give a C-like syntax to it)* + +You can put a small number of declarations inside the menu entries. (PROTOCOL, IMAGE_PATH, CMDLINE, RESOLUTION) (Providing the `PROTOCOL` and `IMAGE_PATH` declarations is required for Soapine to boot your OS!) + +## Supported declarations +None for now (we just got the config parser working!) + +At least, you can still define entries! \ No newline at end of file diff --git a/docs/boot/PHILOSOPHY.md b/docs/boot/PHILOSOPHY.md new file mode 100644 index 0000000..ab2f903 --- /dev/null +++ b/docs/boot/PHILOSOPHY.md @@ -0,0 +1,10 @@ +# Soapine's philosophy +Soapine is meant to be lightweight, while being useful to everyone: + +* Ship the bootloader with multiple features (even the weirdest features) +* Ability to extend the bootloader with ELF extensions: If you wanna write an extension to support PE loading, ***DO IT***. + +Soapine is also meant to be customizable: + +* You can modify each bit of the bootloader: If you wanna center the headerbar text, you ***CAN*** +* You can change the default values in Soapine's source code. \ No newline at end of file diff --git a/docs/images/logo.png b/docs/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4b8555c30a344d8727e7e7b25511b2940ebe752b GIT binary patch literal 61466 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%0DSwk& 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 S>iyY;^-*5+eZ@P|!2bfnvDh#G literal 0 HcmV?d00001 diff --git a/kernel/Makefile b/kernel/Makefile new file mode 100644 index 0000000..b98745c --- /dev/null +++ b/kernel/Makefile @@ -0,0 +1,135 @@ +################################################################################### +## Module Name: Makefile ## +## Project: AurixOS ## +## ## +## Copyright (c) 2024 Jozef Nagy ## +## ## +## This source is subject to the MIT License. ## +## See License.txt in the root of this repository. ## +## All other rights reserved. ## +## ## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## +## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## +## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## +## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## +## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## +## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ## +## SOFTWARE. ## +################################################################################### + +.DEFAULT_GOAL := all + +INCLUDE_DIRS := include + +BUILD_DIR ?= build +SYSROOT_DIR ?= sysroot + +KERNEL_FILE := $(BUILD_DIR)/axkrnl + +INCLUDE_DIRS := include \ + include/arch/$(ARCH) \ + include/platform/$(PLATFORM) +DEFINES := __$(ARCH)__ + +KERNEL_AS := $(ARCH)-elf-gcc +KERNEL_CC := $(ARCH)-elf-gcc +KERNEL_LD := $(ARCH)-elf-ld +KERNEL_OBJCOPY := $(ARCH)-elf-objcopy + +KERNEL_ASFLAGS := $(foreach d, $(INCLUDE_DIRS), -I$d) \ + $(foreach d, $(DEFINES), -D$d) + +KERNEL_CFLAGS := $(foreach d, $(INCLUDE_DIRS), -I$d) \ + $(foreach d, $(DEFINES), -D$d) \ + -D__$(ARCH) \ + -std=c99 \ + -ffreestanding \ + -fno-omit-frame-pointer \ + -fno-stack-protector \ + -fno-stack-check \ + -fno-pie \ + -MMD \ + -MP + +KERNEL_LDFLAGS := -Tarch/$(ARCH)/linker.ld \ + -nostdlib \ + -static \ + -no-pie + +ifeq ($(DEBUG),yes) +KERNEL_CFLAGS += -g3 +else +KERNEL_CFLAGS += -O2 +endif + +KERNEL_CFILES := $(shell find . -type d \( -name arch -o -name platform \) -prune -name '*.c') +KERNEL_ASFILES := $(shell find . -type d \( -name arch -o -name platform \) -prune -name '*.S') +KERNEL_ARCH_CFILES := $(shell find arch/$(ARCH) -name '*.c') +KERNEL_ARCH_ASFILES := $(shell find arch/$(ARCH) -name '*.S') +KERNEL_PLATFORM_CFILES := $(shell find platform/$(PLATFORM) -name '*.c') +KERNEL_PLATFORM_ASFILES := $(shell find platform/$(PLATFORM) -name '*.S') + +KERNEL_OBJ := $(KERNEL_CFILES:%.c=$(BUILD_DIR)/kernel/%.c.o) \ + $(KERNEL_ASFILES:%.S=$(BUILD_DIR)/kernel/%.S.o) \ + $(KERNEL_ARCH_CFILES:arch/$(ARCH)/%.c=$(BUILD_DIR)/kernel/arch/%.c.o) \ + $(KERNEL_ARCH_ASFILES:arch/$(ARCH)/%.S=$(BUILD_DIR)/kernel/arch/%.S.o) \ + $(KERNEL_PLATFORM_CFILES:platform/$(PLATFORM)/%.c=$(BUILD_DIR)/kernel/platform/%.c.o) \ + $(KERNEL_PLATFORM_ASFILES:platform/$(PLATFORM)/%.S=$(BUILD_DIR)/kernel/platform/%.S.o) + +-include arch/$(ARCH)/config.mk + +-include $(wildcard $(BUILD_DIR)/kernel/*.d) + +$(KERNEL_FILE): $(KERNEL_OBJ) + @mkdir -p $(@D) + @printf " LD\t$(notdir $@)\n" + @$(KERNEL_LD) $(KERNEL_LDFLAGS) $^ -o $@ +ifeq ($(DEBUG),yes) + @printf " OBJCOPY axkrnl.sym\n" + @$(KERNEL_OBJCOPY) --only-keep-debug $@ $(BUILD_DIR)/axkrnl.sym + @$(KERNEL_OBJCOPY) --strip-debug --strip-unneeded $@ +endif + +$(BUILD_DIR)/kernel/%.c.o: %.c + @mkdir -p $(@D) + @printf " CC\t$<\n" + @$(KERNEL_CC) $(KERNEL_CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/kernel/arch/%.c.o: arch/$(ARCH)/%.c + @mkdir -p $(@D) + @printf " CC\t$<\n" + @$(KERNEL_CC) $(KERNEL_CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/kernel/arch/%.S.o: arch/$(ARCH)/%.S + @mkdir -p $(@D) + @printf " AS\t$<\n" + @$(KERNEL_AS) $(KERNEL_ASFLAGS) -c $< -o $@ + +$(BUILD_DIR)/kernel/platform/%.c.o: platform/$(PLATFORM)/%.c + @mkdir -p $(@D) + @printf " CC\t$<\n" + @$(KERNEL_CC) $(KERNEL_CFLAGS) -c $< -o $@ + +$(BUILD_DIR)/kernel/platform/%.S.o: platform/$(PLATFORM)/%.S + @mkdir -p $(@D) + @printf " AS\t$<\n" + @$(KERNEL_AS) $(KERNEL_ASFLAGS) -c $< -o $@ + +.PHONY: all +all: kernel + +.PHONY: kernel +kernel: $(KERNEL_FILE) + +.PHONY: install +install: install-kernel + +.PHONY: install-kernel +install-kernel: kernel + @mkdir -p $(SYSROOT_DIR)/System + @printf " INSTALL\t/System/$(notdir $(KERNEL_FILE))\n" + @cp $(KERNEL_FILE) $(SYSROOT_DIR)/System + +.PHONY: clean +clean: + @rm -rf $(BUILD_DIR) $(SYSROOT_DIR) diff --git a/kernel/arch/aarch64/config.mk b/kernel/arch/aarch64/config.mk new file mode 100644 index 0000000..36282dc --- /dev/null +++ b/kernel/arch/aarch64/config.mk @@ -0,0 +1,23 @@ +################################################################################### +## Module Name: config.mk ## +## Project: AurixOS ## +## ## +## Copyright (c) 2024-2025 Jozef Nagy ## +## ## +## This source is subject to the MIT License. ## +## See License.txt in the root of this repository. ## +## All other rights reserved. ## +## ## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## +## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## +## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## +## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## +## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## +## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ## +## SOFTWARE. ## +################################################################################### + +KERNEL_CFLAGS += + +KERNEL_LDFLAGS += -nostdlib \ + --no-dynamic-linker diff --git a/kernel/arch/aarch64/linker.ld b/kernel/arch/aarch64/linker.ld new file mode 100644 index 0000000..8579730 --- /dev/null +++ b/kernel/arch/aarch64/linker.ld @@ -0,0 +1,60 @@ +/*********************************************************************************/ +/* Module Name: linker.ld */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +/* This is just a copied linker from the bootloader. */ +/* Not cool. But it's good enough... for now. */ + +SECTIONS +{ + . = 0x80000; + .text : { + *(.text .text.*) + *(.gnu.linkonce.t*) + } + + .rodata : { + *(.rodata .rodata.*) + *(.gnu.linkonce.r*) + } + + PROVIDE(_data = .); + + .data : { + *(.data .data.*) + *(.gnu.linkonce.d*) + } + + .bss (NOLOAD) : { + . = ALIGN(16); + __bss_start = .; + *(.bss .bss.*) + *(COMMON) + __bss_end = .; + } + _end = .; + + /DISCARD/ : { + *(.comment) + *(.gnu*) + *(.note*) + *(.eh_frame*) + } +} + +/*__bss_size = (__bss_end - __bss_start) >> 3;*/ diff --git a/kernel/arch/x86_64/config.mk b/kernel/arch/x86_64/config.mk new file mode 100644 index 0000000..7be3279 --- /dev/null +++ b/kernel/arch/x86_64/config.mk @@ -0,0 +1,33 @@ +################################################################################### +## Module Name: config.mk ## +## Project: AurixOS ## +## ## +## Copyright (c) 2024-2025 Jozef Nagy ## +## ## +## This source is subject to the MIT License. ## +## See License.txt in the root of this repository. ## +## All other rights reserved. ## +## ## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ## +## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ## +## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ## +## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ## +## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ## +## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ## +## SOFTWARE. ## +################################################################################### + +KERNEL_CFLAGS += -m64 \ + -march=x86-64 \ + -mabi=sysv \ + -mno-red-zone \ + -mno-80387 \ + -mno-mmx \ + -mno-sse \ + -mno-sse2 + +KERNEL_LDFLAGS += -nostdlib \ + --no-dynamic-linker \ + -z max-page-size=0x1000 \ + -z text \ + -melf_x86_64 diff --git a/kernel/arch/x86_64/linker.ld b/kernel/arch/x86_64/linker.ld new file mode 100644 index 0000000..4660482 --- /dev/null +++ b/kernel/arch/x86_64/linker.ld @@ -0,0 +1,78 @@ +/*********************************************************************************/ +/* Module Name: linker.ld */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +OUTPUT_FORMAT(elf64-x86-64) +OUTPUT_ARCH(i386:x86-64) +ENTRY(_start) + +PHDRS +{ + text PT_LOAD FLAGS((1 << 0) | (1 << 2)); + rodata PT_LOAD FLAGS(1 << 2); + data PT_LOAD FLAGS((1 << 1) | (1 << 2)); + dynamic PT_DYNAMIC FLAGS((1 << 1) | (1 << 2)); +} + +SECTIONS +{ + /* AxBoot remaps this to 0xffffffff80000000 for us */ + . = 0x1000; + + _linker_start_text = .; + + .text : { + *(.text .text.*) + } :text + + _linker_end_text = .; + + . = ALIGN(CONSTANT(MAXPAGESIZE)); + + _linker_start_rodata = .; + + .rodata : { + *(.rodata .rodata.*) + } :rodata + + _linker_end_rodata = .; + + . = ALIGN(CONSTANT(MAXPAGESIZE)); + + _linker_start_data = .; + + .data : { + *(.data .data.*) + } :data + + .dynamic : { + *(.dynamic) + } :data :dynamic + + .bss : { + *(COMMON) + *(.bss .bss.*) + } :data + + _linker_end_data = .; + + /DISCARD/ : { + *(.eh_frame) + *(.note .note.*) + } +} \ No newline at end of file diff --git a/kernel/include/arch/aarch64/arch/cpu/cpu.h b/kernel/include/arch/aarch64/arch/cpu/cpu.h new file mode 100644 index 0000000..4660de0 --- /dev/null +++ b/kernel/include/arch/aarch64/arch/cpu/cpu.h @@ -0,0 +1,23 @@ +/*********************************************************************************/ +/* Module Name: cpu.h */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +#ifndef _ARCH_CPU_CPU_H +#define _ARCH_CPU_CPU_H + +#endif /* _ARCH_CPU_CPU_H */ \ No newline at end of file diff --git a/kernel/include/arch/x86_64/arch/cpu/cpu.h b/kernel/include/arch/x86_64/arch/cpu/cpu.h new file mode 100644 index 0000000..d4aa6f5 --- /dev/null +++ b/kernel/include/arch/x86_64/arch/cpu/cpu.h @@ -0,0 +1,116 @@ +/*********************************************************************************/ +/* Module Name: cpu.h */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +#ifndef _ARCH_CPU_CPU_H +#define _ARCH_CPU_CPU_H + +#include + +//// +// Utilities +/// + +static inline void cpu_disable_interrupts(void) +{ + __asm__ volatile("cli" ::: "memory"); +} + +static inline uint64_t read_cr0() +{ + uint64_t val; + __asm__ volatile("mov %%cr0, %0" + : "=r"(val)); + return val; +} + +static inline uint64_t read_cr2() +{ + uint64_t val; + __asm__ volatile("mov %%cr2, %0" + : "=r"(val)); + return val; +} + +static inline uint64_t read_cr3() +{ + uint64_t val; + __asm__ volatile("mov %%cr3, %0" + : "=r"(val)); + return val; +} + +static inline uint64_t read_cr4() +{ + uint64_t val; + __asm__ volatile("mov %%cr4, %0" + : "=r"(val)); + return val; +} + +static inline uint64_t read_cr8() +{ + uint64_t val; + __asm__ volatile("mov %%cr8, %0" + : "=r"(val)); + return val; +} + +static inline void write_cr0(uint64_t val) +{ + __asm__ volatile("mov %0, %%cr0" + :: "r"(val)); +} + +static inline void write_cr2(uint64_t val) +{ + __asm__ volatile("mov %0, %%cr2" + :: "r"(val)); +} + +static inline void write_cr3(uint64_t val) +{ + __asm__ volatile("mov %0, %%cr3" + :: "r"(val) : "memory"); +} + +static inline void write_cr4(uint64_t val) +{ + __asm__ volatile("mov %0, %%cr4" + :: "r"(val)); +} + +static inline void write_cr8(uint64_t val) +{ + __asm__ volatile("mov %0, %%cr8" + :: "r"(val)); +} + +static inline uint8_t inb(uint16_t port) +{ + uint8_t ret; + __asm__ volatile("inb %w1, %b0" : "=a"(ret) : "Nd"(port) : "memory"); + return ret; +} + +static inline void outb(uint16_t port, uint8_t val) +{ + __asm__ volatile("outb %b0, %w1" :: "a"(val), "Nd"(port) : "memory"); +} + +#endif /* _ARCH_CPU_CPU_H */ \ No newline at end of file diff --git a/kernel/include/debug/uart.h b/kernel/include/debug/uart.h new file mode 100644 index 0000000..b0abbc2 --- /dev/null +++ b/kernel/include/debug/uart.h @@ -0,0 +1,30 @@ +/*********************************************************************************/ +/* Module Name: uart.h */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +#ifndef _DEBUG_SERIAL_H +#define _DEBUG_SERIAL_H + +#include + +void serial_init(void); + +void serial_send(char c); +void serial_sendstr(char *s); + +#endif /* _DEBUG_SERIAL_H */ \ No newline at end of file diff --git a/kernel/include/platform/generic-pc/platform/debug/uart.h b/kernel/include/platform/generic-pc/platform/debug/uart.h new file mode 100644 index 0000000..a84063a --- /dev/null +++ b/kernel/include/platform/generic-pc/platform/debug/uart.h @@ -0,0 +1,32 @@ +/*********************************************************************************/ +/* Module Name: serial.h */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +#ifndef _MACHINE_DEBUG_SERIAL_H +#define _MACHINE_DEBUG_SERIAL_H + +#define COM1 0x3f8 +#define COM2 0x2f8 +#define COM3 0x3e8 +#define COM4 0x2e8 +#define COM5 0x5f8 +#define COM6 0x4f8 +#define COM7 0x5e8 +#define COM8 0x4e8 + +#endif /* _MACHINE_DEBUG_SERIAL_H */ \ No newline at end of file diff --git a/kernel/include/platform/raspi4/platform/debug/uart.h b/kernel/include/platform/raspi4/platform/debug/uart.h new file mode 100644 index 0000000..4293725 --- /dev/null +++ b/kernel/include/platform/raspi4/platform/debug/uart.h @@ -0,0 +1,23 @@ +/*********************************************************************************/ +/* Module Name: serial.h */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +#ifndef _MACHINE_DEBUG_SERIAL_H +#define _MACHINE_DEBUG_SERIAL_H + +#endif /* _MACHINE_DEBUG_SERIAL_H */ \ No newline at end of file diff --git a/kernel/kinit.c b/kernel/kinit.c new file mode 100644 index 0000000..7fff330 --- /dev/null +++ b/kernel/kinit.c @@ -0,0 +1,34 @@ +/*********************************************************************************/ +/* Module Name: kinit.c */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +#include + +void _start(void) +{ + serial_init(); + serial_sendstr("Hello from AurixOS!\n"); + + for (;;) { +#ifdef __x86_64__ + __asm__ volatile("cli;hlt"); +#elif __aarch64__ + __asm__ volatile("wfe"); +#endif + } +} \ No newline at end of file diff --git a/kernel/platform/generic-pc/debug/uart/uart.c b/kernel/platform/generic-pc/debug/uart/uart.c new file mode 100644 index 0000000..f2b9e6d --- /dev/null +++ b/kernel/platform/generic-pc/debug/uart/uart.c @@ -0,0 +1,63 @@ +/*********************************************************************************/ +/* Module Name: uart.c */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +#include +#include + +#include + +static uint8_t is_tx_empty(void) +{ + return inb(COM1 + 5) & 0x20; +} + +static uint8_t received(void) +{ + return inb(COM1 + 5) & 1; +} + +void serial_init(void) +{ + // TODO: Initialize all COM ports + outb(COM1 + 1, 0x00); + outb(COM1 + 3, 0x80); + outb(COM1, 0x03); + outb(COM1 + 1, 0x00); + outb(COM1 + 3, 0x03); + outb(COM1 + 2, 0xC7); + outb(COM1 + 4, 0x0B); + outb(COM1 + 4, 0x0F); +} + +void serial_send(char c) +{ + while (is_tx_empty() == 0); + outb(COM1, c); +} + +void serial_sendstr(char *s) +{ + while (*s != '\0') { + if (*s == '\r') { + s++; + continue; + } + serial_send(*s++); + } +} \ No newline at end of file diff --git a/kernel/platform/raspi4/debug/uart/uart.c b/kernel/platform/raspi4/debug/uart/uart.c new file mode 100644 index 0000000..3137654 --- /dev/null +++ b/kernel/platform/raspi4/debug/uart/uart.c @@ -0,0 +1,34 @@ +/*********************************************************************************/ +/* Module Name: uart.c */ +/* Project: AurixOS */ +/* */ +/* Copyright (c) 2024-2025 Jozef Nagy */ +/* */ +/* This source is subject to the MIT License. */ +/* See License.txt in the root of this repository. */ +/* All other rights reserved. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */ +/* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, */ +/* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE */ +/* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */ +/* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, */ +/* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ +/* SOFTWARE. */ +/*********************************************************************************/ + +#include + +#include + +void serial_init(void) +{ +} + +void serial_send(char c) +{ +} + +void serial_sendstr(char *s) +{ +} \ No newline at end of file diff --git a/machine/i686/qemu.mk b/machine/i686/qemu.mk new file mode 100644 index 0000000..4f6559d --- /dev/null +++ b/machine/i686/qemu.mk @@ -0,0 +1 @@ +QEMU_MACHINE_FLAGS := -M q35 \ No newline at end of file diff --git a/machine/x86_64/qemu.mk b/machine/x86_64/qemu.mk new file mode 100644 index 0000000..4f6559d --- /dev/null +++ b/machine/x86_64/qemu.mk @@ -0,0 +1 @@ +QEMU_MACHINE_FLAGS := -M q35 \ No newline at end of file diff --git a/utils/arch/i686/generate-hdd.sh b/utils/arch/i686/generate-hdd.sh new file mode 100755 index 0000000..abbf9a7 --- /dev/null +++ b/utils/arch/i686/generate-hdd.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +if [[ -z $1 ]]; then + printf "Please don't invoke this script manually. Run \`make release_hdd\` instead.\n" + exit 1 +fi + +sysroot_size=$(du -hsm kernel | cut -f 1) + +disk_name=$1 +disk_size=$(($sysroot_size + $efi_partition_size + 2)) + +unamestr=$(uname) + +# Check if we're running on a supported host +if ! [[ "$unamestr" =~ ^('Linux'|'Darwin') ]]; then + printf " failed. (unsupported host)\n" + exit 128 +fi + +# create a disk image +dd if=/dev/zero of="$disk_name" bs=1M count="$disk_size" >/dev/null 2>&1 + +# create a partition table +fdisk "$disk_name" >/dev/null 2>&1 << EOF +g +n p +1 + ++$efi_partition_size\M +t 1 +1 +n p +2 + + +t 2 +11 +w +EOF + +tempmountdir=$(mktemp -d 2>/dev/null) + +# mount disk +if [ "$unamestr" = 'Linux' ]; then + echo "Linux HDD Generation not implemented yet" + rm -r $tempmountdir + exit 200 +elif [ "$unamestr" = 'Darwin' ]; then + loopback=$(hdiutil attach -nomount $disk_name) + loopback=$(echo $loopback | cut -f1 -d" ") + + loopback_efi="$loopback"s1 +fi + +# format EFI partition +mkfs.vfat -F32 -n "EFI" "$loopback_efi" >/dev/null 2>&1 + +# mount EFI partition +if [ "$unamestr" = 'Darwin' ]; then + diskutil mount -mountPoint "$tempmountdir" "$loopback_efi" >/dev/null 2>&1 +else + mount "$loopback_efi" "$tempmountdir" +fi + +# Copy system root to the newly created image +cp -r "$ROOT_DIR"/sysroot/* "$tempmountdir" + +# unmount all partitions +if [ "$unamestr" = 'Linux' ]; then + echo "Linux HDD Generation not implemented yet" + rm -r $tempmountdir + exit 200 +elif [ "$unamestr" = 'Darwin' ]; then + diskutil unmount "$tempmountdir" >/dev/null 2>&1 + hdiutil detach "$loopback" >/dev/null 2>&1 +fi + +rm -r "$tempmountdir" + +# Install legacy BIOS placeholder to the new image +dd if="$BUILD_DIR/boot/boot/bootsect-hdd.bin" of="$disk_name" conv=notrunc bs=446 count=1 >/dev/null 2>&1 +dd if="$BUILD_DIR/boot/boot/bootsect-hdd.bin" of="$disk_name" conv=notrunc bs=1 count=2 skip=510 seek=510 >/dev/null 2>&1 + +printf " done.\n" diff --git a/utils/arch/i686/generate-iso.sh b/utils/arch/i686/generate-iso.sh new file mode 100755 index 0000000..7235f2d --- /dev/null +++ b/utils/arch/i686/generate-iso.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +if [[ -z $1 ]]; then + printf "Please don't invoke this script manually. Run \`make livecd\` instead.\n" + exit 1 +fi + +disk_name=$1 + +uefi_image=$BUILD_DIR/uefi.img +tempmountdir=$(mktemp -d 2>/dev/null) + +# Create UEFI image +dd if=/dev/zero of=$uefi_image bs=1k count=1440 >/dev/null 2>&1 +mformat -i $uefi_image -f 1440 :: >/dev/null 2>&1 +mmd -i $uefi_image ::/EFI >/dev/null 2>&1 +mmd -i $uefi_image ::/EFI/BOOT >/dev/null 2>&1 +mcopy -i $uefi_image $SYSROOT_DIR/EFI/BOOT/BOOTIA32.EFI ::/EFI/BOOT >/dev/null 2>&1 +## !FIXME: Huge hack! Make a filesystem. +mmd -i $uefi_image ::/System >/dev/null 2>&1 +mcopy -i $uefi_image $SYSROOT_DIR/System/axkrnl ::/System >/dev/null 2>&1 + +# Create directory structure +mkdir -p $tempmountdir/boot + +cp $uefi_image $tempmountdir/boot/uefi.bin +cp $BUILD_DIR/boot/boot/bootsect-cd.bin $tempmountdir/boot/bootcd.bin +cp -r $ROOT_DIR/sysroot/* $tempmountdir/ + +# Create ISO +xorriso -as mkisofs -b boot/bootcd.bin \ + -no-emul-boot -boot-load-size 4 -boot-info-table \ + --efi-boot boot/uefi.bin \ + -efi-boot-part --efi-boot-image --protective-msdos-label \ + $tempmountdir -o $1 >/dev/null 2>&1 + +rm -rf $tempmountdir + +printf " done.\n" \ No newline at end of file diff --git a/utils/arch/x86_64/generate-hdd.sh b/utils/arch/x86_64/generate-hdd.sh new file mode 100755 index 0000000..39673ee --- /dev/null +++ b/utils/arch/x86_64/generate-hdd.sh @@ -0,0 +1,87 @@ +#!/bin/bash + +if [[ -z $1 ]]; then + printf "Please don't invoke this script manually. Run \`make release_hdd\` instead.\n" + exit 1 +fi + +#sysroot_size=$(du -hsm kernel | cut -f 1) +sysroot_size=16 +efi_partition_size=32 + +disk_name=$1 +disk_size=$(($sysroot_size + $efi_partition_size + 2)) + +unamestr=$(uname) + +# Check if we're running on a supported host +if ! [[ "$unamestr" =~ ^('Linux'|'Darwin') ]]; then + printf " failed. (unsupported host)\n" + exit 128 +fi + +# create a disk image +dd if=/dev/zero of="$disk_name" bs=1M count="$disk_size" >/dev/null 2>&1 + +# create a partition table +fdisk "$disk_name" >/dev/null 2>&1 << EOF +g +n p +1 + ++$efi_partition_size\M +t 1 +1 +n p +2 + + +t 2 +11 +w +EOF + +tempmountdir=$(mktemp -d 2>/dev/null) + +# mount disk +if [ "$unamestr" = 'Linux' ]; then + echo "Linux HDD Generation not implemented yet" + rm -r $tempmountdir + exit 200 +elif [ "$unamestr" = 'Darwin' ]; then + loopback=$(hdiutil attach -nomount $disk_name) + loopback=$(echo $loopback | cut -f1 -d" ") + + loopback_efi="$loopback"s1 +fi + +# format EFI partition +mkfs.vfat -F32 -n "EFI" "$loopback_efi" >/dev/null 2>&1 + +# mount EFI partition +if [ "$unamestr" = 'Darwin' ]; then + diskutil mount -mountPoint "$tempmountdir" "$loopback_efi" >/dev/null 2>&1 +else + mount "$loopback_efi" "$tempmountdir" +fi + +# Copy system root to the newly created image +cp -r "$ROOT_DIR"/sysroot/* "$tempmountdir" + +# unmount all partitions +if [ "$unamestr" = 'Linux' ]; then + echo "Linux HDD Generation not implemented yet" + rm -r $tempmountdir + exit 200 +elif [ "$unamestr" = 'Darwin' ]; then + diskutil unmount "$tempmountdir" >/dev/null 2>&1 + hdiutil detach "$loopback" >/dev/null 2>&1 +fi + +rm -r "$tempmountdir" + +# Install legacy BIOS placeholder to the new image +dd if="$BUILD_DIR/boot/pc-bios/stage1-hdd.bin" of="$disk_name" conv=notrunc bs=446 count=1 >/dev/null 2>&1 +dd if="$BUILD_DIR/boot/pc-bios/stage1-hdd.bin" of="$disk_name" conv=notrunc bs=1 count=2 skip=510 seek=510 >/dev/null 2>&1 + +printf " done.\n" diff --git a/utils/arch/x86_64/generate-iso.sh b/utils/arch/x86_64/generate-iso.sh new file mode 100755 index 0000000..27355d8 --- /dev/null +++ b/utils/arch/x86_64/generate-iso.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +if [[ -z $1 ]]; then + printf "Please don't invoke this script manually. Run \`make livecd\` instead.\n" + exit 1 +fi + +disk_name=$1 + +uefi_image=$BUILD_DIR/uefi.img +tempmountdir=$(mktemp -d 2>/dev/null) + +# Create UEFI image +dd if=/dev/zero of=$uefi_image bs=1k count=1440 >/dev/null 2>&1 +mformat -i $uefi_image -f 1440 :: >/dev/null 2>&1 +mmd -i $uefi_image ::/EFI >/dev/null 2>&1 +mmd -i $uefi_image ::/EFI/BOOT >/dev/null 2>&1 +mcopy -i $uefi_image $SYSROOT_DIR/EFI/BOOT/BOOTX64.EFI ::/EFI/BOOT >/dev/null 2>&1 +## !FIXME: Huge hack! Make a filesystem. +mmd -i $uefi_image ::/System >/dev/null 2>&1 +mcopy -i $uefi_image $SYSROOT_DIR/System/axkrnl ::/System >/dev/null 2>&1 + +# Create directory structure +mkdir -p $tempmountdir/boot + +cp $uefi_image $tempmountdir/boot/uefi.bin +cp $BUILD_DIR/boot/pc-bios/stage1-cd.bin $tempmountdir/boot/bootcd.bin +cp -r $ROOT_DIR/sysroot/* $tempmountdir/ + +# Create ISO +xorriso -as mkisofs -b boot/bootcd.bin \ + -no-emul-boot -boot-load-size 4 -boot-info-table \ + --efi-boot boot/uefi.bin \ + -efi-boot-part --efi-boot-image --protective-msdos-label \ + $tempmountdir -o $1 >/dev/null 2>&1 + +rm -rf $tempmountdir + +printf " done.\n" \ No newline at end of file diff --git a/utils/download-ovmf.sh b/utils/download-ovmf.sh new file mode 100755 index 0000000..72c7308 --- /dev/null +++ b/utils/download-ovmf.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +check_command() { + if ! command -v "$1" &> /dev/null; then + echo "[!] '$1' could not be found!" + exit 1 + fi +} + +# Make sure we have curl +check_command curl + +mkdir -p ovmf + +curl https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF.fd -o ovmf/ovmf-x86_64.fd >/dev/null 2>&1 +curl https://retrage.github.io/edk2-nightly/bin/RELEASEIA32_OVMF.fd -o ovmf/ovmf-i686.fd >/dev/null 2>&1 +curl https://retrage.github.io/edk2-nightly/bin/RELEASEAARCH64_QEMU_EFI.fd -o ovmf/ovmf-aarch64.fd >/dev/null 2>&1 +curl https://retrage.github.io/edk2-nightly/bin/RELEASERISCV64_VIRT_CODE.fd -o ovmf/ovmf-riscv64.fd >/dev/null 2>&1 + +printf " done.\n" \ No newline at end of file