diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ed95549..fb2894a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,11 @@ name: CI # and self-contained), one target alone, a single file and an okb # hosted the machine's own triple takes the platform's hosted arrangement # host-graph a build program is compiled as an ok image and run by okrun +# i686 Linux's thirty-two bit row: the hosted program and the +# specification's conformance suite, run on Linux x86_64 +# i686-openkal the ok image of i386, run by okrun built for i686-linux-musl +# dos a DOS program, and the i386 image carried as one (technical +# experiment only), run under DOSBox Staging through dirun # freebsd the musl rows of FreeBSD are built here and run in a FreeBSD VM, # with okrun built for them running an ok image there # @@ -36,7 +41,7 @@ on: env: MCPP_SOURCE_REF: ${{ github.event.inputs.mcpp_ref || vars.MCPP_SOURCE_REF || '' }} - MCPP_VERSION: 2026.11.10.1 + MCPP_VERSION: 2026.11.14.1 XLINGS_VERSION: v2026.10.26.1 XLINGS_NON_INTERACTIVE: '1' @@ -224,6 +229,225 @@ jobs: # programs; the x86_64 one runs in a FreeBSD virtual machine, beside okrun # built for the same row from the openkal-image release this group names, # which runs an ok image there. + # I686 (0.28.0): Linux's thirty-two bit row, which a Linux x86_64 machine runs + # itself: the hosted program, and the specification's conformance suite through + # the platform, over the implementation at the group's version. + i686: + name: i686-linux-musl (built and run on Linux x86_64) + runs-on: ubuntu-24.04 + timeout-minutes: 90 + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + + - name: Install xlings and mcpp + run: | + curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh \ + | bash -s "$XLINGS_VERSION" + export PATH="$HOME/.xlings/subos/current/bin:$PATH" + echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH" + for attempt in 1 2 3 4 5 6; do + xlings update > /dev/null 2>&1 || true + if xlings install "mcpp@$MCPP_VERSION" -y -g; then break; fi + [ "$attempt" = 6 ] && { echo "::error::mcpp could not be installed"; exit 1; } + sleep 60 + done + mcpp --version + mcpp self config --mirror GLOBAL + + - name: The hosted program, thirty-two bit + working-directory: checks/hosted + run: | + mcpp run --target i686-linux-musl -- one two | tee run.log + grep -q "hosted ok: hosted" run.log || { echo "::error::the hosted program's output"; exit 1; } + grep -q "hosted args: 3 two" run.log || { echo "::error::the hosted program's arguments"; exit 1; } + p=$(find target/i686-linux-musl -path '*bin/hosted' -type f | head -1) + file "$p" + file "$p" | grep -q 'ELF 32-bit' && file "$p" | grep -q 'Intel 80386' && file "$p" | grep -q 'statically linked' \ + || { echo "::error::i686-linux-musl is not a static i386 program"; exit 1; } + + - name: The specification's conformance suite + run: | + set -euo pipefail + spec=$(sed -n 's/^openkal *= *"\(.*\)"/\1/p' mcpp.toml | head -1) + impl=$(sed -n 's/^openkal-linux *= *"\(.*\)"/\1/p' mcpp.toml | head -1) + git clone --quiet --depth 1 --branch "$spec" https://github.com/mcpplibs/openkal.git .spec + git clone --quiet --depth 1 --branch "$impl" https://github.com/mcpplibs/openkal-linux.git .impl + # The platform serves the row mcpp registers as planned (mcpp 2026.11.14.1+). + bash .spec/tools/run-conformance.sh openkal-linux .impl full,optional \ + --target i686-linux-musl --platform "path:$GITHUB_WORKSPACE" | tee conformance.log + grep -qE '^observations: [0-9]+ held, 0 did not hold' conformance.log \ + || { echo "::error::an observation did not hold on i686-linux-musl"; exit 1; } + + uefi: + name: x86_64-uefi (built on Linux x86_64, run under OVMF) + runs-on: ubuntu-24.04 + timeout-minutes: 90 + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + + - name: Install xlings and mcpp + run: | + curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh \ + | bash -s "$XLINGS_VERSION" + export PATH="$HOME/.xlings/subos/current/bin:$PATH" + echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH" + for attempt in 1 2 3 4 5 6; do + xlings update > /dev/null 2>&1 || true + if xlings install "mcpp@$MCPP_VERSION" -y -g; then break; fi + [ "$attempt" = 6 ] && { echo "::error::mcpp could not be installed"; exit 1; } + sleep 60 + done + mcpp --version + mcpp self config --mirror GLOBAL + + # The provider's runner: dirun, xim's OVMF with its profile, and QEMU, installed by the + # first run; the status returns through the board's exit. + - name: The application, built and booted + working-directory: checks/uefi + run: | + set +e + mcpp run --release --target x86_64-uefi | tee run.log + status=${PIPESTATUS[0]} + set -e + grep -q "uefi ok: \[1, 2, 3\] 1" run.log || { echo "::error::the application's output"; exit 1; } + [ "$status" -eq 42 ] || { echo "::error::the application returned $status, not 42"; exit 1; } + p=$(find target/x86_64-uefi -path '*bin/uefi.efi' -type f | head -1) + file "$p" + file "$p" | grep -q 'PE32+' && file "$p" | grep -q 'EFI application' \ + || { echo "::error::x86_64-uefi is not a PE32+ EFI application"; exit 1; } + + # I686-OPENKAL (0.28.0): the ok image of i386 (ELF32, REL), built from + # checks/i386 in both modes, checked by okld, and run by okrun built for + # i686-linux-musl from the openkal-image release this group names. + i686-openkal: + name: i686-openkal (an ok image of i386, run on Linux x86_64) + runs-on: ubuntu-24.04 + timeout-minutes: 90 + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + + - name: Install xlings and mcpp + run: | + curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh \ + | bash -s "$XLINGS_VERSION" + export PATH="$HOME/.xlings/subos/current/bin:$PATH" + echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH" + for attempt in 1 2 3 4 5 6; do + xlings update > /dev/null 2>&1 || true + if xlings install "mcpp@$MCPP_VERSION" -y -g; then break; fi + [ "$attempt" = 6 ] && { echo "::error::mcpp could not be installed"; exit 1; } + sleep 60 + done + mcpp --version + mcpp self config --mirror GLOBAL + + - name: okrun for i686-linux-musl + run: | + tag=$(sed -n 's/^openkal-tools *= *"\(.*\)"/\1/p' mcpp.toml | head -1) + git clone --quiet --depth 1 --branch "$tag" https://github.com/mcpplibs/openkal-image.git "$RUNNER_TEMP/oi" + printf '\n[target.i686-linux-musl]\nplatform = { path = "%s" }\ntoolchain = "llvm@23.1.3"\n' "$GITHUB_WORKSPACE" >> "$RUNNER_TEMP/oi/tools/mcpp.toml" + (cd "$RUNNER_TEMP/oi/tools" && mcpp build --release --target i686-linux-musl) + okrun=$(find "$RUNNER_TEMP/oi/target/i686-linux-musl" -path '*bin/openkal-tools/okrun' -type f | head -1) + file "$okrun" + file "$okrun" | grep -q 'ELF 32-bit' || { echo "::error::okrun is not an i386 program"; exit 1; } + echo "OKRUN32=$okrun" >> "$GITHUB_ENV" + + - name: The image, built, checked and run (dev and release) + working-directory: checks/i386 + run: | + py=python3 + for mode in "" --release; do + rm -rf target/i686-openkal + mcpp build --target i686-openkal $mode + okld=$(mcpp self env --target i686-openkal --format json | $py -c 'import json,sys; print(json.load(sys.stdin)["data"]["tools"]["okld"])') + image=$(find target/i686-openkal -path '*bin*' -name i386.oke -type f | head -1) + "$okld" check "$image" + "$okld" inspect "$image" | tee inspect.log + grep -Eq '^architecture +i686' inspect.log || { echo "::error::the image does not state i686"; exit 1; } + set +e + "$OKRUN32" "$image" two one | tee run.log + status=${PIPESTATUS[0]} + set -e + grep -q 'i386 ok: \["one", "two"\]' run.log && grep -q 'caught: across a frame' run.log \ + || { echo "::error::the image's output ($mode)"; exit 1; } + [ "$status" -eq 7 ] || { echo "::error::the image returned $status, not 7 ($mode)"; exit 1; } + done + + # DOS (0.28.0; technical experiment only): a program of i686-dos-musl + # (openkal-musl over openkal-dos, run under CWSDPMI) built in both modes and + # run by the provider's runner, dirun with DOSBox Staging; and the i386 ok + # image packed as one DOS program, the core runner carrying it, and run there. + dos: + name: i686-dos-musl (built on Linux x86_64, run under DOSBox Staging) + runs-on: ubuntu-24.04 + timeout-minutes: 90 + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + + - name: Install xlings and mcpp + run: | + curl -fsSL https://raw.githubusercontent.com/openxlings/xlings/main/tools/other/quick_install.sh \ + | bash -s "$XLINGS_VERSION" + export PATH="$HOME/.xlings/subos/current/bin:$PATH" + echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH" + for attempt in 1 2 3 4 5 6; do + xlings update > /dev/null 2>&1 || true + if xlings install "mcpp@$MCPP_VERSION" -y -g; then break; fi + [ "$attempt" = 6 ] && { echo "::error::mcpp could not be installed"; exit 1; } + sleep 60 + done + mcpp --version + mcpp self config --mirror GLOBAL + + - name: The DOS program (dev and release) + working-directory: checks/dos + run: | + for mode in "" --release; do + set +e + mcpp run --target i686-dos-musl $mode -- two one | tr -d '\r' | tee run.log + status=${PIPESTATUS[0]} + set -e + grep -q 'dos ok: \["one", "two"\]' run.log && grep -q 'caught: across a frame' run.log \ + || { echo "::error::the DOS program's output ($mode)"; exit 1; } + [ "$status" -eq 7 ] || { echo "::error::the DOS program returned $status, not 7 ($mode)"; exit 1; } + done + p=$(find target/i686-dos-musl -path '*bin/dos.exe' -type f | head -1) + file "$p" + + - name: The i386 image carried as a DOS program + working-directory: checks/i386 + run: | + # `-o` names the file as written; DOS runs a program named .exe. + mcpp pack --target i686-openkal --pkg-format single-file --systems dos-i686 -o carried.exe + exe=target/dist/carried.exe + [ -f "$exe" ] || { echo "::error::no DOS program was packed"; ls target/dist; exit 1; } + file "$exe" + # The DOS row's runner, which the previous step installed: dirun, and + # the profile of the DOSBox Staging package beside it. + dirun=$(cd ../dos && mcpp self env --target i686-dos-musl --format json \ + | python3 -c 'import json,sys; print(json.load(sys.stdin)["data"]["runner"]["program"])') + root=$(cd "$(dirname "$dirun")/../../.." && pwd) + profile=$(find "$root"/xim-x-dosbox-staging -name dirun.toml -type f | sort | tail -1) + [ -n "$dirun" ] && [ -n "$profile" ] || { echo "::error::dirun or the DOSBox profile is not installed"; exit 1; } + set +e + "$dirun" --payload "$exe" --profile "$profile" -- two one | tr -d '\r' | tee carried.log + status=${PIPESTATUS[0]} + set -e + grep -q 'i386 ok: \["one", "two"\]' carried.log || { echo "::error::the carried image's output"; exit 1; } + [ "$status" -eq 7 ] || { echo "::error::the carried image returned $status, not 7"; exit 1; } + freebsd: name: freebsd (built for x86_64 and aarch64, run in a VM) runs-on: ubuntu-24.04 diff --git a/.github/workflows/family-release.yml b/.github/workflows/family-release.yml index 2815f26..07039d8 100644 --- a/.github/workflows/family-release.yml +++ b/.github/workflows/family-release.yml @@ -40,7 +40,7 @@ defaults: shell: bash env: - MCPP_PREVIOUS: 2026.11.10.1 + MCPP_PREVIOUS: 2026.11.13.1 MCPP_SOURCE_REF: ${{ github.event.inputs.mcpp_ref || vars.MCPP_SOURCE_REF || '' }} XLINGS_VERSION: v2026.10.26.1 XLINGS_NON_INTERACTIVE: '1' diff --git a/README.md b/README.md index eb678b2..033fd77 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,10 @@ mcpp pack --target x86_64-openkal --pkg-format okb # one file okrun runs | Group | Value | |---|---| -| targets | `x86_64-openkal`, `aarch64-openkal` | +| targets | `x86_64-openkal`, `aarch64-openkal`, `i686-openkal` (0.28.0) | | toolchain | `llvm@23.1.3` | | layers | `c-abi` openkal-musl (`posix`), `c++-abi` and `compiler-runtime` openkal-llvm-runtime | -| tools | `okld`, `okrun`, `okprof` (openkal-tools) | +| tools | `okld`, `okrun`, `okprof` (openkal-tools); `okdos` (openkal-dos-tools), the seal of the DOS row (0.28.0) | | build | `-fPIC -femulated-tls -fvisibility=hidden -fasynchronous-unwind-tables`, stack-clash protection, `-ffunction-sections -fdata-sections`; frame pointers, `-fstack-protector-strong -mstack-protector-guard=global`, `-ftrivial-auto-var-init=zero`, libc++'s fast hardening (0.23.0); on x86_64 `-fcf-protection=full`; on aarch64 `-ffixed-x18 -mno-outline-atomics -mbranch-protection=pac-ret+bti` (not `standard`, which clang 23 counts the guarded control stack in) | | link | `ld.lld`, entry `ok_main`, 64 KiB pages, relative relocations packed, `--gc-sections --icf=safe`, `--build-id=sha1`, retained debug relocations (0.24.0), the image notes produced by `okld note-source` | | seal | the selected LLVM toolchain's `llvm-objcopy` prunes ordinary static relocations (0.24.0); `okld note`; `okld place`, the writable segments computed for the base the image's bytes choose (openkal 0.23); then `okld check --with` the shared images the program needs | @@ -118,6 +118,53 @@ does; before, a program that polls before each `recv` took a stream one byte per call (openkal-musl#52). The group stays on openkal-windows 0.21, the line openkal-run and mcpplibs.os name. The platform's statements are those of 0.27.0. +Platform 0.28.0 adds Linux's thirty-two bit row, `i686-linux-musl`, to the hosted +arrangement (openkal 0.25.1, clause 5.5): openkal-musl 0.29.5 over openkal-linux +0.26.3's i386 system-call layer, with openkal-llvm-runtime 0.20.2 above it. A +Linux x86_64 machine runs its programs itself; CI builds and runs a hosted +program there and the specification's conformance suite through the platform. +The group follows openkal 0.25.3, whose conformance suite keeps the surface under +a link that discards what nothing reaches, and its implementations declare the +specification by its minor version (openkal-macos 0.23.3, openkal-windows 0.21.2, +openkal-freebsd 0.1.2; openkal-image 0.6.2), so a project that names any patch of +openkal 0.25 resolves with them. + +Platform 0.28.0 also serves the ok image of the thirty-two bit class, +`i686-openkal` (openkal 0.25.1, clauses 5.5 and 12.2; openkal-image 0.6.3 reads, +checks, places, loads and debugs it). Its conventions are the ok images' own, +and three apply to every architecture or to i386 alone: +- `-z rela`: an image states its addends, which is i386's one difference from + its linker's default. +- `-march=i486`: the 486's instructions, the least machine an i386 image runs + on. DOSBox Staging 0.83, which runs the DOS row and its carrier, executes + RDTSC and not CMPXCHG8B under every `cputype` (measured), so a sixty-four bit + atomic is compiler-rt's call rather than the Pentium's instruction. +- `-mstack-alignment=16`: the System V i386 convention's sixteen bytes at a call + (SPEC 12.2). The image's triple names no system, and for such a triple clang + keeps four. Measured: okrun's own code, built for Linux, faulted on a `movaps` + when an image built at four called it. + +An i386 image runs on an i386 runner (`i686-linux-musl` on Linux), never inside +a runner of another architecture. mcpp registers the row `planned`; this +platform lists it among its targets with its toolchain, which serves it (mcpp +2026.11.14.1+), so a project names only the platform, and `--target openkal` +builds the three architectures: + +```toml +[target.i686-openkal] +platform = "openkal-platform@0.28.0" +``` + +Measured: a C++23 image with `import std` and an exception across a frame, +built so, runs under an okrun built for `i686-linux-musl` on x86_64 Linux and +returns its status, unoptimised and optimised. + +The same image runs on DOS (technical experiment only): the runner's `hosts` +include `dos-i686`, so `mcpp pack --pkg-format single-file --systems dos-i686` +builds openkal-run's core runner as a program of the DOS row below, with the +image in its `.oke` section. Measured under DOSBox Staging: the image sorted its +arguments, caught its exception, read DOS's `PATH` and returned 7. + A project overrides any group under `[target.]`, for example `[target.'cfg(platform = "openkal")'.layers] "c++-abi" = false`, and selects another platform, or none, with `platform = "..."` or `--platform`. @@ -207,6 +254,73 @@ own openkal implementation (`checks/hosted`). `[host] platform` selects it for the host graph: build programs are then compiled as ok images of the machine's architecture and run by okrun (`checks/host-graph`). +## UEFI applications (0.28.0) + +`x86_64-uefi` is a row this platform defines and serves (`[[platform.define]]`, +`[[platform.provider]] name = "uefi"`): an application the firmware loads, on +the same C library and C++ run-time over openkal-uefi, so it is written in C++23 +with `import std`: + +```toml +[target.x86_64-uefi] +platform = "openkal-platform@0.28.0" +``` + +The row is PE. mcpp 2026.11.13.1 realises the C environment by the container, as +for Windows, and ld.lld's MinGW driver (`link-emulation = "i386pep"`) links an +EFI application entered at `efi_main`. The provider states the row's own link, +layers and runner over `base = "none"`, so none of the ok images' conventions +reach it: `mcpp run` boots the application with dirun, xim's OVMF and its +profile (`{pkg:ovmf}/dirun.toml`) and QEMU, and `board/qemu-exit.S` returns the +status through QEMU's debug-exit port. A program that wants no C library states +`c-abi = false` for the row. Measured: `std::println` and `std::ranges::sort`, a +1.2 MB application, printed and returned 42 under OVMF in 0.8 s. The row and its +runner were openkal-uefi-platform 0.1.0, which is folded here and into +openkal-uefi's example. + +An ok image runs on firmware as it runs on the other systems: the runner's +`hosts` include `uefi-x86_64`, so `mcpp pack --pkg-format single-file --systems +uefi-x86_64` builds the carrier for the row and places the image in its `.oke` +section, and the core runner (openkal-image 0.6.3) places the image through +`openkal.exec`. The board makes standard error the console (OVMF's `StdErr` +reaches no serial reader), and the machine has 2 GiB: memory is backed when the +allocator reserves it, and the carrier's allocator and the image's each place a +32 MiB arena at a 256 MiB alignment (openkal-musl 0.29.6, openkal-uefi 0.10.0). +Measured: an image that prints, writes to standard error, throws and catches, +carried by a 2.4 MB application, returned its status 7 under OVMF in 0.4 s. + +## DOS programs (0.28.0; technical experiment only) + +`i686-dos-musl` is a row this platform defines and serves (`[[platform.provider]] +name = "dos"`): a program a DPMI host runs, on the same C library and C++ +run-time over openkal-dos, so it is written in C++23 with `import std`: + +```toml +[target.i686-dos-musl] +platform = "openkal-platform@0.28.0" +``` + +The row is TECHNICAL EXPERIMENT ONLY (openkal-dos's README). A program is the +stub of openkal-dos followed by an ELF32 i386 executable linked at its address; +the seal `okdos` (openkal-dos-tools) writes the stub, and the stub enters 32-bit +protected mode through the machine's DPMI host or CWSDPMI beside the program. The +row's flags are its conventions: `-march=i486`, emulated thread-local storage +(there is no thread register the toolchain can address), unwind tables for +every unit, and the stack aligned to sixteen bytes at a call, as an i386 ok +image's is (the row's code calls images and is called by them). The provider's +link is `ld.lld -static --image-base=0x1000 -z norelro --gc-sections +--eh-frame-hdr`, entered at `okdos_entry`, over `base = "none"`, and +the platform's tools it carries are the one its seal names (mcpp 2026.11.13.1): +a DOS build builds `okdos` alone, and an ok image does not build it. `mcpp run` +runs the program with dirun and xim's DOSBox Staging profile, which places +CWSDPMI beside it. Measured: a C++23 program with `import std`, an exception +crossing a frame, `std::ranges::sort`, a sleep and `getenv` printed and returned +7 under DOSBox Staging. Through openkal-dos's `fs`, `std::filesystem` and +`std::fstream` work on the FAT volumes: a program wrote, appended, read, renamed, +listed, resized and removed files and directories there (measured). openkal-dos +provides `abort`, `env`, `exec`, `fs`, `memory`, `stream`, `terminal` and +`time`. `region` is absent, because the DPMI host CWSDPMI is DPMI 0.90. + ## Version group `[platform.versions]` names one version of each package of the family. From @@ -241,6 +355,8 @@ previous patch's. | `checks/hosted` | the hosted arrangement runs on Linux, macOS and Windows | | `checks/host-graph` | a build program runs as an ok image | | `checks/snapshot` | a C++ program starts from a snapshot of its initialisers, and a start whose named value differs does not | +| `checks/i386` | an ok image of i386 is built in both modes, checked by okld and run by okrun built for i686-linux-musl; packed with `--systems dos-i686`, it runs as a DOS program | +| `checks/dos` | a program of i686-dos-musl runs in both modes under DOSBox Staging (technical experiment only) | | family release | the group's members are tagged and mirrored, both mcpp releases resolve an image of it, and a patch removes no symbol | ## License diff --git a/board/qemu-exit.S b/board/qemu-exit.S new file mode 100644 index 0000000..7ae3beb --- /dev/null +++ b/board/qemu-exit.S @@ -0,0 +1,38 @@ +/* THE BOARD'S EXIT: a status leaves the machine through QEMU's `isa-debug-exit' device (port 0xf4), + * whose write ends the emulator with (status << 1) | 1. openkal-uefi calls this before Exit + * (okuefi_board_exit); its own definition does nothing. + * + * Only under QEMU. The processor states a hypervisor in CPUID leaf 1 (ECX bit 31) and names it in + * leaf 0x40000000 ("TCGTCGTCGTCG" for QEMU's translator, "KVMKVMKVM" under KVM); on a machine + * that is neither, nothing is written and the status leaves through Exit alone. The Microsoft x64 + * convention: the status arrives in ECX. */ + .text + .globl okuefi_board_exit + .def okuefi_board_exit; .scl 2; .type 32; .endef +okuefi_board_exit: + push %rbx + mov %ecx, %r8d + mov $1, %eax + cpuid + bt $31, %ecx + jnc 1f + mov $0x40000000, %eax + cpuid + cmp $0x54474354, %ebx /* "TCGT" */ + je 2f + cmp $0x4b4d564b, %ebx /* "KVMK" */ + jne 1f +2: mov %r8d, %eax + and $0xff, %eax + mov $0xf4, %dx + out %eax, %dx +1: pop %rbx + ret + +/* THE BOARD'S ERROR CONSOLE: OVMF's StdErr writes nothing to the serial console the runner reads, + * so standard error is ConOut on this board (openkal-uefi 0.10.0, okuefi_board_errors_to_console). */ + .globl okuefi_board_errors_to_console + .def okuefi_board_errors_to_console; .scl 2; .type 32; .endef +okuefi_board_errors_to_console: + mov $1, %eax + ret diff --git a/checks/dos/mcpp.toml b/checks/dos/mcpp.toml new file mode 100644 index 0000000..0708af4 --- /dev/null +++ b/checks/dos/mcpp.toml @@ -0,0 +1,12 @@ +# A DOS program (0.28.0; technical experiment only): C++23 over the family's C library and C++ +# run-time over openkal-dos, run with dirun under DOSBox Staging. The platform is the directory above. +[package] +name = "dos" +version = "0.1.0" + +[targets.dos] +kind = "bin" +main = "src/main.cpp" + +[target.i686-dos-musl] +platform = { path = "../.." } diff --git a/checks/dos/src/main.cpp b/checks/dos/src/main.cpp new file mode 100644 index 0000000..b166791 --- /dev/null +++ b/checks/dos/src/main.cpp @@ -0,0 +1,12 @@ +// A DOS program: its arguments sorted, an exception across a frame, the environment, a status. +import std; + +int main(int argc, char** argv) { + std::vector v(argv + 1, argv + argc); + std::ranges::sort(v); + std::println("dos ok: {}", v); + try { throw std::runtime_error("across a frame"); } + catch (const std::exception& e) { std::println("caught: {}", e.what()); } + std::println("PATH set: {}", std::getenv("PATH") != nullptr); + return 7; +} diff --git a/checks/hosted/mcpp.toml b/checks/hosted/mcpp.toml index fc0e637..c876c45 100644 --- a/checks/hosted/mcpp.toml +++ b/checks/hosted/mcpp.toml @@ -8,10 +8,5 @@ version = "0.1.0" [target.'cfg(any(linux, macos, windows, os = "freebsd"))'] platform = { path = "../.." } -# FreeBSD's musl rows are served from mcpp 2026.11.10.2; an earlier mcpp lists -# them as planned and takes them when the toolchain is named. -[target.x86_64-freebsd-musl] -toolchain = "llvm@23.1.3" - -[target.aarch64-freebsd-musl] -toolchain = "llvm@23.1.3" +# Linux's thirty-two bit row (0.28.0) is registered as planned in mcpp and +# served by this platform's hosted targets (mcpp 2026.11.14.1+). diff --git a/checks/i386/mcpp.toml b/checks/i386/mcpp.toml new file mode 100644 index 0000000..49ecabd --- /dev/null +++ b/checks/i386/mcpp.toml @@ -0,0 +1,14 @@ +# The ok image of i386 (0.28.0): C++23 over the family's C library and C++ run-time, built for +# i686-openkal and run by an okrun built for i686-linux-musl, and carried as a DOS program by the +# core runner (`mcpp pack --pkg-format single-file --systems dos-i686`). The platform is the +# directory above, which serves the row mcpp registers as planned (mcpp 2026.11.14.1+). +[package] +name = "i386" +version = "0.1.0" + +[targets.i386] +kind = "bin" +main = "src/main.cpp" + +[target.i686-openkal] +platform = { path = "../.." } diff --git a/checks/i386/src/main.cpp b/checks/i386/src/main.cpp new file mode 100644 index 0000000..4cf885f --- /dev/null +++ b/checks/i386/src/main.cpp @@ -0,0 +1,12 @@ +// An i386 image: its arguments sorted, an exception across a frame, the environment, a status. +import std; + +int main(int argc, char** argv) { + std::vector v(argv + 1, argv + argc); + std::ranges::sort(v); + std::println("i386 ok: {}", v); + try { throw std::runtime_error("across a frame"); } + catch (const std::exception& e) { std::println("caught: {}", e.what()); } + std::println("PATH set: {}", std::getenv("PATH") != nullptr); + return 7; +} diff --git a/checks/uefi/mcpp.toml b/checks/uefi/mcpp.toml new file mode 100644 index 0000000..df87570 --- /dev/null +++ b/checks/uefi/mcpp.toml @@ -0,0 +1,12 @@ +# The UEFI row (0.28.0): an application the firmware loads, on the family's C +# library and C++ run-time over openkal-uefi. The platform is the directory above. +[package] +name = "uefi" +version = "0.1.0" + +[targets.uefi] +kind = "bin" +main = "src/main.cpp" + +[target.x86_64-uefi] +platform = { path = "../.." } diff --git a/checks/uefi/src/main.cpp b/checks/uefi/src/main.cpp new file mode 100644 index 0000000..a3a4203 --- /dev/null +++ b/checks/uefi/src/main.cpp @@ -0,0 +1,10 @@ +// The C++ a UEFI application is written in: the standard library's text, ranges and containers +// over openkal-musl and openkal-llvm-runtime, and its status returned to the machine that ran it. +import std; + +int main(int argc, char** argv) { + std::vector v{3, 1, 2}; + std::ranges::sort(v); + std::println("uefi ok: {} {}", v, argc); + return 42; +} diff --git a/mcpp.toml b/mcpp.toml index 8cbca29..a54615d 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -12,7 +12,7 @@ [package] namespace = "mcpplibs" name = "openkal-platform" -version = "0.27.1" +version = "0.28.0" description = "The platform of ok images (-openkal): their toolchain, C library and C++ run-time, tools, compile and link conventions, seal, runner and carrier, as one versioned statement mcpp reads." license = "Apache-2.0" authors = ["mcpplibs"] @@ -21,10 +21,19 @@ repo = "https://github.com/mcpplibs/openkal-platform" # implementation provides (MCPP_KERNEL_ABI_INTERFACES), and FreeBSD's rows are # built-in. 2026.11.9.1: openkal-run (from 0.5.4) states its openkal implementation as a # capability with a default per system (`[defaults]`). -mcpp = ">=2026.11.10.1" +# 2026.11.13.1: the UEFI row (the PE rows' C environment by the container, a +# provider's link and runner over `base = "none"', a row's link emulation +# without its machine). 2026.11.14.1: a planned row is served by the platform +# chosen for it when the platform lists the row with a toolchain, so the i686 +# rows below need no toolchain named by the project. +mcpp = ">=2026.11.14.1" [platform] -targets = ["x86_64-openkal", "aarch64-openkal"] +# `i686-openkal` (0.28.0): the image of the thirty-two bit class, i386 (openkal +# 0.25.1, clauses 5.5 and 12.2; openkal-image 0.6.3 reads, checks and loads it). +# mcpp registers the row `planned'; this list, with `toolchain', serves it +# (mcpp 2026.11.14.1), and `--target openkal' builds every leg. +targets = ["x86_64-openkal", "aarch64-openkal", "i686-openkal"] toolchain = "llvm@23.1.3" # The loader runs an image of its own architecture on Linux, macOS and Windows; # `hosts` are the systems and architectures openkal-tools builds it for, which is @@ -35,11 +44,16 @@ toolchain = "llvm@23.1.3" # as, where the engine holds no convention for the system. A package that # carries its runner (`mcpp pack --mode self-contained --systems all`) builds # FreeBSD's as the musl identity this platform's hosted arrangement serves: one -# static program, the same whichever machine packs it. -runner = { cmd = ["okrun", "{}"], host = "same-arch", hosts = ["linux-x86_64", "linux-aarch64", "macos-aarch64", "windows-x86_64", "freebsd-x86_64", "freebsd-aarch64"], built-as = { "freebsd-x86_64" = "x86_64-freebsd-musl", "freebsd-aarch64" = "aarch64-freebsd-musl" } } +# static program, the same whichever machine packs it. `uefi-x86_64` (0.28.0): +# the runner is a UEFI application of the row this platform defines below (the +# core runner, which carries what it runs), so an ok image packs for firmware. +# `dos-i686` (0.28.0; technical experiment only): the core runner as a program +# of the DOS row below, so an i386 image packs for DOS (mcpp 2026.11.13.1 builds +# a system's runner as the row the platform defines for it). +runner = { cmd = ["okrun", "{}"], host = "same-arch", hosts = ["linux-x86_64", "linux-aarch64", "macos-aarch64", "windows-x86_64", "freebsd-x86_64", "freebsd-aarch64", "uefi-x86_64", "dos-i686"], built-as = { "freebsd-x86_64" = "x86_64-freebsd-musl", "freebsd-aarch64" = "aarch64-freebsd-musl" } } # `mcpp pack --mode self-contained`: a program of the packing machine that # carries the image in a section and runs it through libokrun (M3). -carrier = { package = "openkal-run@0.6.1", section = ".oke", main = "carrier/main.cpp" } +carrier = { package = "openkal-run@0.6.3", section = ".oke", main = "carrier/main.cpp" } # The `okb` package format (`mcpp pack --pkg-format okb`): the images of every # architecture as one file okrun runs (openkal-image docs/okb.md). plugin = "build.mcpp" @@ -51,18 +65,20 @@ debuggee = ["okrun", "--debugger", "{}"] # The C library and the C++ run-time inside an image. A package of the graph # that provides a layer takes its place (`provides`), and `false` leaves it out. [platform.layers] -compiler-runtime = "openkal-llvm-runtime@0.20.1" -"c++-abi" = "openkal-llvm-runtime@0.20.1" -c-abi = { name = "openkal-musl", version = "0.29.4", default-features = false, features = ["posix", "malloc-mimalloc"] } +compiler-runtime = "openkal-llvm-runtime@0.20.3" +"c++-abi" = "openkal-llvm-runtime@0.20.3" +c-abi = { name = "openkal-musl", version = "0.29.6", default-features = false, features = ["posix", "malloc-mimalloc"] } # Built for the building machine: `okld` writes and checks the notes, `okrun` # runs an image, `okprof` reads what a measured run recorded (0.23.0: the # order of a start's first executions, which a producer lays its code out by # and writes as the touch note). [platform.tools] -okld = "openkal-tools@0.6.1" -okrun = "openkal-tools@0.6.1" -okprof = "openkal-tools@0.6.1" +okld = "openkal-tools@0.6.3" +okrun = "openkal-tools@0.6.3" +okprof = "openkal-tools@0.6.3" +# The seal of the DOS row (0.28.0): the stub of openkal-dos before an executable. +okdos = "openkal-dos-tools@0.1.0" # Every unit of an image (SPEC 12.2): # -fPIC the image is ET_DYN and has no text relocation; @@ -131,6 +147,22 @@ cflags = ["-ffixed-x18", "-mno-outline-atomics", "-mbranch-protection=pac-ret+ cxxflags = ["-ffixed-x18", "-mno-outline-atomics", "-mbranch-protection=pac-ret+bti"] asmflags = ["-ffixed-x18", "-mno-outline-atomics", "-mbranch-protection=pac-ret+bti"] +# i386 (0.28.0): the 486's instructions (-march=i486) are the least machine an +# i386 image runs on: DOSBox Staging 0.83, which runs the DOS row and its +# carrier, executes RDTSC and not CMPXCHG8B under every `cputype' (measured), so +# a sixty-four bit atomic is compiler-rt's call (atomic.c) rather than the +# Pentium's instruction. An image is then the same bytes on Linux, on Windows and +# beneath DOS's carrier. The floating point is the x87's, the convention's own +# (SPEC 12.2). +# The stack is aligned to sixteen bytes at a call (SPEC 12.2): the image's triple +# names no system, for which clang keeps four, and a loader's code built for its +# system relies on sixteen (measured: `movaps' of okrun's kal_time_monotonic, +# entered from an image built at four, faults). +[platform.build.'cfg(arch = "i686")'] +cflags = ["-march=i486", "-mstack-alignment=16"] +cxxflags = ["-march=i486", "-mstack-alignment=16"] +asmflags = ["-march=i486"] + # The link, by `ld.lld` itself: position-independent, every loadable segment # aligned to 64 KiB (the largest protection granularity among the systems), # bound now, no executable stack, both hash tables, what nothing reaches @@ -138,7 +170,10 @@ asmflags = ["-ffixed-x18", "-mno-outline-atomics", "-mbranch-protection=pac-ret+ # and the unwind index an unwinder finds through `dl_iterate_phdr`; the relative # relocations packed (`-z pack-relative-relocs`, SPEC 12.2's packed relative # form): a loader reads clangd's 164 000 of them from 32 KiB rather than 3.9 MB -# on every start, and the image is 4 MB smaller. +# on every start, and the image is 4 MB smaller. `-z rela`: i386's default is +# REL, whose addend is the word the relocation writes; an image states its +# addends (RELA) on every architecture, so a loader and a placement read one +# form (SPEC 12.2; the option is the default's form on the sixty-four bit ones). # An executable image is entered at `ok_main`, which its C library defines; its # notes come from `okld note-source`; `okld note` writes its requirements, # `okld place` its writable segments computed for the base it chooses from the @@ -151,7 +186,7 @@ asmflags = ["-ffixed-x18", "-mno-outline-atomics", "-mbranch-protection=pac-ret+ [platform.link] driver = "lld" start = "ok_main" -flags = ["-shared", "-z", "max-page-size=65536", "-z", "pack-relative-relocs", "-z", "now", "-z", "noexecstack", +flags = ["-shared", "-z", "max-page-size=65536", "-z", "pack-relative-relocs", "-z", "rela", "-z", "now", "-z", "noexecstack", "--hash-style=both", "--gc-sections", "--icf=safe", "--eh-frame-hdr", "--build-id=sha1", "--emit-relocs", "-mllvm", "-emulated-tls"] inputs = [{ from = ["okld", "note-source"], as = "ok-notes.S" }] @@ -171,15 +206,86 @@ seal = [["okld", "note", "{}"], ["okld", "place", "{}"], ["okld", "check", "{} # On FreeBSD (0.26.0) the C library's implementation is openkal-freebsd in its # standalone arrangement, which openkal-musl names for that system: the rows # `-freebsd-musl` are the system with no C library of its own beneath. +# `i686-linux-musl` (0.28.0) is the thirty-two bit row of Linux (openkal 0.25.1, +# clause 5.5): openkal-linux's i386 system-call layer beneath openkal-musl's. targets = ["x86_64-linux-gnu", "aarch64-linux-gnu", "x86_64-linux-musl", "aarch64-linux-musl", "x86_64-macos", "aarch64-macos", "x86_64-windows-gnu", "x86_64-windows-musl", - "x86_64-freebsd-musl", "aarch64-freebsd-musl"] + "x86_64-freebsd-musl", "aarch64-freebsd-musl", "i686-linux-musl"] toolchain = "llvm@23.1.3" [platform.hosted.layers] -compiler-runtime = "openkal-llvm-runtime@0.20.1" -"c++-abi" = "openkal-llvm-runtime@0.20.1" -c-abi = { name = "openkal-musl", version = "0.29.4", default-features = false, features = ["posix", "malloc-mimalloc"] } +compiler-runtime = "openkal-llvm-runtime@0.20.3" +"c++-abi" = "openkal-llvm-runtime@0.20.3" +c-abi = { name = "openkal-musl", version = "0.29.6", default-features = false, features = ["posix", "malloc-mimalloc"] } + +# UEFI (0.28.0): applications the firmware loads, on the same C library and C++ +# run-time over openkal-uefi (openkal-musl names it, `standalone', on +# `cfg(os = "uefi")'). The row is PE: the C environment is the PE rows' +# (mcpp 2026.11.13.1 realises it by the container, as on Windows), linked by +# ld.lld's MinGW driver (`link-emulation = "i386pep"') as an EFI application. +# The provider states the row's own conventions (`base = "none"': none of the +# ok images' compile, link or seal), its layers and its runner: dirun boots the +# application with xim's OVMF and its profile, and the board's exit +# (board/qemu-exit.S, QEMU's debug-exit port) returns the status; the same file +# makes standard error the console (OVMF's StdErr reaches no serial reader). The +# machine has 2 GiB: memory is backed when the allocator reserves it, and the +# allocator of a carrier and of the image it carries each place an arena at a +# 256 MiB alignment (512 MiB, dirun's default, held one). A program that +# wants no C library states `c-abi = false' for the row and is linked over +# openkal-uefi alone. +[[platform.define]] +triple = "x86_64-uefi" +clang-triple = "x86_64-unknown-uefi" +object-format = "pe" +execution = "uefi" +exe-suffix = ".efi" +link-emulation = "i386pep" + +[[platform.provider]] +name = "uefi" +serves = ["x86_64-uefi"] +base = "none" +hosted = true + +layers = { compiler-runtime = "openkal-llvm-runtime@0.20.3", "c++-abi" = "openkal-llvm-runtime@0.20.3", c-abi = { name = "openkal-musl", version = "0.29.6", default-features = false, features = ["posix", "malloc-mimalloc"] } } +link = { driver = "lld", start = "efi_main", flags = ["--subsystem", "efi_application", "--gc-sections"], exe = "{name}.efi", inputs = [{ file = "board/qemu-exit.S" }], seal = [] } +runner = { cmd = ["dirun", "--payload", "{}", "--profile", "{pkg:ovmf}/dirun.toml", "--emulator", "{pkg:qemu-x86}/bin/qemu-system-x86_64", "--memory", "2048"], hosts = ["linux-x86_64", "linux-aarch64", "macos-aarch64", "windows-x86_64"], requires = ["xim:dirun@0.3.2", "xim:qemu-x86@9.2.4-1", "xim:ovmf@9.2.4-1"] } + +# DOS (0.28.0; TECHNICAL EXPERIMENT ONLY, openkal-dos's README): programs a DPMI +# host runs, on the same C library and C++ run-time over openkal-dos +# (openkal-musl names it, `standalone', on `cfg(os = "dos")'). A program is the +# stub of openkal-dos followed by an ELF32 i386 executable linked at its address; +# `okdos seal' writes the stub. The row's flags are its conventions: there is no +# thread register the toolchain can address, so thread-local storage is emulated +# (openkal-llvm-runtime builds compiler-rt's `emutls.c' for the row), and a triple +# with no system has no unwind tables by default, which the unwinder's own frames +# need. The stack is aligned to sixteen bytes at a call, the System V i386 +# convention that openkal states for an i386 image (SPEC 12.2) and that a triple +# with no system leaves at four: the row's code calls images and is called by +# them. The processor is the 486, as for the i386 ok image (DOSBox Staging 0.83 +# executes no CMPXCHG8B, measured). The runner is dirun with DOSBox Staging and CWSDPMI beside the program +# (xim's `dosbox-staging'). The memory allocator is musl's own (`malloc-mallocng'): +# it asks openkal.memory for blocks and nothing else. +[[platform.define]] +triple = "i686-dos-musl" +clang-triple = "i386-unknown-none-elf" +object-format = "elf" +data-model = "ilp32" +exe-suffix = ".exe" +execution = "dos" +tier = "preview" +march = "i486" +flags = ["-femulated-tls", "-fasynchronous-unwind-tables", "-mstack-alignment=16", "-ffunction-sections", "-fdata-sections"] + +[[platform.provider]] +name = "dos" +serves = ["i686-dos-musl"] +base = "none" +hosted = true + +layers = { compiler-runtime = "openkal-llvm-runtime@0.20.3", "c++-abi" = "openkal-llvm-runtime@0.20.3", c-abi = { name = "openkal-musl", version = "0.29.6", default-features = false, features = ["posix", "malloc-mallocng"] } } +link = { driver = "lld", start = "okdos_entry", flags = ["-static", "--image-base=0x1000", "-z", "norelro", "--gc-sections", "--eh-frame-hdr"], seal = [["okdos", "seal", "{}"]] } +runner = { cmd = ["dirun", "--payload", "{}", "--profile", "{pkg:dosbox-staging}/dirun.toml", "--"], hosts = ["linux-x86_64", "macos-aarch64", "windows-x86_64"], requires = ["xim:dirun@0.3.2", "xim:dosbox-staging@0.83.0"] } # The family released together for openkal 0.17 (the 0.17.2 round: okb, the # runner's bundle and library interfaces, musl's renameat2; the 0.17.3 round: @@ -225,27 +331,39 @@ c-abi = { name = "openkal-musl", version = "0.29.4", default-features # openkal-llvm-runtime 0.20.1 on FreeBSD; 0.27.1, the implementations retire a # handle before closing it (openkal-linux 0.26.1, openkal-macos 0.23.1, # openkal-windows 0.21.1) and openkal-musl 0.29.4 delivers what a readiness -# enquiry held with the rest): one version of each in a build. From 0.24.1 the members name one another by +# enquiry held with the rest; 0.28.0, openkal 0.25.3 (its suite keeps the surface +# under a discarding link and is an mtest project of its own) and i386: openkal-linux +# 0.26.3, openkal-musl 0.29.5 and openkal-llvm-runtime 0.20.2 for `i686-linux-musl`, +# and the implementations by the specification's minor version (openkal-macos +# 0.23.3, which supplies its libSystem stub only to a link without an SDK, openkal-windows 0.21.2, openkal-freebsd 0.1.2, openkal-image 0.6.2), and +# UEFI: openkal-uefi 0.10.0 beneath openkal-musl 0.29.6 and openkal-llvm-runtime +# 0.20.3, which take the optional interfaces from the implementation (or, in an +# image, at run time) and serve the PE rows by their container, and openkal-image +# 0.6.3, whose runner has a core for an implementation without files, processes +# or threads): one version of each in a build. From 0.24.1 the members name one another by # compatible ranges (`^0.28.0`) and this group chooses the versions: mcpp # 2026.11.7.1 resolves within it, and a fix of one member is that member's # release and a patch of this group, released together through one index change # (tools/family_release.py, .github/workflows/family-release.yml). [platform.versions] -openkal = "0.25.0" -openkal-linux = "0.26.1" -openkal-macos = "0.23.1" -openkal-windows = "0.21.1" -openkal-freebsd = "0.1.0" -openkal-musl = "0.29.4" -openkal-llvm-runtime = "0.20.1" -openkal-ld = "0.6.1" -openkal-run = "0.6.1" -openkal-tools = "0.6.1" +openkal = "0.25.3" +openkal-linux = "0.26.3" +openkal-macos = "0.23.3" +openkal-windows = "0.21.2" +openkal-freebsd = "0.1.2" +openkal-uefi = "0.10.0" +openkal-dos = "0.1.0" +openkal-dos-tools = "0.1.0" +openkal-musl = "0.29.6" +openkal-llvm-runtime = "0.20.3" +openkal-ld = "0.6.3" +openkal-run = "0.6.3" +openkal-tools = "0.6.3" # The plugin's tool, `okld bundle`, which writes the okb, and its library, the # provider of a package format (mcpp.plugins.platform). [build-dependencies] -openkal-tools = { version = "0.6.1", tools = ["okld"] } +openkal-tools = { version = "0.6.3", tools = ["okld"] } [build-dependencies.mcpp] plugins = { version = "0.20.0", features = ["plugins-platform"], host-module = true } diff --git a/tools/family_release.py b/tools/family_release.py index f07fdb6..3161b35 100644 --- a/tools/family_release.py +++ b/tools/family_release.py @@ -42,7 +42,8 @@ ROOT = Path(__file__).resolve().parents[1] # The packages one repository releases under one tag and one archive. REPOSITORY = {"openkal-ld": "openkal-image", "openkal-run": "openkal-image", - "openkal-tools": "openkal-image", "openkal-bind": "openkal-image"} + "openkal-tools": "openkal-image", "openkal-bind": "openkal-image", + "openkal-dos-tools": "openkal-dos"} INDEX_RAW = "https://raw.githubusercontent.com/mcpplibs/mcpp-index/main/pkgs/o/{}.lua" # The layers whose objects an image links, by the directory mcpp builds them into. LAYERS = ("mcpplibs_openkal-musl", "mcpplibs_openkal-llvm-runtime")