diff --git a/.github/workflows/validate-x86_64.yml b/.github/workflows/validate-x86_64.yml index cff5fce..ea35ade 100755 --- a/.github/workflows/validate-x86_64.yml +++ b/.github/workflows/validate-x86_64.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5 with: repository: high-cde/Zlang - ref: 74958a3452897694f022ebfc8e2097702f3ba73a # compatibile con storage.read-v1 + ref: 65e5c41f9b28b349c9ba18cda26b4ea8d5125833 # compatibile con il target zdos-x86_64 path: Zlang - name: Install build and emulation tools @@ -28,6 +28,12 @@ jobs: sudo apt-get update sudo apt-get install -y --no-install-recommends gcc binutils make grub-pc-bin xorriso mtools qemu-system-x86 python3 php-cli curl + - name: Verify repository hygiene + run: bash scripts/check-repository-hygiene.sh + + - name: Validate ZLB2 bootstrap contract + run: python3 -m unittest discover -s os/x86_64/tools -p 'test_*.py' -v + - name: Validate resident organism boot hook run: bash distro/test-organism-boot.sh @@ -60,6 +66,13 @@ jobs: ref: 74958a3452897694f022ebfc8e2097702f3ba73a # compatibile con storage.read-v1 path: Zlang + - name: Checkout Zlang compiler for bare-metal target + uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5 + with: + repository: high-cde/Zlang + ref: 65e5c41f9b28b349c9ba18cda26b4ea8d5125833 # compatibile con il target zdos-x86_64 + path: Zlang-x86 + - name: Install Linux distro build tools run: | sudo apt-get update @@ -68,6 +81,7 @@ jobs: - name: Run strict Linux distro gate env: ZDOS_ZLANG_ROOT: ${{ github.workspace }}/Zlang + ZDOS_ZLANGC: ${{ github.workspace }}/Zlang-x86/tools/zlangc.py run: ./tools/zdos-selftest.sh --all - name: Upload Linux ISO and checksum diff --git a/.gitignore b/.gitignore index d01c502..cc15478 100755 --- a/.gitignore +++ b/.gitignore @@ -58,3 +58,12 @@ Thumbs.db # ZRetro generated target packages and IR /zretro/projects/*/build/* !/zretro/projects/*/build/.gitkeep + +# Reproducible build and release artifacts +/target/ +/distro/build/ +/os/x86_64/build/ +/artifacts/release/ +*.iso +*.img +*.zlb diff --git a/os/x86_64/Makefile b/os/x86_64/Makefile index 0ec22d8..9ca40df 100755 --- a/os/x86_64/Makefile +++ b/os/x86_64/Makefile @@ -29,7 +29,7 @@ kernel: $(KERNEL) $(PROGRAM_HEADER): $(PROGRAM) $(ZLANGC) @mkdir -p $(GENERATED_DIR) $(dir $(PROGRAM_BYTECODE)) - $(PYTHON) $(ZLANGC) $(PROGRAM) --header $(PROGRAM_HEADER) --bytecode $(PROGRAM_BYTECODE) + $(PYTHON) $(ZLANGC) $(PROGRAM) --target zdos-x86_64 --header $(PROGRAM_HEADER) --bytecode $(PROGRAM_BYTECODE) $(BUILD)/boot/boot.o: boot/boot.S @mkdir -p $(dir $@) diff --git a/os/x86_64/kernel/zlang.c b/os/x86_64/kernel/zlang.c index 5c9d9df..4984573 100755 --- a/os/x86_64/kernel/zlang.c +++ b/os/x86_64/kernel/zlang.c @@ -15,11 +15,6 @@ extern void serial_write_char(char c); #define ZLB2_RECORD_HEADER_SIZE 3u #define ZLB2_HALT 0xff #define ZLB2_EMIT 0x01 -#define ZLB2_LET 0x02 -#define ZLB2_IF 0x03 -#define ZLB2_LABEL 0x04 -#define ZLB2_WAIT 0x05 -#define ZLB2_STORAGE_READ 0x06 static uint16_t read_u16_le(const uint8_t *p) { return (uint16_t)p[0] | ((uint16_t)p[1] << 8); @@ -36,10 +31,8 @@ static int has_magic_and_version(const uint8_t *program, size_t length) { } static int known_opcode(uint8_t opcode) { - return opcode == ZLB2_EMIT || opcode == ZLB2_LET || - opcode == ZLB2_IF || opcode == ZLB2_LABEL || - opcode == ZLB2_WAIT || opcode == ZLB2_STORAGE_READ || - opcode == ZLB2_HALT; + /* The bootstrap target accepts only opcodes with executable semantics. */ + return opcode == ZLB2_EMIT || opcode == ZLB2_HALT; } static void emit_payload(const uint8_t *payload, uint16_t length) { @@ -49,9 +42,9 @@ static void emit_payload(const uint8_t *payload, uint16_t length) { serial_write_char('\n'); } -/* Execute the compiler-generated ZLB2 buffer. Only EMIT has an observable - * effect in this bootstrap; the remaining v2.5 records are validated and - * skipped until their capability implementations are introduced. */ +/* Execute the compiler-generated ZLB2 buffer. This bootstrap target is + * intentionally restricted to EMIT and terminal HALT. Any future capability + * must be implemented and tested before it is accepted by known_opcode(). */ void zlang_run(void) { const uint8_t *program = zlang_bytecode; const size_t length = sizeof(zlang_bytecode); @@ -85,9 +78,6 @@ void zlang_run(void) { offset = payload_start + payload_length; if (opcode == ZLB2_EMIT) { emit_payload(program + payload_start, payload_length); - } else if (opcode == ZLB2_STORAGE_READ) { - serial_write_string("ZDOS: storage.read capability requires Linux bridge\n"); - return; } else if (opcode == ZLB2_HALT) { if (payload_length != 0u || offset != length) { serial_write_string("ZDOS: ZLB2 invalid HALT\n"); diff --git a/os/x86_64/tools/test_zlb2.py b/os/x86_64/tools/test_zlb2.py index 0719847..087adea 100755 --- a/os/x86_64/tools/test_zlb2.py +++ b/os/x86_64/tools/test_zlb2.py @@ -6,7 +6,8 @@ import sys from pathlib import Path -KNOWN_OPCODES = {1, 2, 3, 4, 5, 0xFF} +# Keep this allowlist identical to kernel/zlang.c for the bootstrap target. +KNOWN_OPCODES = {0x01, 0xFF} def load_bytes(header: Path) -> bytes: diff --git a/os/x86_64/tools/test_zlb2_contract.py b/os/x86_64/tools/test_zlb2_contract.py new file mode 100644 index 0000000..dab67c7 --- /dev/null +++ b/os/x86_64/tools/test_zlb2_contract.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +import unittest + +from test_zlb2 import validate + + +class Zlb2BootstrapContractTests(unittest.TestCase): + def test_accepts_emit_and_terminal_halt(self) -> None: + validate(b"ZLB2\x02\x05\x01\x02\x00ok\xff\x00\x00") + + def test_rejects_opcode_without_bootstrap_semantics(self) -> None: + with self.assertRaisesRegex(AssertionError, "unknown opcode"): + validate(b"ZLB2\x02\x05\x02\x01\x00x\xff\x00\x00") + + def test_rejects_non_terminal_halt(self) -> None: + with self.assertRaisesRegex(AssertionError, "bytes found after HALT"): + validate(b"ZLB2\x02\x05\xff\x00\x00\x01\x01\x00x") + + def test_rejects_truncated_record(self) -> None: + with self.assertRaisesRegex(AssertionError, "truncated ZLB2 record header"): + validate(b"ZLB2\x02\x05\x01") + + +if __name__ == "__main__": + unittest.main() diff --git a/scripts/check-repository-hygiene.sh b/scripts/check-repository-hygiene.sh new file mode 100644 index 0000000..063e916 --- /dev/null +++ b/scripts/check-repository-hygiene.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# Verify that the ZDOS source repository contains source and contracts only. +set -Eeuo pipefail + +ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +cd "$ROOT" + +fail() { + printf 'ZDOS_HYGIENE_FAILED: %s\n' "$*" >&2 + exit 1 +} + +command -v git >/dev/null 2>&1 || fail 'git is required' +git diff --check || fail 'whitespace errors detected' + +forbidden=$(git ls-files | grep -E '(^|/)node_modules/|^target/|^distro/build/|^os/x86_64/build/|(^|/)__pycache__/|\.py[co]$|(^|/)\.pytest_cache/|\.(iso|img|zlb)$' || true) +if [[ -n "$forbidden" ]]; then + printf '%s\n' "$forbidden" >&2 + fail 'generated or vendor artifacts are tracked' +fi + +git check-ignore -q --no-index interface/web/node_modules/.sentinel || fail 'node_modules is not ignored' +git check-ignore -q --no-index distro/build/.sentinel || fail 'distro build output is not ignored' +git check-ignore -q --no-index os/x86_64/build/.sentinel || fail 'bare-metal build output is not ignored' +printf 'ZDOS_HYGIENE_OK\n' diff --git a/tools/zdos-selftest.sh b/tools/zdos-selftest.sh index 79de447..43d26c2 100755 --- a/tools/zdos-selftest.sh +++ b/tools/zdos-selftest.sh @@ -4,6 +4,7 @@ set -Eeuo pipefail ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) PYTHON_BIN="${PYTHON:-python3}" ZLANG_ROOT="${ZDOS_ZLANG_ROOT:-$ROOT/../Zlang}" +ZLANGC="${ZDOS_ZLANGC:-$ZLANG_ROOT/tools/zlangc.py}" RUN_DISTRO=0 RUN_QEMU=0 STRICT=0 @@ -66,6 +67,7 @@ done cd "$ROOT" printf 'ZDOS_SELFTEST_ROOT=%s\n' "$ROOT" printf 'ZDOS_SELFTEST_ZLANG_ROOT=%s\n' "$ZLANG_ROOT" +printf 'ZDOS_SELFTEST_ZLANGC=%s\n' "$ZLANGC" run_check 'shell syntax' bash -n \ distro/build.sh distro/prepare-persistence-modules.sh distro/rootfs/init \ @@ -110,8 +112,8 @@ else fi if has_commands make gcc grub-file; then - if [ -d "$ZLANG_ROOT" ] && [ -f "$ZLANG_ROOT/tools/zlangc.py" ]; then - run_check 'x86_64 kernel verification' env ZLANGC="$ZLANG_ROOT/tools/zlangc.py" bash -c 'cd os/x86_64 && make clean verify' + if [ -f "$ZLANGC" ]; then + run_check 'x86_64 kernel verification' env ZLANGC="$ZLANGC" bash -c 'cd os/x86_64 && make clean verify' else skip 'x86_64 kernel verification (Zlang non disponibile)' fi