feat: add support for aarch64 timer/interrupts, port MontaukOS userspace
This commit is contained in:
1 parent
8b6379fb7e
commit
8c09ccef66
101 files changed
+1720
-629
No files matched your search
+27
-1
@@ -6,11 +6,16 @@ Two cross toolchains live here, both installed to `toolchain/local/`:
|
||||
|--------|--------|---------|
|
||||
| `build-toolchain.sh` | `x86_64-elf` | Bare-metal compiler for the kernel |
|
||||
| `build-montauk-toolchain.sh` | `x86_64-montauk` | OS-aware compiler for userspace programs |
|
||||
| `build-montauk-toolchain.sh aarch64` | `aarch64-montauk` | the same for the ARM port |
|
||||
|
||||
Both use Binutils 2.43.1 + GCC 14.2.0 and share the source trees in
|
||||
`src/`. The Montauk target is added by the patches in `patches/` plus
|
||||
the target header `files/montauk.h` (installed as
|
||||
`gcc/config/montauk.h` in the GCC tree).
|
||||
`gcc/config/montauk.h` in the GCC tree). The AArch64 target is a second
|
||||
pair of patches (`*-montauk-aarch64.patch`) applied on top, with its
|
||||
target header `files/aarch64-montauk.h` (installed as
|
||||
`gcc/config/aarch64/montauk.h`). The ARM kernel is built with clang, not
|
||||
with a bare-metal GCC.
|
||||
|
||||
## The x86_64-montauk target
|
||||
|
||||
@@ -42,6 +47,27 @@ The in-tree programs are built `-fno-exceptions -fno-rtti` (the project
|
||||
convention), but that is a project style choice, not a toolchain limit —
|
||||
see the STL section below.
|
||||
|
||||
## The aarch64-montauk target
|
||||
|
||||
`./toolchain/build-montauk-toolchain.sh aarch64` builds
|
||||
`aarch64-montauk-gcc/g++` next to the x86 compilers, with its sysroot in
|
||||
`toolchain/sysroot-aarch64/` and its libc from
|
||||
`programs/lib/libc/build-aarch64/`. On a first build the libc cannot be
|
||||
compiled yet, so the script builds GCC against the headers alone and
|
||||
completes the sysroot afterwards. The target matches x86_64-montauk
|
||||
(static `ET_EXEC` at 0x400000, 4 KiB segment alignment, the same crt
|
||||
files and `-lc`), plus two defaults chosen so the shared userspace
|
||||
behaves the same on both architectures:
|
||||
|
||||
- `-fsigned-char`: plain `char` is signed, as on x86 (AAPCS64 makes it
|
||||
unsigned). The GUI's CP1252 text handling assumes signed bytes.
|
||||
- `-mno-outline-atomics`: inline LL/SC atomics. Outline atomics choose
|
||||
LSE at run time from HWCAP, which Montauk does not provide, and their
|
||||
helpers are in libgcc, which `-nostdlib` programs never link.
|
||||
|
||||
`make ARCH=aarch64` at the repository root builds the userspace with it
|
||||
(into `programs/bin-aarch64/`); see `programs/mk/arch.mk`.
|
||||
|
||||
## C++ standard library (hosted libstdc++)
|
||||
|
||||
`build-montauk-toolchain.sh` builds and installs a **hosted** libstdc++
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# build-montauk-toolchain.sh — Build an x86_64-montauk cross compiler
|
||||
# build-montauk-toolchain.sh — Build an <arch>-montauk cross compiler
|
||||
# (Binutils + GCC) for MontaukOS userspace programs.
|
||||
#
|
||||
# Usage: ./toolchain/build-montauk-toolchain.sh
|
||||
# Usage: ./toolchain/build-montauk-toolchain.sh [x86_64|aarch64]
|
||||
# (or ARCH=aarch64 ./toolchain/build-montauk-toolchain.sh)
|
||||
#
|
||||
# Unlike the bare-metal x86_64-elf toolchain (build-toolchain.sh), this
|
||||
# target knows the Montauk userspace ABI: it links static ET_EXEC
|
||||
@@ -13,9 +14,11 @@
|
||||
#
|
||||
# The toolchain is installed to toolchain/local/ and provides:
|
||||
# x86_64-montauk-gcc, x86_64-montauk-g++, x86_64-montauk-ld, etc.
|
||||
# (aarch64-montauk-* for ARCH=aarch64; both can live side by side).
|
||||
#
|
||||
# The target sysroot (libc headers + libs) is assembled into
|
||||
# toolchain/sysroot/ from programs/include/libc and programs/lib/libc.
|
||||
# toolchain/sysroot/ (x86_64) or toolchain/sysroot-<arch>/ from
|
||||
# programs/include/libc and programs/lib/libc.
|
||||
# Re-run this script after changing libc headers or rebuilding libc to
|
||||
# refresh the sysroot (the sysroot step always runs; compiler builds
|
||||
# are skipped once installed).
|
||||
@@ -27,7 +30,12 @@ set -euo pipefail
|
||||
# ── Versions (kept in lockstep with build-toolchain.sh) ───────────────────────
|
||||
BINUTILS_VERSION="2.43.1"
|
||||
GCC_VERSION="14.2.0"
|
||||
TARGET="x86_64-montauk"
|
||||
ARCH="${1:-${ARCH:-x86_64}}"
|
||||
case "${ARCH}" in
|
||||
x86_64|aarch64) ;;
|
||||
*) echo "Unsupported architecture: ${ARCH}" >&2; exit 1 ;;
|
||||
esac
|
||||
TARGET="${ARCH}-montauk"
|
||||
|
||||
# ── Directories ───────────────────────────────────────────────────────────────
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
@@ -35,7 +43,13 @@ REPO_DIR="$(dirname "${SCRIPT_DIR}")"
|
||||
PREFIX="${SCRIPT_DIR}/local"
|
||||
SRC_DIR="${SCRIPT_DIR}/src"
|
||||
BUILD_DIR="${SCRIPT_DIR}/build"
|
||||
SYSROOT="${SCRIPT_DIR}/sysroot"
|
||||
if [[ "${ARCH}" == x86_64 ]]; then
|
||||
SYSROOT="${SCRIPT_DIR}/sysroot"
|
||||
BUILD_SUFFIX=""
|
||||
else
|
||||
SYSROOT="${SCRIPT_DIR}/sysroot-${ARCH}"
|
||||
BUILD_SUFFIX="-${ARCH}"
|
||||
fi
|
||||
PATCH_DIR="${SCRIPT_DIR}/patches"
|
||||
FILES_DIR="${SCRIPT_DIR}/files"
|
||||
|
||||
@@ -104,54 +118,87 @@ apply_patch() {
|
||||
fi
|
||||
}
|
||||
|
||||
# The AArch64 target is a second patch on top of the first.
|
||||
apply_arch_patch() {
|
||||
local dir="$1" patch="$2" marker="$3"
|
||||
if grep -q "aarch64-\*-montauk" "${dir}/${marker}"; then
|
||||
bold "Already patched for aarch64: $(basename "${dir}")"
|
||||
else
|
||||
bold "Patching $(basename "${dir}") for aarch64..."
|
||||
patch -d "${dir}" -p1 < "${patch}"
|
||||
fi
|
||||
}
|
||||
|
||||
apply_patch "${SRC_DIR}/binutils-${BINUTILS_VERSION}" "${PATCH_DIR}/binutils-${BINUTILS_VERSION}-montauk.patch"
|
||||
apply_patch "${SRC_DIR}/gcc-${GCC_VERSION}" "${PATCH_DIR}/gcc-${GCC_VERSION}-montauk.patch"
|
||||
apply_arch_patch "${SRC_DIR}/binutils-${BINUTILS_VERSION}" \
|
||||
"${PATCH_DIR}/binutils-${BINUTILS_VERSION}-montauk-aarch64.patch" bfd/config.bfd
|
||||
apply_arch_patch "${SRC_DIR}/gcc-${GCC_VERSION}" \
|
||||
"${PATCH_DIR}/gcc-${GCC_VERSION}-montauk-aarch64.patch" gcc/config.gcc
|
||||
cp "${FILES_DIR}/montauk.h" "${SRC_DIR}/gcc-${GCC_VERSION}/gcc/config/montauk.h"
|
||||
cp "${FILES_DIR}/aarch64-montauk.h" "${SRC_DIR}/gcc-${GCC_VERSION}/gcc/config/aarch64/montauk.h"
|
||||
|
||||
# ── Assemble the target sysroot ───────────────────────────────────────────────
|
||||
# usr/include — the Montauk libc headers (self-contained C; stddef/stdint/
|
||||
# stdarg come from GCC itself) plus the montauk/ SDK headers.
|
||||
# usr/lib — crt startup objects and the libc static library.
|
||||
bold "Assembling sysroot from programs/lib/libc..."
|
||||
make -C "${REPO_DIR}/programs" libc >/dev/null
|
||||
|
||||
rm -rf "${SYSROOT}"
|
||||
mkdir -p "${SYSROOT}/usr/include" "${SYSROOT}/usr/lib"
|
||||
cp -r "${REPO_DIR}/programs/include/libc/." "${SYSROOT}/usr/include/"
|
||||
cp -r "${REPO_DIR}/programs/include/montauk" "${SYSROOT}/usr/include/montauk"
|
||||
cp -r "${REPO_DIR}/programs/include/Api" "${SYSROOT}/usr/include/Api"
|
||||
# Freestanding C++ headers (<cstdint>, <type_traits>, <atomic>, ...) —
|
||||
# the subset the kernel and the in-tree programs build against. This is
|
||||
# NOT the whole C++ story: a hosted libstdc++ is built further down and
|
||||
# installs its own headers under ${PREFIX}/x86_64-montauk/include/c++.
|
||||
# That directory comes first on the include path and this one comes
|
||||
# last, so a plain g++ invocation gets the hosted headers and these are
|
||||
# shadowed. See "C++ standard library" in README.md.
|
||||
cp -r "${REPO_DIR}/kernel/freestnd-cxx-hdrs/x86_64/include/." "${SYSROOT}/usr/include/"
|
||||
cp "${REPO_DIR}/programs/lib/libc/crt1.o" \
|
||||
"${REPO_DIR}/programs/lib/libc/crti.o" \
|
||||
"${REPO_DIR}/programs/lib/libc/crtn.o" "${SYSROOT}/usr/lib/"
|
||||
cp "${REPO_DIR}/programs/lib/libc/liblibc.a" "${SYSROOT}/usr/lib/libc.a"
|
||||
# libm is an empty stand-in (math lives inside libc.a; the g++ driver
|
||||
# links -lm unconditionally). libstdc++ is copied from the toolchain
|
||||
# once built - the empty stand-in only bridges the first bootstrap.
|
||||
ar rcs "${SYSROOT}/usr/lib/libm.a"
|
||||
if [[ -f "${PREFIX}/x86_64-montauk/lib/libstdc++.a" ]]; then
|
||||
cp "${PREFIX}/x86_64-montauk/lib/libstdc++.a" "${SYSROOT}/usr/lib/libstdc++.a"
|
||||
else
|
||||
ar rcs "${SYSROOT}/usr/lib/libstdc++.a"
|
||||
LIBC_DIR="${REPO_DIR}/programs/lib/libc"
|
||||
if [[ "${ARCH}" != x86_64 ]]; then
|
||||
LIBC_DIR="${LIBC_DIR}/build-${ARCH}"
|
||||
fi
|
||||
green "Sysroot ready."
|
||||
|
||||
# On a first build for a new architecture the libc cannot be compiled yet
|
||||
# (it needs this compiler): the sysroot then holds only headers, which is
|
||||
# all building GCC needs, and is completed once GCC exists.
|
||||
assemble_sysroot() {
|
||||
bold "Assembling sysroot from programs/lib/libc..."
|
||||
local have_libc=1
|
||||
make -C "${REPO_DIR}/programs" libc ARCH="${ARCH}" >/dev/null 2>&1 || have_libc=0
|
||||
[[ -f "${LIBC_DIR}/liblibc.a" && -f "${LIBC_DIR}/crt1.o" ]] || have_libc=0
|
||||
|
||||
rm -rf "${SYSROOT}"
|
||||
mkdir -p "${SYSROOT}/usr/include" "${SYSROOT}/usr/lib"
|
||||
cp -r "${REPO_DIR}/programs/include/libc/." "${SYSROOT}/usr/include/"
|
||||
cp -r "${REPO_DIR}/programs/include/montauk" "${SYSROOT}/usr/include/montauk"
|
||||
cp -r "${REPO_DIR}/programs/include/Api" "${SYSROOT}/usr/include/Api"
|
||||
# Freestanding C++ headers (<cstdint>, <type_traits>, <atomic>, ...) —
|
||||
# the subset the kernel and the in-tree programs build against. This is
|
||||
# NOT the whole C++ story: a hosted libstdc++ is built further down and
|
||||
# installs its own headers under ${PREFIX}/${TARGET}/include/c++.
|
||||
# That directory comes first on the include path and this one comes
|
||||
# last, so a plain g++ invocation gets the hosted headers and these are
|
||||
# shadowed. See "C++ standard library" in README.md.
|
||||
cp -r "${REPO_DIR}/kernel/freestnd-cxx-hdrs/${ARCH}/include/." "${SYSROOT}/usr/include/"
|
||||
if [[ ${have_libc} == 1 ]]; then
|
||||
cp "${LIBC_DIR}/crt1.o" "${LIBC_DIR}/crti.o" "${LIBC_DIR}/crtn.o" "${SYSROOT}/usr/lib/"
|
||||
cp "${LIBC_DIR}/liblibc.a" "${SYSROOT}/usr/lib/libc.a"
|
||||
else
|
||||
bold "libc not built yet - sysroot has headers only."
|
||||
fi
|
||||
# libm is an empty stand-in (math lives inside libc.a; the g++ driver
|
||||
# links -lm unconditionally). libstdc++ is copied from the toolchain
|
||||
# once built - the empty stand-in only bridges the first bootstrap.
|
||||
ar rcs "${SYSROOT}/usr/lib/libm.a"
|
||||
if [[ -f "${PREFIX}/${TARGET}/lib/libstdc++.a" ]]; then
|
||||
cp "${PREFIX}/${TARGET}/lib/libstdc++.a" "${SYSROOT}/usr/lib/libstdc++.a"
|
||||
else
|
||||
ar rcs "${SYSROOT}/usr/lib/libstdc++.a"
|
||||
fi
|
||||
green "Sysroot ready."
|
||||
return 0
|
||||
}
|
||||
|
||||
assemble_sysroot
|
||||
|
||||
# ── Build Binutils ────────────────────────────────────────────────────────────
|
||||
if [[ -x "${PREFIX}/bin/${TARGET}-ld" ]]; then
|
||||
bold "Binutils already installed — skipping."
|
||||
else
|
||||
bold "Building Binutils ${BINUTILS_VERSION} for ${TARGET}..."
|
||||
rm -rf "${BUILD_DIR}/binutils-montauk"
|
||||
mkdir -p "${BUILD_DIR}/binutils-montauk"
|
||||
rm -rf "${BUILD_DIR}/binutils-montauk${BUILD_SUFFIX}"
|
||||
mkdir -p "${BUILD_DIR}/binutils-montauk${BUILD_SUFFIX}"
|
||||
(
|
||||
cd "${BUILD_DIR}/binutils-montauk"
|
||||
cd "${BUILD_DIR}/binutils-montauk${BUILD_SUFFIX}"
|
||||
"${SRC_DIR}/binutils-${BINUTILS_VERSION}/configure" \
|
||||
--target="${TARGET}" \
|
||||
--prefix="${PREFIX}" \
|
||||
@@ -174,10 +221,10 @@ if [[ -x "${PREFIX}/bin/${TARGET}-gcc" ]]; then
|
||||
bold "GCC already installed — skipping."
|
||||
else
|
||||
bold "Building GCC ${GCC_VERSION} for ${TARGET}..."
|
||||
rm -rf "${BUILD_DIR}/gcc-montauk"
|
||||
mkdir -p "${BUILD_DIR}/gcc-montauk"
|
||||
rm -rf "${BUILD_DIR}/gcc-montauk${BUILD_SUFFIX}"
|
||||
mkdir -p "${BUILD_DIR}/gcc-montauk${BUILD_SUFFIX}"
|
||||
(
|
||||
cd "${BUILD_DIR}/gcc-montauk"
|
||||
cd "${BUILD_DIR}/gcc-montauk${BUILD_SUFFIX}"
|
||||
"${SRC_DIR}/gcc-${GCC_VERSION}/configure" \
|
||||
--target="${TARGET}" \
|
||||
--prefix="${PREFIX}" \
|
||||
@@ -203,6 +250,11 @@ else
|
||||
green "GCC and libstdc++ installed."
|
||||
fi
|
||||
|
||||
# A first build for this architecture had no libc: complete the sysroot.
|
||||
if [[ ! -f "${SYSROOT}/usr/lib/libc.a" ]] || [[ ! -s "${SYSROOT}/usr/lib/libstdc++.a" ]]; then
|
||||
assemble_sysroot
|
||||
fi
|
||||
|
||||
# ── Done ──────────────────────────────────────────────────────────────────────
|
||||
echo
|
||||
green "=== ${TARGET} cross compiler ready ==="
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* aarch64-montauk.h
|
||||
* GCC target definitions for aarch64-montauk userspace
|
||||
* (installed as gcc/config/aarch64/montauk.h)
|
||||
* Copyright (c) 2026 Daniel Hammer
|
||||
*
|
||||
* Programs are static, non-PIE ET_EXEC images loaded by the kernel
|
||||
* ELF loader (kernel/src/Sched/ElfLoader.cpp) at their linked
|
||||
* address. The loader maps 4 KiB pages, so segments must not use
|
||||
* the 64 KiB AArch64 default alignment.
|
||||
*/
|
||||
|
||||
#undef TARGET_OS_CPP_BUILTINS
|
||||
#define TARGET_OS_CPP_BUILTINS() \
|
||||
do { \
|
||||
builtin_define("__montauk__"); \
|
||||
builtin_define("__MONTAUK__"); \
|
||||
builtin_assert("system=montauk"); \
|
||||
} while (0)
|
||||
|
||||
/* Inline LL/SC atomics: outline atomics pick LSE at run time from HWCAP,
|
||||
which Montauk does not provide, and their helpers live in libgcc, which
|
||||
-nostdlib programs never link.
|
||||
Signed plain char, as on x86_64-montauk: the userspace is shared between
|
||||
the architectures and its CP1252 text handling assumes it.
|
||||
-moutline-atomics / -funsigned-char still override. */
|
||||
#undef CC1_SPEC
|
||||
#define CC1_SPEC "%{!moutline-atomics:-mno-outline-atomics} %{!funsigned-char:-fsigned-char}"
|
||||
|
||||
#undef LINK_SPEC
|
||||
#define LINK_SPEC \
|
||||
"-maarch64elf -X -z max-page-size=0x1000 %{shared:-shared} %{!shared:-static}"
|
||||
|
||||
/* As on x86_64-montauk: crt1/crti/crtn from the Montauk libc,
|
||||
crtbegin/crtend from libgcc. */
|
||||
#undef STARTFILE_SPEC
|
||||
#define STARTFILE_SPEC "crt1.o%s crti.o%s crtbegin.o%s"
|
||||
|
||||
#undef ENDFILE_SPEC
|
||||
#define ENDFILE_SPEC "crtend.o%s crtn.o%s"
|
||||
|
||||
#undef LIB_SPEC
|
||||
#define LIB_SPEC "-lc"
|
||||
@@ -0,0 +1,35 @@
|
||||
--- a/bfd/config.bfd
|
||||
+++ b/bfd/config.bfd
|
||||
@@ -248,6 +248,11 @@
|
||||
targ_selvecs="aarch64_elf64_be_vec aarch64_elf32_le_vec aarch64_elf32_be_vec arm_elf32_le_vec arm_elf32_be_vec aarch64_pei_le_vec aarch64_pe_le_vec"
|
||||
want64=true
|
||||
;;
|
||||
+ aarch64-*-montauk*)
|
||||
+ targ_defvec=aarch64_elf64_le_vec
|
||||
+ targ_selvecs=aarch64_elf64_be_vec
|
||||
+ want64=true
|
||||
+ ;;
|
||||
aarch64-*-pe* | aarch64-*-mingw*)
|
||||
targ_defvec=aarch64_pe_le_vec
|
||||
targ_selvecs="aarch64_pe_le_vec aarch64_pei_le_vec aarch64_elf64_le_vec aarch64_elf64_be_vec aarch64_elf32_le_vec aarch64_elf32_be_vec arm_elf32_le_vec arm_elf32_be_vec pdb_vec"
|
||||
--- a/gas/configure.tgt
|
||||
+++ b/gas/configure.tgt
|
||||
@@ -128,6 +128,7 @@
|
||||
case ${generic_target} in
|
||||
aarch64*-*-elf*) fmt=elf;;
|
||||
aarch64*-*-fuchsia*) fmt=elf;;
|
||||
+ aarch64*-*-montauk*) fmt=elf;;
|
||||
aarch64*-*-haiku*) fmt=elf em=haiku ;;
|
||||
aarch64*-*-genode*) fmt=elf;;
|
||||
aarch64*-*-linux*) fmt=elf em=linux
|
||||
--- a/ld/configure.tgt
|
||||
+++ b/ld/configure.tgt
|
||||
@@ -89,6 +89,8 @@
|
||||
targ_emul=aarch64elf
|
||||
targ_extra_emuls="aarch64elf32 aarch64elf32b aarch64elfb armelf armelfb"
|
||||
;;
|
||||
+aarch64-*-montauk*) targ_emul=aarch64elf
|
||||
+ ;;
|
||||
aarch64-*-cloudabi*) targ_emul=aarch64cloudabi
|
||||
targ_extra_emuls=aarch64cloudabib
|
||||
;;
|
||||
@@ -0,0 +1,40 @@
|
||||
--- a/gcc/config.gcc
|
||||
+++ b/gcc/config.gcc
|
||||
@@ -1189,7 +1189,7 @@
|
||||
esac
|
||||
|
||||
case ${target} in
|
||||
-aarch64*-*-elf | aarch64*-*-fuchsia* | aarch64*-*-rtems*)
|
||||
+aarch64*-*-elf | aarch64*-*-fuchsia* | aarch64*-*-rtems* | aarch64*-*-montauk*)
|
||||
tm_file="${tm_file} elfos.h newlib-stdint.h"
|
||||
tm_file="${tm_file} aarch64/aarch64-elf.h aarch64/aarch64-errata.h aarch64/aarch64-elf-raw.h"
|
||||
tmake_file="${tmake_file} aarch64/t-aarch64"
|
||||
@@ -1204,6 +1204,11 @@
|
||||
tm_file="${tm_file} aarch64/rtems.h rtems.h"
|
||||
tmake_file="${tmake_file} aarch64/t-aarch64-rtems"
|
||||
;;
|
||||
+ aarch64-*-montauk*)
|
||||
+ tm_file="${tm_file} aarch64/montauk.h"
|
||||
+ # As for x86_64-montauk: the libc has no stdint.h of its own.
|
||||
+ use_gcc_stdint=provide
|
||||
+ ;;
|
||||
esac
|
||||
case $target in
|
||||
aarch64_be-*)
|
||||
--- a/libgcc/config.host
|
||||
+++ b/libgcc/config.host
|
||||
@@ -418,6 +418,14 @@
|
||||
tmake_file="${tmake_file} t-dfprules"
|
||||
md_unwind_header=aarch64/aarch64-unwind.h
|
||||
;;
|
||||
+aarch64*-*-montauk*)
|
||||
+ extra_parts="$extra_parts crtbegin.o crtend.o crtfastmath.o"
|
||||
+ tmake_file="${tmake_file} ${cpu_type}/t-aarch64"
|
||||
+ tmake_file="${tmake_file} ${cpu_type}/t-lse t-slibgcc-libgcc"
|
||||
+ tmake_file="${tmake_file} ${cpu_type}/t-softfp t-softfp t-crtfm"
|
||||
+ tmake_file="${tmake_file} t-dfprules"
|
||||
+ md_unwind_header=aarch64/aarch64-unwind.h
|
||||
+ ;;
|
||||
aarch64*-*-freebsd*)
|
||||
extra_parts="$extra_parts crtfastmath.o"
|
||||
tmake_file="${tmake_file} ${cpu_type}/t-aarch64"
|
||||
Reference in new issue
Block a user