From e1859f72eea16b5d10cc67760a59fcb37a184155 Mon Sep 17 00:00:00 2001 From: Samuka007 <24754678@student.uwa.edu.au> Date: Wed, 17 Dec 2025 11:23:32 +0800 Subject: [PATCH 01/16] nix base qemu start script flake 2 3 4 --- .gitignore | 7 -- tools/nix-dev-shell/flake.lock | 100 ++++++++++++++++++ tools/nix-dev-shell/flake.nix | 16 +-- tools/qemu/flake.lock | 61 +++++++++++ tools/qemu/flake.nix | 179 +++++++++++++++++++++++++++++++++ 5 files changed, 348 insertions(+), 15 deletions(-) create mode 100644 tools/nix-dev-shell/flake.lock create mode 100644 tools/qemu/flake.lock create mode 100644 tools/qemu/flake.nix diff --git a/.gitignore b/.gitignore index 3080ca0b8..37810f418 100644 --- a/.gitignore +++ b/.gitignore @@ -21,13 +21,6 @@ compile_commands.json /logs/ *.log -# Nix dev files -/.envrc -/.direnv/ -/flake.nix -flake.lock -/default.nix - # initram *.cpio *.cpio.xz diff --git a/tools/nix-dev-shell/flake.lock b/tools/nix-dev-shell/flake.lock new file mode 100644 index 000000000..3f7edbf56 --- /dev/null +++ b/tools/nix-dev-shell/flake.lock @@ -0,0 +1,100 @@ +{ + "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1765435813, + "narHash": "sha256-C6tT7K1Lx6VsYw1BY5S3OavtapUvEnDQtmQB5DSgbCc=", + "owner": "nix-community", + "repo": "fenix", + "rev": "6399553b7a300c77e7f07342904eb696a5b6bf9d", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1765779637, + "narHash": "sha256-KJ2wa/BLSrTqDjbfyNx70ov/HdgNBCBBSQP3BIzKnv4=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "1306659b587dc277866c7b69eb97e5f07864d8c4", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "fenix": "fenix", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1765400135, + "narHash": "sha256-D3+4hfNwUhG0fdCpDhOASLwEQ1jKuHi4mV72up4kLQM=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "fface27171988b3d605ef45cf986c25533116f7e", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/tools/nix-dev-shell/flake.nix b/tools/nix-dev-shell/flake.nix index b627a347f..328158b41 100644 --- a/tools/nix-dev-shell/flake.nix +++ b/tools/nix-dev-shell/flake.nix @@ -4,15 +4,16 @@ nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; fenix.url = "github:nix-community/fenix"; fenix.inputs.nixpkgs.follows = "nixpkgs"; - utils.url = "github:numtide/flake-utils"; + flake-utils.url = "github:numtide/flake-utils"; }; - outputs = { self, nixpkgs, fenix, utils, ... } @ inputs: - utils.lib.eachDefaultSystem (system: + outputs = { self, nixpkgs, fenix, flake-utils, ... } @ inputs: + flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; - rusttoolchain = fenix.packages.${system}.fromToolchainFile{ + rust-toolchain = fenix.packages.${system}.fromToolchainFile{ file = ../../kernel/rust-toolchain.toml; + sha256 = "sha256-3JA9u08FrvsLdi5dGIsUeQZq3Tpn9RvWdkLus2+5cHs="; }; in { devShells.default = pkgs.mkShell { @@ -21,13 +22,12 @@ git llvm libclang - rusttoolchain + rust-toolchain ]; env = { - LIBCLANG_PATH = "${pkgs.libclang.lib}/lib"; - }; - + LIBCLANG_PATH = "${pkgs.libclang.lib}/lib"; + }; # Shell启动脚本 shellHook = '' diff --git a/tools/qemu/flake.lock b/tools/qemu/flake.lock new file mode 100644 index 000000000..5222f3ca0 --- /dev/null +++ b/tools/qemu/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1765779637, + "narHash": "sha256-KJ2wa/BLSrTqDjbfyNx70ov/HdgNBCBBSQP3BIzKnv4=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "1306659b587dc277866c7b69eb97e5f07864d8c4", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/tools/qemu/flake.nix b/tools/qemu/flake.nix new file mode 100644 index 000000000..7b5e27c97 --- /dev/null +++ b/tools/qemu/flake.nix @@ -0,0 +1,179 @@ +{ + description = "DragonOS QEMU Boot Environment (Debuggable)"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + # nixpkgs-old.url = "github:NixOS/nixpkgs/cc1d2f5bf5e65c0bddcbc87887bed2f1ff4aa65b"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + lib = pkgs.lib; + + # 1. 核心配置 + baseConfig = { + nographic = true; + memory = "512M"; + cores = "2"; + shmId = "dragonos-qemu-shm.ram"; + }; + + # 2. 路径定义 + riscv-uboot = pkgs.pkgsCross.riscv64-embedded.buildUBoot { + defconfig = "qemu-riscv64_smode_defconfig"; + extraMeta.platforms = [ "riscv64-linux" ]; + filesToInstall = [ "u-boot.bin" ]; + }; + + paths = { + kernelX86 = "bin/kernel/kernel.elf"; + diskX86 = "bin/disk-image-x86_64.img"; + diskRiscv = "bin/disk-image-riscv64.img"; + ubootRiscv = "${riscv-uboot}/u-boot.bin"; + }; + + # 3. 参数生成器 (Nix List -> Nix List) + mkQemuArgs = { arch, isNographic }: + let + baseArgs = [ + "-L" "${pkgs.qemu}/share/qemu" + "-m" baseConfig.memory + # FIXED: 补全 smp 参数,和原脚本一致 + "-smp" "${baseConfig.cores},cores=${baseConfig.cores},threads=1,sockets=1" + "-object" "memory-backend-file,size=${baseConfig.memory},id=${baseConfig.shmId},mem-path=/dev/shm/${baseConfig.shmId},share=on" + "-netdev" "user,id=hostnet0,hostfwd=tcp::12580-:12580" + "-device" "virtio-net-pci,vectors=5,netdev=hostnet0,id=net0" + "-usb" + "-device" "qemu-xhci,id=xhci,p2=8,p3=4" + "-D" "qemu.log" + + # FIXED: 新增缺失的 Boot Order + "-boot" "order=d" + # FIXED: 新增缺失的 GDB Stub + "-s" + # FIXED: 新增缺失的 RTC 设置 + "-rtc" "clock=host,base=localtime" + # FIXED: 新增缺失的 Trace 事件 (注意 * 号在 Nix 字符串里是安全的,escapeShellArgs 会处理它) + "-d" "cpu_reset,guest_errors,trace:virtio*,trace:e1000e_rx*,trace:e1000e_tx*,trace:e1000e_irq*" + ]; + nographicArgs = lib.optionals isNographic ([ + "-nographic" + "-serial" "chardev:mux" + "-monitor" "chardev:mux" + "-chardev" "stdio,id=mux,mux=on,signal=off,logfile=serial_opt.txt" + ] ++ (if arch == "riscv64" then [ + "-device" "virtio-serial-device" "-device" "virtconsole,chardev=mux" + ] else [ + "-device" "virtio-serial" "-device" "virtconsole,chardev=mux" + ])); + kernelCmdlinePart = if isNographic then "console=/dev/hvc0" else ""; + in { + flags = baseArgs ++ nographicArgs; + cmdlineExtra = kernelCmdlinePart; + }; + + # 4. 运行脚本生成器 + mkRunScript = { name, arch, isNographic, qemuBin, diskPath, kernelPath ? null, ubootPath ? null }: + let + qemuConfig = mkQemuArgs { inherit arch isNographic; }; + qemuFlagsStr = lib.escapeShellArgs qemuConfig.flags; + + # FIXED: 这里的逻辑彻底重写,以匹配原脚本的行为 + archSpecificArgs = if arch == "x86_64" then '' + MACHINE_TYPE_FLAGS=( "-machine" "q35,memory-backend=${baseConfig.shmId}" ) + + # 保持之前的 CPU 定义 + CPU_FLAGS=( "-cpu" "IvyBridge,apic,x2apic,+fpu,check,+vmx," ) + + # 保持之前的 KVM 加速定义 + if [ "$ACCEL" == "kvm" ]; then + ACCEL_FLAGS=( "-machine" "accel=kvm" "-enable-kvm" ) + else + ACCEL_FLAGS=( "-machine" "accel=tcg" ) + fi + + ARCH_FLAGS=( "''${MACHINE_TYPE_FLAGS[@]}" "''${CPU_FLAGS[@]}" "''${ACCEL_FLAGS[@]}" ) + + BOOT_ARGS=( "-kernel" "${paths.kernelX86}" "-append" "$FINAL_CMDLINE" ) + + DISK_ARGS=( "-device" "virtio-blk-pci,drive=disk" "-device" "pci-bridge,chassis_nr=1,id=pci.1" "-device" "pcie-root-port" "-drive" "id=disk,file=${diskPath},if=none" ) + '' else '' + ARCH_FLAGS=( "-cpu" "sifive-u54" "-machine" "virt,accel=$ACCEL,memory-backend=${baseConfig.shmId}" ) + BOOT_ARGS=( "-kernel" "${ubootPath}" "-append" "$FINAL_CMDLINE" ) + DISK_ARGS=( "-device" "virtio-blk-device,drive=disk" "-drive" "id=disk,file=${diskPath},if=none" ) + ''; + + initProgram = if arch == "riscv64" then "/bin/riscv_rust_init" else "/bin/busybox init"; + + in pkgs.writeScriptBin name '' + #!${pkgs.runtimeShell} + + if [ ! -d "bin" ]; then echo "Error: Please run from project root (bin/ missing)."; exit 1; fi + + ACCEL="tcg" + if [ -e /dev/kvm ] && [ -w /dev/kvm ]; then ACCEL="kvm"; fi + + cleanup() { sudo rm -f /dev/shm/${baseConfig.shmId}; } + trap cleanup EXIT + # FIXED: 既然用了 sudo 运行 qemu,这里创建 shm 也需要权限, + # 但实际上 qemu 会自己创建,这里只需要保证清理。 + # 原脚本是 rm -rf ... -> qemu -> rm -rf ... + + DRAGONOS_LOGLEVEL=''${DRAGONOS_LOGLEVEL:-4} + EXTRA_CMDLINE="${qemuConfig.cmdlineExtra}" + + # FIXED: 补全缺失的默认内核参数 AUTO_TEST 和 SYSCALL_TEST_DIR + FINAL_CMDLINE="init=${initProgram} loglevel=$DRAGONOS_LOGLEVEL AUTO_TEST=none SYSCALL_TEST_DIR=/opt/tests/gvisor $EXTRA_CMDLINE" + + # --- 1. 生成动态参数 --- + ${archSpecificArgs} + + # --- 2. 命令预览 --- + GREEN='\033[0;32m' + NC='\033[0m' + BOLD='\033[1m' + + echo -e "''${GREEN}================== DragonOS QEMU Command Preview ==================''${NC}" + echo -e "''${BOLD}Binary:''${NC} sudo ${qemuBin}" + echo -e "''${BOLD}Base Flags:''${NC} ${qemuFlagsStr}" + echo -e "''${BOLD}Arch Flags:''${NC} ''${ARCH_FLAGS[*]}" + echo -e "''${BOLD}Boot Args:''${NC} ''${BOOT_ARGS[*]}" + echo -e "''${BOLD}Disk Args:''${NC} ''${DISK_ARGS[*]}" + echo -e "''${GREEN}==================================================================''${NC}" + echo "" + + # --- 3. 执行 --- + # FIXED: 加上 sudo + ${qemuBin} --version + exec sudo ${qemuBin} ${qemuFlagsStr} "''${ARCH_FLAGS[@]}" -bios /usr/share/seabios/bios-256k.bin "''${BOOT_ARGS[@]}" "''${DISK_ARGS[@]}" "$@" + ''; + + in { + packages = { + x86_64 = mkRunScript { + name = "run-dragonos-x86"; + arch = "x86_64"; + isNographic = baseConfig.nographic; + qemuBin = "${pkgs.qemu_full}/bin/qemu-system-x86_64"; + diskPath = paths.diskX86; + kernelPath = paths.kernelX86; + }; + + riscv = mkRunScript { + name = "run-dragonos-riscv"; + arch = "riscv64"; + isNographic = true; + qemuBin = "${pkgs.qemu_full}/bin/qemu-system-riscv64"; + diskPath = paths.diskRiscv; + ubootPath = paths.ubootRiscv; + }; + }; + + apps.default = flake-utils.lib.mkApp { drv = self.packages.${system}.x86_64; }; + apps.riscv = flake-utils.lib.mkApp { drv = self.packages.${system}.riscv; }; + } + ); +} \ No newline at end of file From 3bd7dc202c4166827548aa1a9b8dafadaff9a160 Mon Sep 17 00:00:00 2001 From: Samuka007 <24754678@student.uwa.edu.au> Date: Wed, 17 Dec 2025 23:52:27 +0800 Subject: [PATCH 02/16] userspace rootfs app creation uni-1 --- flake.lock | 66 +++ flake.nix | 40 ++ tools/qemu/default.nix | 161 +++++ tools/qemu/flake.nix | 17 +- user/apps/about/default.nix | 23 + user/apps/default.nix | 21 + user/apps/tests/syscall/gvisor/default.nix | 57 ++ .../tests/syscall/gvisor/runner/.gitignore | 1 - .../tests/syscall/gvisor/runner/Cargo.lock | 558 ++++++++++++++++++ user/default.nix | 78 +++ user/flake.lock | 66 +++ user/rootfs-tar.nix | 24 + 12 files changed, 1107 insertions(+), 5 deletions(-) create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 tools/qemu/default.nix create mode 100644 user/apps/about/default.nix create mode 100644 user/apps/default.nix create mode 100644 user/apps/tests/syscall/gvisor/default.nix create mode 100644 user/apps/tests/syscall/gvisor/runner/Cargo.lock create mode 100644 user/default.nix create mode 100644 user/flake.lock create mode 100644 user/rootfs-tar.nix diff --git a/flake.lock b/flake.lock new file mode 100644 index 000000000..d1ed1de55 --- /dev/null +++ b/flake.lock @@ -0,0 +1,66 @@ +{ + "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1765435813, + "narHash": "sha256-C6tT7K1Lx6VsYw1BY5S3OavtapUvEnDQtmQB5DSgbCc=", + "owner": "nix-community", + "repo": "fenix", + "rev": "6399553b7a300c77e7f07342904eb696a5b6bf9d", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1765779637, + "narHash": "sha256-KJ2wa/BLSrTqDjbfyNx70ov/HdgNBCBBSQP3BIzKnv4=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "1306659b587dc277866c7b69eb97e5f07864d8c4", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "fenix": "fenix", + "nixpkgs": "nixpkgs" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1765400135, + "narHash": "sha256-D3+4hfNwUhG0fdCpDhOASLwEQ1jKuHi4mV72up4kLQM=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "fface27171988b3d605ef45cf986c25533116f7e", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 000000000..eb5b86162 --- /dev/null +++ b/flake.nix @@ -0,0 +1,40 @@ +{ + description = "RootFS"; + + inputs = { + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + }; + + outputs = { self, nixpkgs, fenix }: + let + # targetSystems = [ "x86_64-linux" "riscv64-linux" ]; # TODO: Support multi-arch + system = "x86_64-linux"; + pkgs = nixpkgs.legacyPackages.${system}; + target = "x86_64"; + qemuScripts = import ./tools/qemu/default.nix { + lib = pkgs.lib; + inherit pkgs; + rootfsDisk = "./bin/disk-image-${target}.img"; + kernel = "./bin/kernel"; + }; + in { + # packages.${system}.default = ; + apps.${system} = { + start.${target} = { + type = "app"; + program = "${qemuScripts.${target}}/bin/run-dragonos-x86"; + }; + build-rootfs.${target} = { + type = "app"; + program = "${pkgs.callPackage ./user/default.nix { + inherit pkgs system fenix target; + buildDir = "./bin"; + }}/bin/build-rootfs-image"; + }; + }; + }; +} \ No newline at end of file diff --git a/tools/qemu/default.nix b/tools/qemu/default.nix new file mode 100644 index 000000000..05fcd233a --- /dev/null +++ b/tools/qemu/default.nix @@ -0,0 +1,161 @@ +{ + lib, + pkgs, + rootfsDisk, + kernel, +}: + +let + baseConfig = { + nographic = true; + memory = "512M"; + cores = "2"; + shmId = "dragonos-qemu-shm.ram"; + }; + + riscv-uboot = pkgs.pkgsCross.riscv64-embedded.buildUBoot { + defconfig = "qemu-riscv64_smode_defconfig"; + extraMeta.platforms = [ "riscv64-linux" ]; + filesToInstall = [ "u-boot.bin" ]; + }; + + # 3. 参数生成器 (Nix List -> Nix List) + mkQemuArgs = { arch, isNographic }: + let + baseArgs = [ + # "-L" "${pkgs.qemu}/share/qemu" + "-m" baseConfig.memory + # FIXED: 补全 smp 参数,和原脚本一致 + "-smp" "${baseConfig.cores},cores=${baseConfig.cores},threads=1,sockets=1" + "-object" "memory-backend-file,size=${baseConfig.memory},id=${baseConfig.shmId},mem-path=/dev/shm/${baseConfig.shmId},share=on" + "-netdev" "user,id=hostnet0,hostfwd=tcp::12580-:12580" + "-device" "virtio-net-pci,vectors=5,netdev=hostnet0,id=net0" + "-usb" + "-device" "qemu-xhci,id=xhci,p2=8,p3=4" + "-D" "qemu.log" + + # FIXED: 新增缺失的 Boot Order + "-boot" "order=d" + # FIXED: 新增缺失的 GDB Stub + "-s" + # FIXED: 新增缺失的 RTC 设置 + "-rtc" "clock=host,base=localtime" + # FIXED: 新增缺失的 Trace 事件 (注意 * 号在 Nix 字符串里是安全的,escapeShellArgs 会处理它) + "-d" "cpu_reset,guest_errors,trace:virtio*,trace:e1000e_rx*,trace:e1000e_tx*,trace:e1000e_irq*" + ]; + nographicArgs = lib.optionals isNographic ([ + "-nographic" + "-serial" "chardev:mux" + "-monitor" "chardev:mux" + "-chardev" "stdio,id=mux,mux=on,signal=off,logfile=serial_opt.txt" + ] ++ (if arch == "riscv64" then [ + "-device" "virtio-serial-device" "-device" "virtconsole,chardev=mux" + ] else [ + "-device" "virtio-serial" "-device" "virtconsole,chardev=mux" + ])); + kernelCmdlinePart = if isNographic then "console=/dev/hvc0" else ""; + in { + flags = baseArgs ++ nographicArgs; + cmdlineExtra = kernelCmdlinePart; + }; + + # 4. 运行脚本生成器 + mkRunScript = { name, arch, isNographic, qemuBin }: + let + qemuConfig = mkQemuArgs { inherit arch isNographic; }; + qemuFlagsStr = lib.escapeShellArgs qemuConfig.flags; + + # FIXED: 这里的逻辑彻底重写,以匹配原脚本的行为 + archSpecificArgs = if arch == "x86_64" then '' + MACHINE_TYPE_FLAGS=( "-machine" "q35,memory-backend=${baseConfig.shmId}" ) + + # 保持之前的 CPU 定义 + CPU_FLAGS=( "-cpu" "IvyBridge,apic,x2apic,+fpu,check,+vmx," ) + + # 保持之前的 KVM 加速定义 + if [ "$ACCEL" == "kvm" ]; then + ACCEL_FLAGS=( "-machine" "accel=kvm" "-enable-kvm" ) + else + ACCEL_FLAGS=( "-machine" "accel=tcg" ) + fi + + ARCH_FLAGS=( "''${MACHINE_TYPE_FLAGS[@]}" "''${CPU_FLAGS[@]}" "''${ACCEL_FLAGS[@]}" ) + + BOOT_ARGS=( "-kernel" "${kernel}/kernel.elf" "-append" "$FINAL_CMDLINE" ) + + DISK_ARGS=( "-device" "virtio-blk-pci,drive=disk" "-device" "pci-bridge,chassis_nr=1,id=pci.1" "-device" "pcie-root-port" "-drive" "id=disk,file=${rootfsDisk},if=none" ) + '' else '' + ARCH_FLAGS=( "-cpu" "sifive-u54" "-machine" "virt,accel=$ACCEL,memory-backend=${baseConfig.shmId}" ) + BOOT_ARGS=( "-kernel" "${riscv-uboot}/u-boot.bin" "-append" "$FINAL_CMDLINE" ) + DISK_ARGS=( "-device" "virtio-blk-device,drive=disk" "-drive" "id=disk,file=${rootfsDisk},if=none" ) + ''; + + initProgram = if arch == "riscv64" then "/bin/riscv_rust_init" else "/bin/busybox init"; + + in pkgs.writeScriptBin name '' + #!${pkgs.runtimeShell} + + if [ ! -d "bin" ]; then echo "Error: Please run from project root (bin/ missing)."; exit 1; fi + + ACCEL="tcg" + if [ -e /dev/kvm ] && [ -w /dev/kvm ]; then ACCEL="kvm"; fi + + cleanup() { sudo rm -f /dev/shm/${baseConfig.shmId}; } + trap cleanup EXIT + # FIXED: 既然用了 sudo 运行 qemu,这里创建 shm 也需要权限, + # 但实际上 qemu 会自己创建,这里只需要保证清理。 + # 原脚本是 rm -rf ... -> qemu -> rm -rf ... + + DRAGONOS_LOGLEVEL=''${DRAGONOS_LOGLEVEL:-4} + EXTRA_CMDLINE="${qemuConfig.cmdlineExtra}" + + # FIXED: 补全缺失的默认内核参数 AUTO_TEST 和 SYSCALL_TEST_DIR + FINAL_CMDLINE="init=${initProgram} loglevel=$DRAGONOS_LOGLEVEL AUTO_TEST=none SYSCALL_TEST_DIR=/opt/tests/gvisor $EXTRA_CMDLINE" + + # --- 1. 生成动态参数 --- + ${archSpecificArgs} + + # --- 2. 命令预览 --- + GREEN='\033[0;32m' + NC='\033[0m' + BOLD='\033[1m' + + echo -e "''${GREEN}================== DragonOS QEMU Command Preview ==================''${NC}" + echo -e "''${BOLD}Binary:''${NC} sudo ${qemuBin}" + echo -e "''${BOLD}Base Flags:''${NC} ${qemuFlagsStr}" + echo -e "''${BOLD}Arch Flags:''${NC} ''${ARCH_FLAGS[*]}" + echo -e "''${BOLD}Boot Args:''${NC} ''${BOOT_ARGS[*]}" + echo -e "''${BOLD}Disk Args:''${NC} ''${DISK_ARGS[*]}" + echo -e "''${GREEN}==================================================================''${NC}" + echo "" + + # --- 3. 执行 --- + ${qemuBin} --version + exec sudo ${qemuBin} ${qemuFlagsStr} "''${ARCH_FLAGS[@]}" "''${BOOT_ARGS[@]}" "''${DISK_ARGS[@]}" "$@" + ''; + + # 5. QEMU 二进制选择器 (支持外部 QEMU) + # 用法: QEMU_X86=/usr/bin/qemu-system-x86_64 nix run --impure .#x86_64 + getQemuBin = arch: fallback: + let + envVar = if arch == "x86_64" then "QEMU_X86" else "QEMU_RISCV"; + externalPath = builtins.getEnv envVar; + in + if externalPath != "" then externalPath else fallback; + + script = { + x86_64 = mkRunScript { + name = "run-dragonos-x86"; + arch = "x86_64"; + isNographic = baseConfig.nographic; + qemuBin = getQemuBin "x86_64" "${pkgs.qemu_full}/bin/qemu-system-x86_64"; + }; + + riscv64 = mkRunScript { + name = "run-dragonos-riscv"; + arch = "riscv64"; + isNographic = true; + qemuBin = getQemuBin "riscv64" "${pkgs.qemu_full}/bin/qemu-system-riscv64"; + }; + }; +in script \ No newline at end of file diff --git a/tools/qemu/flake.nix b/tools/qemu/flake.nix index 7b5e27c97..8b62d8c90 100644 --- a/tools/qemu/flake.nix +++ b/tools/qemu/flake.nix @@ -39,7 +39,7 @@ mkQemuArgs = { arch, isNographic }: let baseArgs = [ - "-L" "${pkgs.qemu}/share/qemu" + # "-L" "${pkgs.qemu}/share/qemu" "-m" baseConfig.memory # FIXED: 补全 smp 参数,和原脚本一致 "-smp" "${baseConfig.cores},cores=${baseConfig.cores},threads=1,sockets=1" @@ -148,16 +148,25 @@ # --- 3. 执行 --- # FIXED: 加上 sudo ${qemuBin} --version - exec sudo ${qemuBin} ${qemuFlagsStr} "''${ARCH_FLAGS[@]}" -bios /usr/share/seabios/bios-256k.bin "''${BOOT_ARGS[@]}" "''${DISK_ARGS[@]}" "$@" + exec sudo ${qemuBin} ${qemuFlagsStr} "''${ARCH_FLAGS[@]}" "''${BOOT_ARGS[@]}" "''${DISK_ARGS[@]}" "$@" ''; + # 5. QEMU 二进制选择器 (支持外部 QEMU) + # 用法: QEMU_X86=/usr/bin/qemu-system-x86_64 nix run --impure .#x86_64 + getQemuBin = arch: fallback: + let + envVar = if arch == "x86_64" then "QEMU_X86" else "QEMU_RISCV"; + externalPath = builtins.getEnv envVar; + in + if externalPath != "" then externalPath else fallback; + in { packages = { x86_64 = mkRunScript { name = "run-dragonos-x86"; arch = "x86_64"; isNographic = baseConfig.nographic; - qemuBin = "${pkgs.qemu_full}/bin/qemu-system-x86_64"; + qemuBin = getQemuBin "x86_64" "${pkgs.qemu_full}/bin/qemu-system-x86_64"; diskPath = paths.diskX86; kernelPath = paths.kernelX86; }; @@ -166,7 +175,7 @@ name = "run-dragonos-riscv"; arch = "riscv64"; isNographic = true; - qemuBin = "${pkgs.qemu_full}/bin/qemu-system-riscv64"; + qemuBin = getQemuBin "riscv64" "${pkgs.qemu_full}/bin/qemu-system-riscv64"; diskPath = paths.diskRiscv; ubootPath = paths.ubootRiscv; }; diff --git a/user/apps/about/default.nix b/user/apps/about/default.nix new file mode 100644 index 000000000..61a6233d6 --- /dev/null +++ b/user/apps/about/default.nix @@ -0,0 +1,23 @@ +{ stdenv }: + +stdenv.mkDerivation { + pname = "about"; + version = "0.1.0"; + + src = ./.; + + makeFlags = [ + "ARCH=x86_64" + "CROSS_COMPILE=${stdenv.cc.targetPrefix}" + ]; + + installPhase = '' + mkdir -p $out/bin + install -m755 about $out/bin/about.elf + ''; + + meta = { + description = "About utility for DragonOS"; + platforms = [ "x86_64-linux" ]; + }; +} diff --git a/user/apps/default.nix b/user/apps/default.nix new file mode 100644 index 000000000..8d367e50f --- /dev/null +++ b/user/apps/default.nix @@ -0,0 +1,21 @@ +{ pkgs, system, fenix }: + +# Return a list of app derivations to be copied into the rootfs. +let + static = pkgs.pkgsStatic; +in [ + static.busybox + static.curl + static.dropbear + pkgs.glibc + + # Simple C utility + (static.callPackage ./about {}) + + # gVisor syscall tests runner + assets + (pkgs.callPackage ./tests/syscall/gvisor { + fenix = fenix; + system = system; + installDir = "share/gvisor"; + }) +] diff --git a/user/apps/tests/syscall/gvisor/default.nix b/user/apps/tests/syscall/gvisor/default.nix new file mode 100644 index 000000000..33631ee2a --- /dev/null +++ b/user/apps/tests/syscall/gvisor/default.nix @@ -0,0 +1,57 @@ +{ lib, pkgs, fenix, system, installDir }: + +let + fenixPkgs = fenix.packages.${system}; + toolchain = fenixPkgs.combine (with fenixPkgs; [ + minimal.rustc + minimal.cargo + ]); + rustPlatform = pkgs.makeRustPlatform { + cargo = toolchain; + rustc = toolchain; + }; + fullSrc = ./.; + runnerSrc = ./runner; + runnerName = "runner"; + outName = "gvisor-tests"; + # installDir = installDir; + testsArchive = pkgs.fetchurl { + url = "https://cnb.cool/DragonOS-Community/test-suites/-/releases/download/release_20250626/gvisor-syscalls-tests.tar.xz"; + sha256 = "sha256-GSZ0N3oUOerb0lXU4LZ0z4ybD/xZdy7TtfstEoffcsk="; + }; + +in rustPlatform.buildRustPackage { + pname = outName; + version = "0.1.0"; + + src = runnerSrc; + cargoLock = { + lockFile = ./runner/Cargo.lock; + }; + + postInstall = '' + # Ensure runner binary exists and rename to gvisor-test-runner as per Makefile install + mkdir -p $out/bin + if [ -x "$out/bin/${runnerName}" ]; then + mv "$out/bin/${runnerName}" "$out/${installDir}/gvisor-test-runner" + fi + + # Only package files used by install target: whitelist, blocklists, run_tests.sh + mkdir -p $out/${installDir} + install -m644 ${fullSrc}/whitelist.txt $out/${installDir}/ + cp -r ${fullSrc}/blocklists $out/${installDir}/ + install -m755 ${fullSrc}/run_tests.sh $out/${installDir}/ + + # Bundle tests archive for offline systems + mkdir -p $out/${installDir}/tests + tar -xf ${testsArchive} -C $out/${installDir}/tests --strip-components=1 + # Ensure test binaries are executable + find $out/${installDir}/tests -type f -name '*_test' -exec chmod +x {} + || true + ''; + + meta = with lib; { + description = "gVisor syscall test runner and scripts"; + platforms = platforms.linux; + license = licenses.mit; + }; +} diff --git a/user/apps/tests/syscall/gvisor/runner/.gitignore b/user/apps/tests/syscall/gvisor/runner/.gitignore index 786a31746..9511f4a21 100644 --- a/user/apps/tests/syscall/gvisor/runner/.gitignore +++ b/user/apps/tests/syscall/gvisor/runner/.gitignore @@ -1,6 +1,5 @@ # Rust构建产物 /target/ -Cargo.lock # IDE文件 .vscode/ diff --git a/user/apps/tests/syscall/gvisor/runner/Cargo.lock b/user/apps/tests/syscall/gvisor/runner/Cargo.lock new file mode 100644 index 000000000..27e77bcd0 --- /dev/null +++ b/user/apps/tests/syscall/gvisor/runner/Cargo.lock @@ -0,0 +1,558 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "cc" +version = "1.2.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c481bdbf0ed3b892f6f806287d72acd515b352a4ec27a208489b8c1bc839633a" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "clap" +version = "4.5.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "env_logger" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[package]] +name = "gvisor-test-runner" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "clap", + "env_logger", + "log", + "regex", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "humantime" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" + +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "is-terminal" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "js-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "wasm-bindgen" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] diff --git a/user/default.nix b/user/default.nix new file mode 100644 index 000000000..6b98140fa --- /dev/null +++ b/user/default.nix @@ -0,0 +1,78 @@ +{ pkgs, system, fenix, target, buildDir ? "./bin" }: + +let + imageStream = import ./rootfs-tar.nix { inherit pkgs system fenix; }; + diskName = "disk-image-${target}.img"; + + # 构建脚本 - 在bin/目录下构建 + buildScript = pkgs.writeShellApplication { + name = "build-rootfs-image"; + runtimeInputs = [ pkgs.coreutils pkgs.gnutar pkgs.libguestfs ]; + text = '' + set -euo pipefail + + # Ensure build directory exists + mkdir -p "${buildDir}" + + OUTPUT_TAR="${buildDir}/rootfs.tar" + DISK_IMAGE="${buildDir}/${diskName}" + + echo "==> Generating rootfs from docker image stream" + + # 创建临时目录 + TEMP_DIR=$(mktemp -d) + trap 'rm -rf "$TEMP_DIR"' EXIT + + # 执行 streamLayeredImage 脚本生成 docker tar + echo " Running image stream script..." + ${imageStream} > "$TEMP_DIR/image.tar" + + # 提取 layer.tar (rootfs) + echo " Extracting rootfs layer..." + cd "$TEMP_DIR" + tar -xf image.tar + + # 找到 layer.tar 并复制到 bin/ + LAYER_TAR=$(find . -name "layer.tar" | head -1) + if [ -z "$LAYER_TAR" ]; then + echo "Error: layer.tar not found in docker image" + exit 1 + fi + + cp "$LAYER_TAR" "$OLDPWD/$OUTPUT_TAR" + cd "$OLDPWD" + + TAR_SIZE=$(du -h "$OUTPUT_TAR" | cut -f1) + echo " ✓ rootfs.tar created ($TAR_SIZE)" + + echo "==> Building disk image at $DISK_IMAGE" + + TEMP_IMG="$DISK_IMAGE.tmp" + truncate -s 2G "$TEMP_IMG" + + export LIBGUESTFS_CACHEDIR=/tmp + + echo " Partitioning and formatting..." + guestfish -a "$TEMP_IMG" <<'EOF' + run + part-init /dev/sda gpt + part-add /dev/sda primary 2048 -2048 + mkfs ext4 /dev/sda1 + EOF + + echo " Injecting rootfs..." + guestfish -a "$TEMP_IMG" -m /dev/sda1 tar-in "$OUTPUT_TAR" / compress:none + guestfish -a "$TEMP_IMG" -m /dev/sda1 chmod 0755 / + + mv "$TEMP_IMG" "$DISK_IMAGE" + + IMG_SIZE=$(du -h "$DISK_IMAGE" | cut -f1) + echo " ✓ disk image created ($IMG_SIZE)" + + echo "==> Build complete!" + echo " Rootfs tar: $OUTPUT_TAR" + echo " Disk image: $DISK_IMAGE" + ''; + }; + +in buildScript \ No newline at end of file diff --git a/user/flake.lock b/user/flake.lock new file mode 100644 index 000000000..d1ed1de55 --- /dev/null +++ b/user/flake.lock @@ -0,0 +1,66 @@ +{ + "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1765435813, + "narHash": "sha256-C6tT7K1Lx6VsYw1BY5S3OavtapUvEnDQtmQB5DSgbCc=", + "owner": "nix-community", + "repo": "fenix", + "rev": "6399553b7a300c77e7f07342904eb696a5b6bf9d", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1765779637, + "narHash": "sha256-KJ2wa/BLSrTqDjbfyNx70ov/HdgNBCBBSQP3BIzKnv4=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "1306659b587dc277866c7b69eb97e5f07864d8c4", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "fenix": "fenix", + "nixpkgs": "nixpkgs" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1765400135, + "narHash": "sha256-D3+4hfNwUhG0fdCpDhOASLwEQ1jKuHi4mV72up4kLQM=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "fface27171988b3d605ef45cf986c25533116f7e", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/user/rootfs-tar.nix b/user/rootfs-tar.nix new file mode 100644 index 000000000..60fc91121 --- /dev/null +++ b/user/rootfs-tar.nix @@ -0,0 +1,24 @@ +{ pkgs, system, fenix }: + +# 产物是一个可以生成 rootfs.tar 的脚本 +let + apps = import ./apps { inherit pkgs system fenix; }; + + sys-config = pkgs.runCommand "sysconfig" { + src = ./sysconfig; + } '' + mkdir -p $out + cp -r $src/* $out/ + ''; + + # streamLayeredImage 返回一个脚本,执行后生成 docker tar,不会在 store 中存储完整镜像 + imageStream = pkgs.dockerTools.streamLayeredImage { + name = "busybox-rootfs"; + tag = "latest"; + + contents = [ + sys-config + ] ++ apps; + }; + +in imageStream \ No newline at end of file From 50b52580a3e06175cd679fe2e0734b1ac5de309e Mon Sep 17 00:00:00 2001 From: Samuka007 <24754678@student.uwa.edu.au> Date: Thu, 18 Dec 2025 20:18:00 +0800 Subject: [PATCH 03/16] successfully build ext4 rootfs --- flake.nix | 5 +- tools/qemu/default.nix | 41 ++--- tools/qemu/flake.lock | 61 ------- tools/qemu/flake.nix | 188 --------------------- user/apps/default.nix | 4 +- user/apps/tests/syscall/gvisor/default.nix | 2 +- user/default.nix | 41 ++--- user/flake.lock | 66 -------- user/rootfs-tar.nix | 19 +-- 9 files changed, 55 insertions(+), 372 deletions(-) delete mode 100644 tools/qemu/flake.lock delete mode 100644 tools/qemu/flake.nix delete mode 100644 user/flake.lock diff --git a/flake.nix b/flake.nix index eb5b86162..9cf0cf143 100644 --- a/flake.nix +++ b/flake.nix @@ -15,11 +15,14 @@ system = "x86_64-linux"; pkgs = nixpkgs.legacyPackages.${system}; target = "x86_64"; + syscallTestDir = "/usr/share/gvisor"; qemuScripts = import ./tools/qemu/default.nix { lib = pkgs.lib; inherit pkgs; rootfsDisk = "./bin/disk-image-${target}.img"; kernel = "./bin/kernel"; + autotest = "none"; + syscallTestDir = syscallTestDir; }; in { # packages.${system}.default = ; @@ -31,7 +34,7 @@ build-rootfs.${target} = { type = "app"; program = "${pkgs.callPackage ./user/default.nix { - inherit pkgs system fenix target; + inherit pkgs system fenix target syscallTestDir; buildDir = "./bin"; }}/bin/build-rootfs-image"; }; diff --git a/tools/qemu/default.nix b/tools/qemu/default.nix index 05fcd233a..a9b192b27 100644 --- a/tools/qemu/default.nix +++ b/tools/qemu/default.nix @@ -3,6 +3,8 @@ pkgs, rootfsDisk, kernel, + syscallTestDir, + autotest }: let @@ -23,9 +25,7 @@ let mkQemuArgs = { arch, isNographic }: let baseArgs = [ - # "-L" "${pkgs.qemu}/share/qemu" "-m" baseConfig.memory - # FIXED: 补全 smp 参数,和原脚本一致 "-smp" "${baseConfig.cores},cores=${baseConfig.cores},threads=1,sockets=1" "-object" "memory-backend-file,size=${baseConfig.memory},id=${baseConfig.shmId},mem-path=/dev/shm/${baseConfig.shmId},share=on" "-netdev" "user,id=hostnet0,hostfwd=tcp::12580-:12580" @@ -33,18 +33,18 @@ let "-usb" "-device" "qemu-xhci,id=xhci,p2=8,p3=4" "-D" "qemu.log" - - # FIXED: 新增缺失的 Boot Order + + # Boot Order "-boot" "order=d" - # FIXED: 新增缺失的 GDB Stub + # GDB Stub "-s" - # FIXED: 新增缺失的 RTC 设置 + "-rtc" "clock=host,base=localtime" - # FIXED: 新增缺失的 Trace 事件 (注意 * 号在 Nix 字符串里是安全的,escapeShellArgs 会处理它) + # Trace events "-d" "cpu_reset,guest_errors,trace:virtio*,trace:e1000e_rx*,trace:e1000e_tx*,trace:e1000e_irq*" ]; nographicArgs = lib.optionals isNographic ([ - "-nographic" + "--nographic" "-serial" "chardev:mux" "-monitor" "chardev:mux" "-chardev" "stdio,id=mux,mux=on,signal=off,logfile=serial_opt.txt" @@ -106,32 +106,25 @@ let # 但实际上 qemu 会自己创建,这里只需要保证清理。 # 原脚本是 rm -rf ... -> qemu -> rm -rf ... - DRAGONOS_LOGLEVEL=''${DRAGONOS_LOGLEVEL:-4} EXTRA_CMDLINE="${qemuConfig.cmdlineExtra}" # FIXED: 补全缺失的默认内核参数 AUTO_TEST 和 SYSCALL_TEST_DIR - FINAL_CMDLINE="init=${initProgram} loglevel=$DRAGONOS_LOGLEVEL AUTO_TEST=none SYSCALL_TEST_DIR=/opt/tests/gvisor $EXTRA_CMDLINE" + FINAL_CMDLINE="init=${initProgram} AUTO_TEST=${autotest} SYSCALL_TEST_DIR=${syscallTestDir} $EXTRA_CMDLINE" - # --- 1. 生成动态参数 --- ${archSpecificArgs} - # --- 2. 命令预览 --- - GREEN='\033[0;32m' - NC='\033[0m' - BOLD='\033[1m' - - echo -e "''${GREEN}================== DragonOS QEMU Command Preview ==================''${NC}" - echo -e "''${BOLD}Binary:''${NC} sudo ${qemuBin}" - echo -e "''${BOLD}Base Flags:''${NC} ${qemuFlagsStr}" - echo -e "''${BOLD}Arch Flags:''${NC} ''${ARCH_FLAGS[*]}" - echo -e "''${BOLD}Boot Args:''${NC} ''${BOOT_ARGS[*]}" - echo -e "''${BOLD}Disk Args:''${NC} ''${DISK_ARGS[*]}" - echo -e "''${GREEN}==================================================================''${NC}" + echo -e "================== DragonOS QEMU Command Preview ==================" + echo -e "Binary: sudo ${qemuBin}" + echo -e "Base Flags: ${qemuFlagsStr}" + echo -e "Arch Flags: ''${ARCH_FLAGS[*]}" + echo -e "Boot Args: ''${BOOT_ARGS[*]}" + echo -e "Disk Args: ''${DISK_ARGS[*]}" + echo -e "==================================================================" echo "" # --- 3. 执行 --- ${qemuBin} --version - exec sudo ${qemuBin} ${qemuFlagsStr} "''${ARCH_FLAGS[@]}" "''${BOOT_ARGS[@]}" "''${DISK_ARGS[@]}" "$@" + sudo ${qemuBin} ${qemuFlagsStr} "''${ARCH_FLAGS[@]}" "''${BOOT_ARGS[@]}" "''${DISK_ARGS[@]}" "$@" ''; # 5. QEMU 二进制选择器 (支持外部 QEMU) diff --git a/tools/qemu/flake.lock b/tools/qemu/flake.lock deleted file mode 100644 index 5222f3ca0..000000000 --- a/tools/qemu/flake.lock +++ /dev/null @@ -1,61 +0,0 @@ -{ - "nodes": { - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1731533236, - "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1765779637, - "narHash": "sha256-KJ2wa/BLSrTqDjbfyNx70ov/HdgNBCBBSQP3BIzKnv4=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "1306659b587dc277866c7b69eb97e5f07864d8c4", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs" - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/tools/qemu/flake.nix b/tools/qemu/flake.nix deleted file mode 100644 index 8b62d8c90..000000000 --- a/tools/qemu/flake.nix +++ /dev/null @@ -1,188 +0,0 @@ -{ - description = "DragonOS QEMU Boot Environment (Debuggable)"; - - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - # nixpkgs-old.url = "github:NixOS/nixpkgs/cc1d2f5bf5e65c0bddcbc87887bed2f1ff4aa65b"; - flake-utils.url = "github:numtide/flake-utils"; - }; - - outputs = { self, nixpkgs, flake-utils }: - flake-utils.lib.eachDefaultSystem (system: - let - pkgs = nixpkgs.legacyPackages.${system}; - lib = pkgs.lib; - - # 1. 核心配置 - baseConfig = { - nographic = true; - memory = "512M"; - cores = "2"; - shmId = "dragonos-qemu-shm.ram"; - }; - - # 2. 路径定义 - riscv-uboot = pkgs.pkgsCross.riscv64-embedded.buildUBoot { - defconfig = "qemu-riscv64_smode_defconfig"; - extraMeta.platforms = [ "riscv64-linux" ]; - filesToInstall = [ "u-boot.bin" ]; - }; - - paths = { - kernelX86 = "bin/kernel/kernel.elf"; - diskX86 = "bin/disk-image-x86_64.img"; - diskRiscv = "bin/disk-image-riscv64.img"; - ubootRiscv = "${riscv-uboot}/u-boot.bin"; - }; - - # 3. 参数生成器 (Nix List -> Nix List) - mkQemuArgs = { arch, isNographic }: - let - baseArgs = [ - # "-L" "${pkgs.qemu}/share/qemu" - "-m" baseConfig.memory - # FIXED: 补全 smp 参数,和原脚本一致 - "-smp" "${baseConfig.cores},cores=${baseConfig.cores},threads=1,sockets=1" - "-object" "memory-backend-file,size=${baseConfig.memory},id=${baseConfig.shmId},mem-path=/dev/shm/${baseConfig.shmId},share=on" - "-netdev" "user,id=hostnet0,hostfwd=tcp::12580-:12580" - "-device" "virtio-net-pci,vectors=5,netdev=hostnet0,id=net0" - "-usb" - "-device" "qemu-xhci,id=xhci,p2=8,p3=4" - "-D" "qemu.log" - - # FIXED: 新增缺失的 Boot Order - "-boot" "order=d" - # FIXED: 新增缺失的 GDB Stub - "-s" - # FIXED: 新增缺失的 RTC 设置 - "-rtc" "clock=host,base=localtime" - # FIXED: 新增缺失的 Trace 事件 (注意 * 号在 Nix 字符串里是安全的,escapeShellArgs 会处理它) - "-d" "cpu_reset,guest_errors,trace:virtio*,trace:e1000e_rx*,trace:e1000e_tx*,trace:e1000e_irq*" - ]; - nographicArgs = lib.optionals isNographic ([ - "-nographic" - "-serial" "chardev:mux" - "-monitor" "chardev:mux" - "-chardev" "stdio,id=mux,mux=on,signal=off,logfile=serial_opt.txt" - ] ++ (if arch == "riscv64" then [ - "-device" "virtio-serial-device" "-device" "virtconsole,chardev=mux" - ] else [ - "-device" "virtio-serial" "-device" "virtconsole,chardev=mux" - ])); - kernelCmdlinePart = if isNographic then "console=/dev/hvc0" else ""; - in { - flags = baseArgs ++ nographicArgs; - cmdlineExtra = kernelCmdlinePart; - }; - - # 4. 运行脚本生成器 - mkRunScript = { name, arch, isNographic, qemuBin, diskPath, kernelPath ? null, ubootPath ? null }: - let - qemuConfig = mkQemuArgs { inherit arch isNographic; }; - qemuFlagsStr = lib.escapeShellArgs qemuConfig.flags; - - # FIXED: 这里的逻辑彻底重写,以匹配原脚本的行为 - archSpecificArgs = if arch == "x86_64" then '' - MACHINE_TYPE_FLAGS=( "-machine" "q35,memory-backend=${baseConfig.shmId}" ) - - # 保持之前的 CPU 定义 - CPU_FLAGS=( "-cpu" "IvyBridge,apic,x2apic,+fpu,check,+vmx," ) - - # 保持之前的 KVM 加速定义 - if [ "$ACCEL" == "kvm" ]; then - ACCEL_FLAGS=( "-machine" "accel=kvm" "-enable-kvm" ) - else - ACCEL_FLAGS=( "-machine" "accel=tcg" ) - fi - - ARCH_FLAGS=( "''${MACHINE_TYPE_FLAGS[@]}" "''${CPU_FLAGS[@]}" "''${ACCEL_FLAGS[@]}" ) - - BOOT_ARGS=( "-kernel" "${paths.kernelX86}" "-append" "$FINAL_CMDLINE" ) - - DISK_ARGS=( "-device" "virtio-blk-pci,drive=disk" "-device" "pci-bridge,chassis_nr=1,id=pci.1" "-device" "pcie-root-port" "-drive" "id=disk,file=${diskPath},if=none" ) - '' else '' - ARCH_FLAGS=( "-cpu" "sifive-u54" "-machine" "virt,accel=$ACCEL,memory-backend=${baseConfig.shmId}" ) - BOOT_ARGS=( "-kernel" "${ubootPath}" "-append" "$FINAL_CMDLINE" ) - DISK_ARGS=( "-device" "virtio-blk-device,drive=disk" "-drive" "id=disk,file=${diskPath},if=none" ) - ''; - - initProgram = if arch == "riscv64" then "/bin/riscv_rust_init" else "/bin/busybox init"; - - in pkgs.writeScriptBin name '' - #!${pkgs.runtimeShell} - - if [ ! -d "bin" ]; then echo "Error: Please run from project root (bin/ missing)."; exit 1; fi - - ACCEL="tcg" - if [ -e /dev/kvm ] && [ -w /dev/kvm ]; then ACCEL="kvm"; fi - - cleanup() { sudo rm -f /dev/shm/${baseConfig.shmId}; } - trap cleanup EXIT - # FIXED: 既然用了 sudo 运行 qemu,这里创建 shm 也需要权限, - # 但实际上 qemu 会自己创建,这里只需要保证清理。 - # 原脚本是 rm -rf ... -> qemu -> rm -rf ... - - DRAGONOS_LOGLEVEL=''${DRAGONOS_LOGLEVEL:-4} - EXTRA_CMDLINE="${qemuConfig.cmdlineExtra}" - - # FIXED: 补全缺失的默认内核参数 AUTO_TEST 和 SYSCALL_TEST_DIR - FINAL_CMDLINE="init=${initProgram} loglevel=$DRAGONOS_LOGLEVEL AUTO_TEST=none SYSCALL_TEST_DIR=/opt/tests/gvisor $EXTRA_CMDLINE" - - # --- 1. 生成动态参数 --- - ${archSpecificArgs} - - # --- 2. 命令预览 --- - GREEN='\033[0;32m' - NC='\033[0m' - BOLD='\033[1m' - - echo -e "''${GREEN}================== DragonOS QEMU Command Preview ==================''${NC}" - echo -e "''${BOLD}Binary:''${NC} sudo ${qemuBin}" - echo -e "''${BOLD}Base Flags:''${NC} ${qemuFlagsStr}" - echo -e "''${BOLD}Arch Flags:''${NC} ''${ARCH_FLAGS[*]}" - echo -e "''${BOLD}Boot Args:''${NC} ''${BOOT_ARGS[*]}" - echo -e "''${BOLD}Disk Args:''${NC} ''${DISK_ARGS[*]}" - echo -e "''${GREEN}==================================================================''${NC}" - echo "" - - # --- 3. 执行 --- - # FIXED: 加上 sudo - ${qemuBin} --version - exec sudo ${qemuBin} ${qemuFlagsStr} "''${ARCH_FLAGS[@]}" "''${BOOT_ARGS[@]}" "''${DISK_ARGS[@]}" "$@" - ''; - - # 5. QEMU 二进制选择器 (支持外部 QEMU) - # 用法: QEMU_X86=/usr/bin/qemu-system-x86_64 nix run --impure .#x86_64 - getQemuBin = arch: fallback: - let - envVar = if arch == "x86_64" then "QEMU_X86" else "QEMU_RISCV"; - externalPath = builtins.getEnv envVar; - in - if externalPath != "" then externalPath else fallback; - - in { - packages = { - x86_64 = mkRunScript { - name = "run-dragonos-x86"; - arch = "x86_64"; - isNographic = baseConfig.nographic; - qemuBin = getQemuBin "x86_64" "${pkgs.qemu_full}/bin/qemu-system-x86_64"; - diskPath = paths.diskX86; - kernelPath = paths.kernelX86; - }; - - riscv = mkRunScript { - name = "run-dragonos-riscv"; - arch = "riscv64"; - isNographic = true; - qemuBin = getQemuBin "riscv64" "${pkgs.qemu_full}/bin/qemu-system-riscv64"; - diskPath = paths.diskRiscv; - ubootPath = paths.ubootRiscv; - }; - }; - - apps.default = flake-utils.lib.mkApp { drv = self.packages.${system}.x86_64; }; - apps.riscv = flake-utils.lib.mkApp { drv = self.packages.${system}.riscv; }; - } - ); -} \ No newline at end of file diff --git a/user/apps/default.nix b/user/apps/default.nix index 8d367e50f..bb1c5d540 100644 --- a/user/apps/default.nix +++ b/user/apps/default.nix @@ -1,4 +1,4 @@ -{ pkgs, system, fenix }: +{ pkgs, system, fenix, syscallTestDir }: # Return a list of app derivations to be copied into the rootfs. let @@ -16,6 +16,6 @@ in [ (pkgs.callPackage ./tests/syscall/gvisor { fenix = fenix; system = system; - installDir = "share/gvisor"; + installDir = syscallTestDir; }) ] diff --git a/user/apps/tests/syscall/gvisor/default.nix b/user/apps/tests/syscall/gvisor/default.nix index 33631ee2a..2394a04c8 100644 --- a/user/apps/tests/syscall/gvisor/default.nix +++ b/user/apps/tests/syscall/gvisor/default.nix @@ -31,7 +31,7 @@ in rustPlatform.buildRustPackage { postInstall = '' # Ensure runner binary exists and rename to gvisor-test-runner as per Makefile install - mkdir -p $out/bin + mkdir -p "$out/${installDir}" if [ -x "$out/bin/${runnerName}" ]; then mv "$out/bin/${runnerName}" "$out/${installDir}/gvisor-test-runner" fi diff --git a/user/default.nix b/user/default.nix index 6b98140fa..e8d8344ec 100644 --- a/user/default.nix +++ b/user/default.nix @@ -1,13 +1,13 @@ -{ pkgs, system, fenix, target, buildDir ? "./bin" }: +{ pkgs, system, fenix, target, buildDir, syscallTestDir }: let - imageStream = import ./rootfs-tar.nix { inherit pkgs system fenix; }; + image = import ./rootfs-tar.nix { inherit pkgs system fenix syscallTestDir; }; diskName = "disk-image-${target}.img"; # 构建脚本 - 在bin/目录下构建 buildScript = pkgs.writeShellApplication { name = "build-rootfs-image"; - runtimeInputs = [ pkgs.coreutils pkgs.gnutar pkgs.libguestfs ]; + runtimeInputs = [ pkgs.coreutils pkgs.gnutar pkgs.libguestfs-with-appliance pkgs.findutils ]; text = '' set -euo pipefail @@ -17,20 +17,17 @@ let OUTPUT_TAR="${buildDir}/rootfs.tar" DISK_IMAGE="${buildDir}/${diskName}" - echo "==> Generating rootfs from docker image stream" + echo "==> Generating rootfs" # 创建临时目录 TEMP_DIR=$(mktemp -d) trap 'rm -rf "$TEMP_DIR"' EXIT - - # 执行 streamLayeredImage 脚本生成 docker tar - echo " Running image stream script..." - ${imageStream} > "$TEMP_DIR/image.tar" - + # 提取 layer.tar (rootfs) echo " Extracting rootfs layer..." cd "$TEMP_DIR" - tar -xf image.tar + tar -xzf ${image} + chmod +w -R . # 找到 layer.tar 并复制到 bin/ LAYER_TAR=$(find . -name "layer.tar" | head -1) @@ -41,29 +38,35 @@ let cp "$LAYER_TAR" "$OLDPWD/$OUTPUT_TAR" cd "$OLDPWD" - + TAR_SIZE=$(du -h "$OUTPUT_TAR" | cut -f1) echo " ✓ rootfs.tar created ($TAR_SIZE)" echo "==> Building disk image at $DISK_IMAGE" + export LIBGUESTFS_CACHEDIR=/tmp + export LIBGUESTFS_BACKEND=direct + + # 创建磁盘镜像并初始化文件系统 + echo " Creating disk image..." TEMP_IMG="$DISK_IMAGE.tmp" truncate -s 2G "$TEMP_IMG" - export LIBGUESTFS_CACHEDIR=/tmp - - echo " Partitioning and formatting..." - guestfish -a "$TEMP_IMG" <<'EOF' + # 使用 guestfish 创建分区并注入 tar + echo " Initializing disk and copying rootfs..." + guestfish -a "$TEMP_IMG" < Date: Thu, 18 Dec 2025 20:56:19 +0800 Subject: [PATCH 04/16] Add configurable rootfs and partition types in build script --- user/default.nix | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/user/default.nix b/user/default.nix index e8d8344ec..5e06dfe39 100644 --- a/user/default.nix +++ b/user/default.nix @@ -1,4 +1,13 @@ -{ pkgs, system, fenix, target, buildDir, syscallTestDir }: +{ + pkgs, + system, + fenix, + target, + buildDir, + syscallTestDir, + rootfsType ? "vfat", + partitionType ? "mbr" +}: let image = import ./rootfs-tar.nix { inherit pkgs system fenix syscallTestDir; }; @@ -56,9 +65,9 @@ let echo " Initializing disk and copying rootfs..." guestfish -a "$TEMP_IMG" < Date: Thu, 18 Dec 2025 22:23:08 +0800 Subject: [PATCH 05/16] Update flake.lock and flake.nix for nixpkgs version and enhance rootfs build process for vfat --- flake.lock | 8 ++++---- flake.nix | 2 +- user/default.nix | 30 ++++++++++++++++++++++++++++-- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/flake.lock b/flake.lock index d1ed1de55..58bcfa079 100644 --- a/flake.lock +++ b/flake.lock @@ -23,16 +23,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1765779637, - "narHash": "sha256-KJ2wa/BLSrTqDjbfyNx70ov/HdgNBCBBSQP3BIzKnv4=", + "lastModified": 1765838191, + "narHash": "sha256-m5KWt1nOm76ILk/JSCxBM4MfK3rYY7Wq9/TZIIeGnT8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "1306659b587dc277866c7b69eb97e5f07864d8c4", + "rev": "c6f52ebd45e5925c188d1a20119978aa4ffd5ef6", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-unstable", + "ref": "nixos-25.11", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index 9cf0cf143..bbf109b7e 100644 --- a/flake.nix +++ b/flake.nix @@ -6,7 +6,7 @@ url = "github:nix-community/fenix"; inputs.nixpkgs.follows = "nixpkgs"; }; - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; }; outputs = { self, nixpkgs, fenix }: diff --git a/user/default.nix b/user/default.nix index 5e06dfe39..623a255fe 100644 --- a/user/default.nix +++ b/user/default.nix @@ -51,6 +51,32 @@ let TAR_SIZE=$(du -h "$OUTPUT_TAR" | cut -f1) echo " ✓ rootfs.tar created ($TAR_SIZE)" + # 如果是 vfat 文件系统,需要特殊处理:排除 /nix/store,解引用符号链接 + FINAL_TAR="$OUTPUT_TAR" + # shellcheck disable=SC2050 + if [ "${rootfsType}" = "vfat" ]; then + echo " Processing rootfs for vfat (excluding /nix/store, dereferencing symlinks)..." + + EXTRACT_DIR=$(mktemp -d) + FILTERED_TAR="${buildDir}/rootfs-filtered.tar" + + # 添加到清理列表 + trap 'chmod +w -R "$TEMP_DIR" "$EXTRACT_DIR" && rm -rf "$TEMP_DIR" "$EXTRACT_DIR"' EXIT + + # 解压原始 tar,排除 /nix/store + echo " Extracting and filtering..." + tar --exclude='./nix' --exclude='./nix/store' -xf "$OUTPUT_TAR" -C "$EXTRACT_DIR" + + # 重新打包,解引用符号链接和硬链接 + echo " Re-packing with dereferenced links..." + tar --dereference --hard-dereference -cf "$FILTERED_TAR" -C "$EXTRACT_DIR" . + + FILTERED_SIZE=$(du -h "$FILTERED_TAR" | cut -f1) + echo " ✓ filtered rootfs.tar created ($FILTERED_SIZE, /nix/store excluded)" + + FINAL_TAR="$FILTERED_TAR" + fi + echo "==> Building disk image at $DISK_IMAGE" export LIBGUESTFS_CACHEDIR=/tmp @@ -59,7 +85,7 @@ let # 创建磁盘镜像并初始化文件系统 echo " Creating disk image..." TEMP_IMG="$DISK_IMAGE.tmp" - truncate -s 2G "$TEMP_IMG" + truncate -s 4G "$TEMP_IMG" # 使用 guestfish 创建分区并注入 tar echo " Initializing disk and copying rootfs..." @@ -69,7 +95,7 @@ let part-add /dev/sda primary 2048 -2048 mkfs ${rootfsType} /dev/sda1 mount /dev/sda1 / - tar-in $OUTPUT_TAR / + tar-in $FINAL_TAR / chmod 0755 / umount / sync From 402109e248181fb3962d393bfca2b5fd807108b2 Mon Sep 17 00:00:00 2001 From: Samuka007 <24754678@student.uwa.edu.au> Date: Fri, 19 Dec 2025 13:21:08 +0800 Subject: [PATCH 06/16] Add QEMU firmware package and update run script to use it --- tools/qemu/default.nix | 18 ++++++------------ tools/qemu/qemu-firmware.nix | 27 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 12 deletions(-) create mode 100644 tools/qemu/qemu-firmware.nix diff --git a/tools/qemu/default.nix b/tools/qemu/default.nix index a9b192b27..f11286775 100644 --- a/tools/qemu/default.nix +++ b/tools/qemu/default.nix @@ -8,6 +8,8 @@ }: let + qemuFirmware = pkgs.callPackage ./qemu-firmware.nix {}; + baseConfig = { nographic = true; memory = "512M"; @@ -42,6 +44,7 @@ let "-rtc" "clock=host,base=localtime" # Trace events "-d" "cpu_reset,guest_errors,trace:virtio*,trace:e1000e_rx*,trace:e1000e_tx*,trace:e1000e_irq*" + "-trace" "fw_cfg*" ]; nographicArgs = lib.optionals isNographic ([ "--nographic" @@ -124,31 +127,22 @@ let # --- 3. 执行 --- ${qemuBin} --version - sudo ${qemuBin} ${qemuFlagsStr} "''${ARCH_FLAGS[@]}" "''${BOOT_ARGS[@]}" "''${DISK_ARGS[@]}" "$@" + sudo ${qemuBin} ${qemuFlagsStr} -L ${qemuFirmware} "''${ARCH_FLAGS[@]}" "''${BOOT_ARGS[@]}" "''${DISK_ARGS[@]}" "$@" ''; - # 5. QEMU 二进制选择器 (支持外部 QEMU) - # 用法: QEMU_X86=/usr/bin/qemu-system-x86_64 nix run --impure .#x86_64 - getQemuBin = arch: fallback: - let - envVar = if arch == "x86_64" then "QEMU_X86" else "QEMU_RISCV"; - externalPath = builtins.getEnv envVar; - in - if externalPath != "" then externalPath else fallback; - script = { x86_64 = mkRunScript { name = "run-dragonos-x86"; arch = "x86_64"; isNographic = baseConfig.nographic; - qemuBin = getQemuBin "x86_64" "${pkgs.qemu_full}/bin/qemu-system-x86_64"; + qemuBin = "${pkgs.qemu_kvm}/bin/qemu-system-x86_64"; }; riscv64 = mkRunScript { name = "run-dragonos-riscv"; arch = "riscv64"; isNographic = true; - qemuBin = getQemuBin "riscv64" "${pkgs.qemu_full}/bin/qemu-system-riscv64"; + qemuBin = "${pkgs.qemu_kvm}/bin/qemu-system-riscv64"; }; }; in script \ No newline at end of file diff --git a/tools/qemu/qemu-firmware.nix b/tools/qemu/qemu-firmware.nix new file mode 100644 index 000000000..000eaa125 --- /dev/null +++ b/tools/qemu/qemu-firmware.nix @@ -0,0 +1,27 @@ +{ pkgs }: + +pkgs.stdenv.mkDerivation { + pname = "qemu-system-data"; + version = "10.1.3+ds-1"; + + src = pkgs.fetchurl { + url = "http://ftp.cn.debian.org/debian/pool/main/q/qemu/qemu-system-data_10.1.3+ds-1_all.deb"; + sha256 = "0nz3whjiw3qmp27mhidh8fk7gfkcvk2vk7z4pr6w3is9hd2g36sg"; + }; + + nativeBuildInputs = with pkgs; [ dpkg ]; + + unpackPhase = '' + dpkg-deb -x $src . + ''; + + installPhase = '' + mkdir -p $out + cp -r usr/share/qemu/* $out/ + ''; + + meta = with pkgs.lib; { + description = "QEMU firmware files from Debian package"; + platforms = platforms.all; + }; +} From ed5521bd757145b5161495e341f93c0eb063fce3 Mon Sep 17 00:00:00 2001 From: Samuka007 <24754678@student.uwa.edu.au> Date: Fri, 19 Dec 2025 13:52:03 +0800 Subject: [PATCH 07/16] fixed nix environment for less frequent source fetching --- tools/nix-dev-shell/flake.lock | 8 ++++---- tools/nix-dev-shell/flake.nix | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/nix-dev-shell/flake.lock b/tools/nix-dev-shell/flake.lock index 3f7edbf56..3aed1fdef 100644 --- a/tools/nix-dev-shell/flake.lock +++ b/tools/nix-dev-shell/flake.lock @@ -41,16 +41,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1765779637, - "narHash": "sha256-KJ2wa/BLSrTqDjbfyNx70ov/HdgNBCBBSQP3BIzKnv4=", + "lastModified": 1765838191, + "narHash": "sha256-m5KWt1nOm76ILk/JSCxBM4MfK3rYY7Wq9/TZIIeGnT8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "1306659b587dc277866c7b69eb97e5f07864d8c4", + "rev": "c6f52ebd45e5925c188d1a20119978aa4ffd5ef6", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-unstable", + "ref": "nixos-25.11", "repo": "nixpkgs", "type": "github" } diff --git a/tools/nix-dev-shell/flake.nix b/tools/nix-dev-shell/flake.nix index 328158b41..a808b8f2e 100644 --- a/tools/nix-dev-shell/flake.nix +++ b/tools/nix-dev-shell/flake.nix @@ -1,7 +1,7 @@ { description = "dragonos-nix-dev"; inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; fenix.url = "github:nix-community/fenix"; fenix.inputs.nixpkgs.follows = "nixpkgs"; flake-utils.url = "github:numtide/flake-utils"; From 3d507f2a5edb773ef642c3efa64e5019bd593039 Mon Sep 17 00:00:00 2001 From: Samuka007 <24754678@student.uwa.edu.au> Date: Fri, 19 Dec 2025 14:03:17 +0800 Subject: [PATCH 08/16] feat(nix): add gcc for kernel build in --- tools/nix-dev-shell/flake.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/nix-dev-shell/flake.nix b/tools/nix-dev-shell/flake.nix index a808b8f2e..df0abf852 100644 --- a/tools/nix-dev-shell/flake.nix +++ b/tools/nix-dev-shell/flake.nix @@ -23,6 +23,7 @@ llvm libclang rust-toolchain + gcc ]; env = { From 3c0d5e05825a52d960a70253d8eaed1714b94786 Mon Sep 17 00:00:00 2001 From: Samuka007 <24754678@student.uwa.edu.au> Date: Fri, 19 Dec 2025 17:54:18 +0800 Subject: [PATCH 09/16] refactor: gVisor tests nix build and fakeroot image pack up Split the test runner and test data into separate derivations (runner, tests) and expose them via symlinkJoin to avoid unnecessary Rust rebuilds. Use sourceByRegex for test sources and patchelf to set the interpreter/RPATH for bundled test binaries. Harden the image builder script: ensure build dir exists, use fakeroot for tar operations, chmod temp dirs before cleanup, adjust traps, and increase the disk image size to 5G. Add .envrc, ignore .direnv, and make minor Nix formatting/comment tweaks. --- .envrc | 1 + .gitignore | 1 + flake.nix | 4 +- user/apps/default.nix | 2 + user/apps/tests/syscall/gvisor/default.nix | 91 +++++++++++++++------- user/default.nix | 75 +++++++++--------- 6 files changed, 105 insertions(+), 69 deletions(-) create mode 100644 .envrc diff --git a/.envrc b/.envrc new file mode 100644 index 000000000..ed11f2b3d --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake ./tools/nix-dev-shell \ No newline at end of file diff --git a/.gitignore b/.gitignore index 37810f418..d83944e00 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ compile_commands.json *.cpio *.cpio.xz *.cpio* +.direnv/ diff --git a/flake.nix b/flake.nix index bbf109b7e..a8088f456 100644 --- a/flake.nix +++ b/flake.nix @@ -16,7 +16,7 @@ pkgs = nixpkgs.legacyPackages.${system}; target = "x86_64"; syscallTestDir = "/usr/share/gvisor"; - qemuScripts = import ./tools/qemu/default.nix { + qemuScripts = import ./tools/qemu/default.nix { lib = pkgs.lib; inherit pkgs; rootfsDisk = "./bin/disk-image-${target}.img"; @@ -40,4 +40,4 @@ }; }; }; -} \ No newline at end of file +} diff --git a/user/apps/default.nix b/user/apps/default.nix index bb1c5d540..0318b72d1 100644 --- a/user/apps/default.nix +++ b/user/apps/default.nix @@ -8,6 +8,8 @@ in [ static.curl static.dropbear pkgs.glibc + # pkgs.gccNGPackages_15.libstdcxx + # pkgs.libcxx # Simple C utility (static.callPackage ./about {}) diff --git a/user/apps/tests/syscall/gvisor/default.nix b/user/apps/tests/syscall/gvisor/default.nix index 2394a04c8..d036bed3b 100644 --- a/user/apps/tests/syscall/gvisor/default.nix +++ b/user/apps/tests/syscall/gvisor/default.nix @@ -10,45 +10,76 @@ let cargo = toolchain; rustc = toolchain; }; - fullSrc = ./.; - runnerSrc = ./runner; - runnerName = "runner"; - outName = "gvisor-tests"; - # installDir = installDir; + testsArchive = pkgs.fetchurl { url = "https://cnb.cool/DragonOS-Community/test-suites/-/releases/download/release_20250626/gvisor-syscalls-tests.tar.xz"; sha256 = "sha256-GSZ0N3oUOerb0lXU4LZ0z4ybD/xZdy7TtfstEoffcsk="; }; -in rustPlatform.buildRustPackage { - pname = outName; - version = "0.1.0"; + # 1. Build the Rust runner separately + # This ensures that changes to test scripts or data don't trigger a Rust rebuild. + runner = rustPlatform.buildRustPackage { + pname = "gvisor-test-runner-bin"; + version = "0.1.0"; + + src = ./runner; + cargoLock = { + lockFile = ./runner/Cargo.lock; + }; - src = runnerSrc; - cargoLock = { - lockFile = ./runner/Cargo.lock; + # Move the binary to the expected install directory structure + postInstall = '' + mkdir -p $out/${installDir} + if [ -f "$out/bin/runner" ]; then + mv "$out/bin/runner" "$out/${installDir}/gvisor-test-runner" + # Clean up empty bin directory if it exists, to avoid clutter in symlinkJoin + rmdir "$out/bin" || true + fi + ''; }; - postInstall = '' - # Ensure runner binary exists and rename to gvisor-test-runner as per Makefile install - mkdir -p "$out/${installDir}" - if [ -x "$out/bin/${runnerName}" ]; then - mv "$out/bin/${runnerName}" "$out/${installDir}/gvisor-test-runner" - fi - - # Only package files used by install target: whitelist, blocklists, run_tests.sh - mkdir -p $out/${installDir} - install -m644 ${fullSrc}/whitelist.txt $out/${installDir}/ - cp -r ${fullSrc}/blocklists $out/${installDir}/ - install -m755 ${fullSrc}/run_tests.sh $out/${installDir}/ - - # Bundle tests archive for offline systems - mkdir -p $out/${installDir}/tests - tar -xf ${testsArchive} -C $out/${installDir}/tests --strip-components=1 - # Ensure test binaries are executable - find $out/${installDir}/tests -type f -name '*_test' -exec chmod +x {} + || true - ''; + # 2. Prepare the test data, scripts, and patched binaries + # This derivation handles downloading, extracting, and patching the tests. + tests = pkgs.stdenv.mkDerivation { + pname = "gvisor-tests-data"; + version = "0.1.0"; + + # Use sourceByRegex to only depend on relevant files. + # This prevents rebuilds when files in ./runner change. + src = lib.sourceByRegex ./. [ + "^whitelist\.txt$" + "^blocklists" + "^blocklists/.*" + "^run_tests\.sh$" + ]; + + nativeBuildInputs = [ pkgs.patchelf ]; + + installPhase = '' + mkdir -p $out/${installDir} + + install -m644 whitelist.txt $out/${installDir}/ + cp -r blocklists $out/${installDir}/ + install -m755 run_tests.sh $out/${installDir}/ + + # Bundle tests archive for offline systems + mkdir -p $out/${installDir}/tests + tar -xf ${testsArchive} -C $out/${installDir}/tests --strip-components=1 + + # Ensure test binaries are executable + find $out/${installDir}/tests -type f -name '*_test' -exec chmod +xw {} + || true + + # Use patchelf to set the interpreter and RPATH for the test binaries + find $out/${installDir}/tests -type f -name '*_test' -exec patchelf \ + --set-interpreter $(cat ${pkgs.stdenv.cc}/nix-support/dynamic-linker) \ + --set-rpath ${lib.makeLibraryPath [ pkgs.stdenv.cc.cc.lib ]} \ + {} \; + ''; + }; +in pkgs.symlinkJoin { + name = "gvisor-tests"; + paths = [ runner tests ]; meta = with lib; { description = "gVisor syscall test runner and scripts"; platforms = platforms.linux; diff --git a/user/default.nix b/user/default.nix index 623a255fe..fe8c445e3 100644 --- a/user/default.nix +++ b/user/default.nix @@ -1,12 +1,12 @@ -{ - pkgs, - system, - fenix, - target, - buildDir, - syscallTestDir, - rootfsType ? "vfat", - partitionType ? "mbr" +{ + pkgs, + system, + fenix, + target, + buildDir, + syscallTestDir, + rootfsType ? "vfat", + partitionType ? "mbr" }: let @@ -19,74 +19,75 @@ let runtimeInputs = [ pkgs.coreutils pkgs.gnutar pkgs.libguestfs-with-appliance pkgs.findutils ]; text = '' set -euo pipefail - + # Ensure build directory exists mkdir -p "${buildDir}" - + OUTPUT_TAR="${buildDir}/rootfs.tar" DISK_IMAGE="${buildDir}/${diskName}" - + echo "==> Generating rootfs" - + # 创建临时目录 TEMP_DIR=$(mktemp -d) - trap 'rm -rf "$TEMP_DIR"' EXIT + trap 'chmod +w -R "$TEMP_DIR" && rm -rf "$TEMP_DIR"' EXIT # 提取 layer.tar (rootfs) echo " Extracting rootfs layer..." cd "$TEMP_DIR" tar -xzf ${image} - chmod +w -R . - + # 找到 layer.tar 并复制到 bin/ LAYER_TAR=$(find . -name "layer.tar" | head -1) if [ -z "$LAYER_TAR" ]; then echo "Error: layer.tar not found in docker image" exit 1 fi - + cp "$LAYER_TAR" "$OLDPWD/$OUTPUT_TAR" cd "$OLDPWD" TAR_SIZE=$(du -h "$OUTPUT_TAR" | cut -f1) echo " ✓ rootfs.tar created ($TAR_SIZE)" - + # 如果是 vfat 文件系统,需要特殊处理:排除 /nix/store,解引用符号链接 FINAL_TAR="$OUTPUT_TAR" # shellcheck disable=SC2050 if [ "${rootfsType}" = "vfat" ]; then echo " Processing rootfs for vfat (excluding /nix/store, dereferencing symlinks)..." - + EXTRACT_DIR=$(mktemp -d) FILTERED_TAR="${buildDir}/rootfs-filtered.tar" - + # 添加到清理列表 trap 'chmod +w -R "$TEMP_DIR" "$EXTRACT_DIR" && rm -rf "$TEMP_DIR" "$EXTRACT_DIR"' EXIT - + # 解压原始 tar,排除 /nix/store - echo " Extracting and filtering..." - tar --exclude='./nix' --exclude='./nix/store' -xf "$OUTPUT_TAR" -C "$EXTRACT_DIR" - + echo " Extracting and not filtering..." + # tar --exclude='nix' -xf "$OUTPUT_TAR" -C "$EXTRACT_DIR" + chmod +w -R "$TEMP_DIR" "$EXTRACT_DIR" + fakeroot tar --owner=0 --group=0 --numeric-owner -xf "$OUTPUT_TAR" -C "$EXTRACT_DIR" + # 重新打包,解引用符号链接和硬链接 echo " Re-packing with dereferenced links..." - tar --dereference --hard-dereference -cf "$FILTERED_TAR" -C "$EXTRACT_DIR" . - + fakeroot tar --owner=0 --group=0 --numeric-owner --dereference --hard-dereference -cf "$FILTERED_TAR" -C "$EXTRACT_DIR" . + FILTERED_SIZE=$(du -h "$FILTERED_TAR" | cut -f1) - echo " ✓ filtered rootfs.tar created ($FILTERED_SIZE, /nix/store excluded)" - + echo " ✓ Re-packed rootfs.tar created ($FILTERED_SIZE)" + FINAL_TAR="$FILTERED_TAR" fi - + echo "==> Building disk image at $DISK_IMAGE" - + export LIBGUESTFS_CACHEDIR=/tmp export LIBGUESTFS_BACKEND=direct - + # 创建磁盘镜像并初始化文件系统 echo " Creating disk image..." TEMP_IMG="$DISK_IMAGE.tmp" - truncate -s 4G "$TEMP_IMG" - + truncate -s 5G "$TEMP_IMG" + # 使用 guestfish 创建分区并注入 tar echo " Initializing disk and copying rootfs..." guestfish -a "$TEMP_IMG" < Build complete!" echo " Rootfs tar: $OUTPUT_TAR" echo " Disk image: $DISK_IMAGE" ''; }; -in buildScript \ No newline at end of file +in buildScript From 60c5e53c0d9ce4603997af90bb5b0ba7f1e68997 Mon Sep 17 00:00:00 2001 From: Samuka007 <24754678@student.uwa.edu.au> Date: Fri, 19 Dec 2025 20:07:05 +0800 Subject: [PATCH 10/16] feat: boot from ext4, dynamic disk size --- flake.nix | 1 + user/default.nix | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index a8088f456..b8c875074 100644 --- a/flake.nix +++ b/flake.nix @@ -35,6 +35,7 @@ type = "app"; program = "${pkgs.callPackage ./user/default.nix { inherit pkgs system fenix target syscallTestDir; + rootfsType = "ext4"; buildDir = "./bin"; }}/bin/build-rootfs-image"; }; diff --git a/user/default.nix b/user/default.nix index fe8c445e3..172056fbd 100644 --- a/user/default.nix +++ b/user/default.nix @@ -66,7 +66,8 @@ let echo " Extracting and not filtering..." # tar --exclude='nix' -xf "$OUTPUT_TAR" -C "$EXTRACT_DIR" chmod +w -R "$TEMP_DIR" "$EXTRACT_DIR" - fakeroot tar --owner=0 --group=0 --numeric-owner -xf "$OUTPUT_TAR" -C "$EXTRACT_DIR" + fakeroot tar --owner=0 --group=0 --numeric-owner --exclude='proc' --exclude='dev' \ + --exclude='sys' -xf "$OUTPUT_TAR" -C "$EXTRACT_DIR" # 当 RootFS 里包含这几个文件夹时会报错 # 重新打包,解引用符号链接和硬链接 echo " Re-packing with dereferenced links..." @@ -86,7 +87,11 @@ let # 创建磁盘镜像并初始化文件系统 echo " Creating disk image..." TEMP_IMG="$DISK_IMAGE.tmp" - truncate -s 5G "$TEMP_IMG" + + # 计算所需磁盘大小:tar包大小 + 1G 缓冲空间 + TAR_SIZE_KB=$(du -k "$FINAL_TAR" | cut -f1) + DISK_SIZE_KB=$(( TAR_SIZE_KB + 1024 * 1024 )) + truncate -s "''${DISK_SIZE_KB}K" "$TEMP_IMG" # 使用 guestfish 创建分区并注入 tar echo " Initializing disk and copying rootfs..." From 02b61b9b0dbed565ffe694811b8cd5a0d1fb2b90 Mon Sep 17 00:00:00 2001 From: Samuka007 <24754678@student.uwa.edu.au> Date: Fri, 19 Dec 2025 20:25:27 +0800 Subject: [PATCH 11/16] feat: use autoPatchHook for syscall tests --- user/apps/tests/syscall/gvisor/default.nix | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/user/apps/tests/syscall/gvisor/default.nix b/user/apps/tests/syscall/gvisor/default.nix index d036bed3b..81002d3a9 100644 --- a/user/apps/tests/syscall/gvisor/default.nix +++ b/user/apps/tests/syscall/gvisor/default.nix @@ -53,7 +53,9 @@ let "^run_tests\.sh$" ]; - nativeBuildInputs = [ pkgs.patchelf ]; + nativeBuildInputs = [ pkgs.autoPatchelfHook ]; + + buildInputs = [ pkgs.stdenv.cc.cc.lib ]; installPhase = '' mkdir -p $out/${installDir} @@ -66,14 +68,9 @@ let mkdir -p $out/${installDir}/tests tar -xf ${testsArchive} -C $out/${installDir}/tests --strip-components=1 - # Ensure test binaries are executable - find $out/${installDir}/tests -type f -name '*_test' -exec chmod +xw {} + || true - - # Use patchelf to set the interpreter and RPATH for the test binaries - find $out/${installDir}/tests -type f -name '*_test' -exec patchelf \ - --set-interpreter $(cat ${pkgs.stdenv.cc}/nix-support/dynamic-linker) \ - --set-rpath ${lib.makeLibraryPath [ pkgs.stdenv.cc.cc.lib ]} \ - {} \; + runHook preInstall + find $out/${installDir}/tests -type f -name '*_test' -exec install -m755 {} $out/${installDir}/tests \; || true + runHook postInstall ''; }; From 7d2878520b8021079244e16511cf5b26db10db4d Mon Sep 17 00:00:00 2001 From: Samuka007 <24754678@student.uwa.edu.au> Date: Sat, 20 Dec 2025 14:08:23 +0800 Subject: [PATCH 12/16] Add multi-target support and unify diskPath Introduce mkApps to generate app entries for x86_64 and riscv64, pass diskPath through the flake and user build scripts, refactor the QEMU runner to produce arch-specific args, and add cross/musl handling for user apps and rootfs generation --- flake.lock | 34 +++++++++++++++ flake.nix | 94 ++++++++++++++++++++++++++++-------------- tools/qemu/default.nix | 94 +++++++++++++++++++++--------------------- user/apps/default.nix | 46 +++++++++++++++++---- user/default.nix | 19 +++++---- user/rootfs-tar.nix | 7 ++-- 6 files changed, 197 insertions(+), 97 deletions(-) diff --git a/flake.lock b/flake.lock index 58bcfa079..0fc081fae 100644 --- a/flake.lock +++ b/flake.lock @@ -21,6 +21,24 @@ "type": "github" } }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1765835352, + "narHash": "sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "a34fae9c08a15ad73f295041fec82323541400a9", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1765838191, @@ -37,9 +55,25 @@ "type": "github" } }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1765674936, + "narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, "root": { "inputs": { "fenix": "fenix", + "flake-parts": "flake-parts", "nixpkgs": "nixpkgs" } }, diff --git a/flake.nix b/flake.nix index b8c875074..1b83b825c 100644 --- a/flake.nix +++ b/flake.nix @@ -7,38 +7,72 @@ inputs.nixpkgs.follows = "nixpkgs"; }; nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; + flake-parts.url = "github:hercules-ci/flake-parts"; }; - outputs = { self, nixpkgs, fenix }: - let - # targetSystems = [ "x86_64-linux" "riscv64-linux" ]; # TODO: Support multi-arch - system = "x86_64-linux"; - pkgs = nixpkgs.legacyPackages.${system}; - target = "x86_64"; - syscallTestDir = "/usr/share/gvisor"; - qemuScripts = import ./tools/qemu/default.nix { + outputs = inputs@{ self, nixpkgs, fenix, flake-parts }: flake-parts.lib.mkFlake { inherit inputs; } { + systems = [ + "x86_64-linux" + ]; + perSystem = { self', inputs', system, ... }: + let + nixpkgs = inputs.nixpkgs; + fenix = inputs.fenix; + pkgs = nixpkgs.legacyPackages.${system}; lib = pkgs.lib; - inherit pkgs; - rootfsDisk = "./bin/disk-image-${target}.img"; - kernel = "./bin/kernel"; - autotest = "none"; - syscallTestDir = syscallTestDir; - }; - in { - # packages.${system}.default = ; - apps.${system} = { - start.${target} = { - type = "app"; - program = "${qemuScripts.${target}}/bin/run-dragonos-x86"; - }; - build-rootfs.${target} = { - type = "app"; - program = "${pkgs.callPackage ./user/default.nix { - inherit pkgs system fenix target syscallTestDir; - rootfsType = "ext4"; - buildDir = "./bin"; - }}/bin/build-rootfs-image"; - }; + syscallTestDir = "/usr/share/gvisor"; + rootfsType = "ext4"; + buildDir = "./bin"; # Specifying temp file location + + mkOutputs = target: + let + diskPath = "${buildDir}/disk-image-${target}.img"; + qemuScripts = import ./tools/qemu/default.nix { + inherit lib pkgs syscallTestDir diskPath; + # 启动相关参数: + # 内核位置 + kernel = "${buildDir}/kernel/kernel.elf"; # TODO: make it a drv 用nix构建内核,避免指定相对目录 + # 自动测试项目,指定内核启动环境变量参数 AUTO_TEST + autotest = "syscall"; + }; + + startPkg = qemuScripts.${target}; + rootfsPkg = pkgs.callPackage ./user/default.nix { + inherit lib pkgs nixpkgs fenix system target syscallTestDir rootfsType buildDir diskPath; + }; + in + { + apps = { + # start-${target} 的产物只是一个shell脚本,因此启动相关的参数,直接在上面修改即可, + # 脚本不占什么空间所以重复eval也没关系,并且最终产出的脚本可读性更好. + "start-${target}" = { + type = "app"; + program = "${startPkg}/bin/run-dragonos"; + meta.description = "以 ${target} 启动DragonOS"; + }; + # rootfs 中涉及到基于docker镜像的rootfs构建,修改了 user/ 下软件包相关内容后, + # rootfs 的docker镜像会重复构建,并且由于nix特性,副本会全部保留 + # 因此可能会占很多空间,如果要清理空间请执行 nix store gc + "rootfs-${target}" = { + type = "app"; + program = "${rootfsPkg}/bin/build-rootfs-image"; + meta.description = "构建 ${target} rootfs 镜像"; + }; + }; + packages = { + "start-${target}" = startPkg; + "rootfs-${target}" = rootfsPkg; + }; + }; + + allOutputs = map mkOutputs [ "x86_64" "riscv64" ]; + merged = lib.foldl' (acc: elem: { + apps = acc.apps // elem.apps; + packages = acc.packages // elem.packages; + }) { apps = {}; packages = {}; } allOutputs; + + in { + inherit (merged) apps packages; }; - }; + }; } diff --git a/tools/qemu/default.nix b/tools/qemu/default.nix index f11286775..e4e07baf6 100644 --- a/tools/qemu/default.nix +++ b/tools/qemu/default.nix @@ -1,7 +1,7 @@ -{ - lib, +{ + lib, pkgs, - rootfsDisk, + diskPath, kernel, syscallTestDir, autotest @@ -11,7 +11,7 @@ let qemuFirmware = pkgs.callPackage ./qemu-firmware.nix {}; baseConfig = { - nographic = true; + nographic = true; memory = "512M"; cores = "2"; shmId = "dragonos-qemu-shm.ram"; @@ -24,7 +24,7 @@ let }; # 3. 参数生成器 (Nix List -> Nix List) - mkQemuArgs = { arch, isNographic }: + mkQemuArgs = { arch, isNographic }: let baseArgs = [ "-m" baseConfig.memory @@ -67,37 +67,43 @@ let let qemuConfig = mkQemuArgs { inherit arch isNographic; }; qemuFlagsStr = lib.escapeShellArgs qemuConfig.flags; - - # FIXED: 这里的逻辑彻底重写,以匹配原脚本的行为 - archSpecificArgs = if arch == "x86_64" then '' - MACHINE_TYPE_FLAGS=( "-machine" "q35,memory-backend=${baseConfig.shmId}" ) - - # 保持之前的 CPU 定义 - CPU_FLAGS=( "-cpu" "IvyBridge,apic,x2apic,+fpu,check,+vmx," ) - - # 保持之前的 KVM 加速定义 + + initProgram = if arch == "riscv64" then "/bin/riscv_rust_init" else "/bin/busybox init"; + + # Define static parts of arguments using Nix lists + commonArchArgs = if arch == "x86_64" then [ + "-machine" "q35,memory-backend=${baseConfig.shmId}" + "-cpu" "IvyBridge,apic,x2apic,+fpu,check,+vmx," + ] else [ + "-cpu" "sifive-u54" + ]; + + kernelPath = if arch == "x86_64" then kernel else "${riscv-uboot}/u-boot.bin"; + + diskArgs = if arch == "x86_64" then [ + "-device" "virtio-blk-pci,drive=disk" + "-device" "pci-bridge,chassis_nr=1,id=pci.1" + "-device" "pcie-root-port" + "-drive" "id=disk,file=${diskPath},if=none" + ] else [ + "-device" "virtio-blk-device,drive=disk" + "-drive" "id=disk,file=${diskPath},if=none" + ]; + + # Generate bash code for dynamic parts + archSpecificBash = if arch == "x86_64" then '' if [ "$ACCEL" == "kvm" ]; then - ACCEL_FLAGS=( "-machine" "accel=kvm" "-enable-kvm" ) + ARCH_FLAGS+=( "-machine" "accel=kvm" "-enable-kvm" ) else - ACCEL_FLAGS=( "-machine" "accel=tcg" ) + ARCH_FLAGS+=( "-machine" "accel=tcg" ) fi - - ARCH_FLAGS=( "''${MACHINE_TYPE_FLAGS[@]}" "''${CPU_FLAGS[@]}" "''${ACCEL_FLAGS[@]}" ) - - BOOT_ARGS=( "-kernel" "${kernel}/kernel.elf" "-append" "$FINAL_CMDLINE" ) - - DISK_ARGS=( "-device" "virtio-blk-pci,drive=disk" "-device" "pci-bridge,chassis_nr=1,id=pci.1" "-device" "pcie-root-port" "-drive" "id=disk,file=${rootfsDisk},if=none" ) '' else '' - ARCH_FLAGS=( "-cpu" "sifive-u54" "-machine" "virt,accel=$ACCEL,memory-backend=${baseConfig.shmId}" ) - BOOT_ARGS=( "-kernel" "${riscv-uboot}/u-boot.bin" "-append" "$FINAL_CMDLINE" ) - DISK_ARGS=( "-device" "virtio-blk-device,drive=disk" "-drive" "id=disk,file=${rootfsDisk},if=none" ) + ARCH_FLAGS+=( "-machine" "virt,accel=$ACCEL,memory-backend=${baseConfig.shmId}" ) ''; - initProgram = if arch == "riscv64" then "/bin/riscv_rust_init" else "/bin/busybox init"; - in pkgs.writeScriptBin name '' #!${pkgs.runtimeShell} - + if [ ! -d "bin" ]; then echo "Error: Please run from project root (bin/ missing)."; exit 1; fi ACCEL="tcg" @@ -110,11 +116,16 @@ let # 原脚本是 rm -rf ... -> qemu -> rm -rf ... EXTRA_CMDLINE="${qemuConfig.cmdlineExtra}" - + # FIXED: 补全缺失的默认内核参数 AUTO_TEST 和 SYSCALL_TEST_DIR FINAL_CMDLINE="init=${initProgram} AUTO_TEST=${autotest} SYSCALL_TEST_DIR=${syscallTestDir} $EXTRA_CMDLINE" - ${archSpecificArgs} + ARCH_FLAGS=( ${lib.escapeShellArgs commonArchArgs} ) + ${archSpecificBash} + + BOOT_ARGS=( "-kernel" "${kernelPath}" "-append" "$FINAL_CMDLINE" ) + + DISK_ARGS=( ${lib.escapeShellArgs diskArgs} ) echo -e "================== DragonOS QEMU Command Preview ==================" echo -e "Binary: sudo ${qemuBin}" @@ -130,19 +141,10 @@ let sudo ${qemuBin} ${qemuFlagsStr} -L ${qemuFirmware} "''${ARCH_FLAGS[@]}" "''${BOOT_ARGS[@]}" "''${DISK_ARGS[@]}" "$@" ''; - script = { - x86_64 = mkRunScript { - name = "run-dragonos-x86"; - arch = "x86_64"; - isNographic = baseConfig.nographic; - qemuBin = "${pkgs.qemu_kvm}/bin/qemu-system-x86_64"; - }; - - riscv64 = mkRunScript { - name = "run-dragonos-riscv"; - arch = "riscv64"; - isNographic = true; - qemuBin = "${pkgs.qemu_kvm}/bin/qemu-system-riscv64"; - }; - }; -in script \ No newline at end of file + script = lib.genAttrs [ "x86_64" "riscv64" ] (arch: mkRunScript { + name = "run-dragonos"; + inherit arch; + isNographic = if arch == "riscv64" then true else baseConfig.nographic; + qemuBin = "${pkgs.qemu_kvm}/bin/qemu-system-${arch}"; + }); +in script diff --git a/user/apps/default.nix b/user/apps/default.nix index 0318b72d1..bcee75220 100644 --- a/user/apps/default.nix +++ b/user/apps/default.nix @@ -1,23 +1,51 @@ -{ pkgs, system, fenix, syscallTestDir }: +{ + lib, + pkgs, + nixpkgs, + system, + target, + fenix, + syscallTestDir +}: # Return a list of app derivations to be copied into the rootfs. let - static = pkgs.pkgsStatic; + cross = if system == "x86_64-linux" && target == "x86_64" then pkgs + else if target == "riscv64" then pkgs.pkgsCross.riscv64 + else import nixpkgs { + localSystem = system; + crossSystem = if target == "x86_64" then "x86_64-unknown-linux-gnu" + else abort "Unsupported target: ${target}}"; + }; + + cross-musl = if system == "x86_64-linux" && target == "x86_64" then pkgs.pkgsMusl + else if target == "riscv64" then pkgs.pkgsCross.riscv64-musl + else import nixpkgs { + localSystem = system; + crossSystem = if target == "x86_64" then "x86_64-unknown-linux-musl" + else abort "Unsupported target: ${target}-musl}"; + }; + + static = if system == "x86_64-linux" && target == "x86_64" then pkgs.pkgsStatic + else if target == "riscv64" then import nixpkgs { + crossSystem = lib.systems.examples.riscv64-musl; + isStatic = true; + } else abort "Unsupported static target: ${target}"; in [ static.busybox static.curl static.dropbear - pkgs.glibc - # pkgs.gccNGPackages_15.libstdcxx - # pkgs.libcxx - + cross.glibc + # Simple C utility (static.callPackage ./about {}) - # gVisor syscall tests runner + assets +] +++ lib.optionals (target == "x86_64") [ + # gvisor test case only included on x86_64 (pkgs.callPackage ./tests/syscall/gvisor { - fenix = fenix; - system = system; + inherit fenix system; installDir = syscallTestDir; }) + # TODO: Add debian libcxx deps or FHS ] diff --git a/user/default.nix b/user/default.nix index 172056fbd..4f3d3a013 100644 --- a/user/default.nix +++ b/user/default.nix @@ -1,17 +1,19 @@ { + lib, pkgs, + nixpkgs, system, - fenix, target, + fenix, buildDir, syscallTestDir, rootfsType ? "vfat", + diskPath, partitionType ? "mbr" }: let - image = import ./rootfs-tar.nix { inherit pkgs system fenix syscallTestDir; }; - diskName = "disk-image-${target}.img"; + image = import ./rootfs-tar.nix { inherit lib pkgs nixpkgs system target fenix syscallTestDir; }; # 构建脚本 - 在bin/目录下构建 buildScript = pkgs.writeShellApplication { @@ -24,7 +26,6 @@ let mkdir -p "${buildDir}" OUTPUT_TAR="${buildDir}/rootfs.tar" - DISK_IMAGE="${buildDir}/${diskName}" echo "==> Generating rootfs" @@ -79,14 +80,14 @@ let FINAL_TAR="$FILTERED_TAR" fi - echo "==> Building disk image at $DISK_IMAGE" + echo "==> Building disk image at ${diskPath}" export LIBGUESTFS_CACHEDIR=/tmp export LIBGUESTFS_BACKEND=direct # 创建磁盘镜像并初始化文件系统 echo " Creating disk image..." - TEMP_IMG="$DISK_IMAGE.tmp" + TEMP_IMG="${diskPath}.tmp" # 计算所需磁盘大小:tar包大小 + 1G 缓冲空间 TAR_SIZE_KB=$(du -k "$FINAL_TAR" | cut -f1) @@ -108,14 +109,14 @@ let shutdown EOF - mv "$TEMP_IMG" "$DISK_IMAGE" + mv "$TEMP_IMG" "${diskPath}" - IMG_SIZE=$(du -h "$DISK_IMAGE" | cut -f1) + IMG_SIZE=$(du -h "${diskPath}" | cut -f1) echo " ✓ disk image created ($IMG_SIZE)" echo "==> Build complete!" echo " Rootfs tar: $OUTPUT_TAR" - echo " Disk image: $DISK_IMAGE" + echo " Disk image: ${diskPath}" ''; }; diff --git a/user/rootfs-tar.nix b/user/rootfs-tar.nix index 2adf0aedc..a33eee5c0 100644 --- a/user/rootfs-tar.nix +++ b/user/rootfs-tar.nix @@ -1,8 +1,9 @@ -{ pkgs, system, fenix, syscallTestDir }: +{ lib, pkgs, nixpkgs, system, target, fenix, syscallTestDir }: # 产物是一个可以生成 rootfs.tar 的脚本 let - apps = import ./apps { inherit pkgs system fenix syscallTestDir; }; + apps = import ./apps { inherit lib pkgs nixpkgs system target fenix syscallTestDir; }; + sys-config = pkgs.runCommand "sysconfig" { src = ./sysconfig; } '' @@ -20,4 +21,4 @@ let keepContentsDirlinks = false; }; -in dockerImage \ No newline at end of file +in dockerImage From 29172ccbc375c0499f84ca3371639472f0057f06 Mon Sep 17 00:00:00 2001 From: Samuka007 <24754678@student.uwa.edu.au> Date: Sat, 20 Dec 2025 17:44:35 +0800 Subject: [PATCH 13/16] Add Nix-based docs flake and documentation Add a reproducible Sphinx flake (docs/flake.nix + flake.lock) and a set of documentation pages (nix dev guide, devcontainer, userland/rootfs, dev-skills, write-docs). Enable myst linkify and update docs requirements. Add per-app flakes and locks (user/apps/about, user/apps/tests/syscall/gvisor) and integrate gvisor tests into user/apps default.nix. --- docs/.gitignore | 2 + docs/conf.py | 1 + docs/dev-skills/nix-skills.md | 45 ++++++++ docs/dev-skills/write-docs.md | 32 ++++++ docs/flake.lock | 61 +++++++++++ docs/flake.nix | 127 ++++++++++++++++++++++ docs/index.rst | 14 ++- docs/introduction/devcontainer.md | 31 ++++++ docs/introduction/develop_nix.md | 77 +++++++++++++ docs/requirements.txt | 1 + docs/userland/appdev/index.rst | 2 +- docs/userland/rootfs/add-your-own-app.md | 109 +++++++++++++++++++ docs/userland/rootfs/diskgen.md | 34 ++++++ docs/userland/rootfs/index.rst | 9 ++ user/apps/about/flake.lock | 61 +++++++++++ user/apps/about/flake.nix | 18 +++ user/apps/default.nix | 10 +- user/apps/tests/syscall/gvisor/flake.lock | 99 +++++++++++++++++ user/apps/tests/syscall/gvisor/flake.nix | 23 ++++ 19 files changed, 749 insertions(+), 7 deletions(-) create mode 100644 docs/.gitignore create mode 100644 docs/dev-skills/nix-skills.md create mode 100644 docs/dev-skills/write-docs.md create mode 100644 docs/flake.lock create mode 100644 docs/flake.nix create mode 100644 docs/introduction/devcontainer.md create mode 100644 docs/introduction/develop_nix.md create mode 100644 docs/userland/rootfs/add-your-own-app.md create mode 100644 docs/userland/rootfs/diskgen.md create mode 100644 docs/userland/rootfs/index.rst create mode 100644 user/apps/about/flake.lock create mode 100644 user/apps/about/flake.nix create mode 100644 user/apps/tests/syscall/gvisor/flake.lock create mode 100644 user/apps/tests/syscall/gvisor/flake.nix diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..8dfcc59dd --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +_build/ +result diff --git a/docs/conf.py b/docs/conf.py index e6bcc1c6f..c1da2dbc9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -88,6 +88,7 @@ "strikethrough", "substitution", "tasklist", + "linkify" ] # sphinx-multiversion 指定哪个分支为 lastest 版本 diff --git a/docs/dev-skills/nix-skills.md b/docs/dev-skills/nix-skills.md new file mode 100644 index 000000000..dc76a4666 --- /dev/null +++ b/docs/dev-skills/nix-skills.md @@ -0,0 +1,45 @@ +# Nix 技巧 + +## 查看可用构建产物 + +```shell +❯ nix flake show +warning: Git tree '/workspace' is dirty +git+file:///workspace +├───apps +│ └───x86_64-linux +│ ├───rootfs-riscv64: app: 构建 riscv64 rootfs 镜像 +│ ├───rootfs-x86_64: app: 构建 x86_64 rootfs 镜像 +│ ├───start-riscv64: app: 以 riscv64 启动DragonOS +│ └───start-x86_64: app: 以 x86_64 启动DragonOS +└───packages + └───x86_64-linux + ├───rootfs-riscv64: package 'build-rootfs-image' + ├───rootfs-x86_64: package 'build-rootfs-image' + ├───start-riscv64: package 'run-dragonos' + └───start-x86_64: package 'run-dragonos' + +user/apps/about ❯ nix flake show +git+file:///workspace?dir=user/apps/about +└───packages + ├───aarch64-darwin + │ └───default omitted (use '--all-systems' to show) + ├───aarch64-linux + │ └───default omitted (use '--all-systems' to show) + ├───x86_64-darwin + │ └───default omitted (use '--all-systems' to show) + └───x86_64-linux + └───default: package 'about-static-x86_64-unknown-linux-musl-0.1.0' + +tests/syscall/gvisor ❯ nix flake show +git+file:///workspace?dir=user/apps/tests/syscall/gvisor +└───packages + └───x86_64-linux + └───default: package 'gvisor-tests' +``` + +## 回收 nix 构建历史缓存 + +```shell +❯ nix store gc +``` diff --git a/docs/dev-skills/write-docs.md b/docs/dev-skills/write-docs.md new file mode 100644 index 000000000..7f8fa1ccf --- /dev/null +++ b/docs/dev-skills/write-docs.md @@ -0,0 +1,32 @@ +# 贡献文档 + +## 使用 nix + +### 实时构建预览文档 + +sphinx-autobuild 作为默认 nix run 目标,写文档只需要 + +```shell +cd docs +nix run +``` + +然后访问 8000 端口即可。 + +### 构建文档为 drv (sphinx-build) + +```shell +cd docs +nix build +``` + +### 预览构建好的文档 (python http-server) + +```shell +cd docs +nix run .#release +``` + +## 使用 pip Makefile 构建文档 + +TODO diff --git a/docs/flake.lock b/docs/flake.lock new file mode 100644 index 000000000..100021151 --- /dev/null +++ b/docs/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1765838191, + "narHash": "sha256-m5KWt1nOm76ILk/JSCxBM4MfK3rYY7Wq9/TZIIeGnT8=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "c6f52ebd45e5925c188d1a20119978aa4ffd5ef6", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-25.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/docs/flake.nix b/docs/flake.nix new file mode 100644 index 000000000..7976983c7 --- /dev/null +++ b/docs/flake.nix @@ -0,0 +1,127 @@ +{ + description = "Reproducible Sphinx Documentation Build"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = nixpkgs.legacyPackages.${system}; + lib = pkgs.lib; + + # ---------------------------------------------------------------- + # 1. 定义精确的 Python 环境 + # ---------------------------------------------------------------- + # 这里显式列出依赖,确保无论在哪台机器,Sphinx 版本一致 + pythonEnv = pkgs.python3.withPackages (ps: with ps; [ + sphinx + sphinx-multiversion + sphinx-autobuild + # 在这里添加你的 theme 或 extension,例如: + sphinx-rtd-theme + myst-parser + sphinxcontrib-mermaid + linkify-it-py + ]); + + # ---------------------------------------------------------------- + # 2. 从 Flake 输入中提取 Git 信息 (Pure 方式) + # ---------------------------------------------------------------- + # Nix 在求值时知道当前的 revision,不需要在 build 时运行 git 命令 + gitHash = if (self ? rev) then self.rev else "dirty-dev"; + isDirty = if (self ? rev) then "0" else "1"; + + buildDir = "./_build"; + port = 8000; + + in + { + # ---------------------------------------------------------------- + # Packages: 纯净构建 (nix build) + # ---------------------------------------------------------------- + # 这种方式构建的是"当前代码的快照",完全不依赖 .git 目录 + packages.default = pkgs.stdenv.mkDerivation { + name = "sphinx-docs"; + src = ./.; + + buildInputs = [ pythonEnv ]; + + # 将 Nix 提取的 Git 信息注入环境变量 + # 这完全替代了 Makefile 中 $(shell git rev-parse) 的逻辑 + env = { + LANGUAGE = "zh_CN"; + CURRENT_GIT_COMMIT_HASH = gitHash; + CURRENT_GIT_COMMIT_DIRTY = isDirty; + SPHINXBUILD = "sphinx-build"; + }; + + # 直接定义构建逻辑,不再依赖 Makefile + buildPhase = '' + echo "Building documentation for commit: $CURRENT_GIT_COMMIT_HASH" + + # 使用 -W 将警告视为错误,确保构建质量 + sphinx-build -M html . ${buildDir} \ + -D language=$LANGUAGE \ + -w _build/warnings.log + ''; + + installPhase = '' + mkdir -p $out + cp -r ${buildDir}/* $out/ + ''; + }; + + # ---------------------------------------------------------------- + # Apps: 快速运行工具 (nix run) + # ---------------------------------------------------------------- + # 提供一个脚本来预览构建结果 + apps.release = flake-utils.lib.mkApp { + drv = let targetDir = self.packages.${system}.default; + in pkgs.writeShellApplication { + name = "preview-docs"; + runtimeInputs = [ pythonEnv ]; + text = '' + echo "Open at http://localhost:${toString port}/index.html" + python3 -m http.server --directory "${targetDir}/html" ${toString port} + ''; + }; + }; + app.develop = flake-utils.lib.mkApp { + drv = pkgs.writeShellApplication { + name = "sphinx-autobuild"; + runtimeInputs = [ pythonEnv ]; + text = '' + sphinx-autobuild . ${buildDir} --host 0.0.0.0 --port ${toString port} + ''; + }; + }; + # 设置默认 run 行为 + apps.default = self.app.${system}.develop; + + # ---------------------------------------------------------------- + # DevShell: 开发环境 (nix develop) + # ---------------------------------------------------------------- + # 用于开发和需要访问 .git 的操作(如 sphinx-multiversion) + devShells.default = pkgs.mkShell { + packages = [ pythonEnv pkgs.git pkgs.gnumake ]; + + shellHook = '' + export LANGUAGE="zh_CN" + export SPHINXBUILD="sphinx-build" + + # 在开发环境中,我们可以动态获取 git 状态 + export CURRENT_GIT_COMMIT_HASH=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown") + + echo "🚀 Sphinx Dev Environment Loaded" + echo "-----------------------------------" + echo "Run 'make html' for standard build" + echo "Run 'make html-multiversion' for versioned build (Requires .git)" + echo "Run 'nix build' for clean production build" + ''; + }; + } + ); +} diff --git a/docs/index.rst b/docs/index.rst index e7ee61b0a..f89e39910 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -25,6 +25,8 @@ introduction/index introduction/build_system + introduction/develop_nix + introduction/devcontainer introduction/mirrors .. toctree:: @@ -55,13 +57,21 @@ .. toctree:: :maxdepth: 1 :caption: 应用层 - + userland/appdev/index + userland/rootfs/index + +.. toctree:: + :maxdepth: 1 + :caption: 开发指南 + + dev-skills/nix-skills + dev-skills/write-docs .. toctree:: :maxdepth: 1 :caption: Q&A - + questions/index diff --git a/docs/introduction/devcontainer.md b/docs/introduction/devcontainer.md new file mode 100644 index 000000000..0e743ae43 --- /dev/null +++ b/docs/introduction/devcontainer.md @@ -0,0 +1,31 @@ +# 使用 devcontainer 开发 DragonOS + +本教程以 VSCode 为例,需要装有 Docker 的 Linux。 + +## 克隆仓库 + +```shell +git clone https://github.com/DragonOS-Community/DragonOS.git +code DragonOS +``` + +## 进入 devcontainer 环境 + +在 VSCode 右下角会有弹窗,选择 `Reopen in Container`。如果不可见,请根据下列步骤来进入: +- 下载 devcontainer 插件 +- `ctrl+shift+p` 打开 VSCode 命令面板 +- 输入 `devcontainer` 字样,会有 `Reopen in Container` 的选项,点击即会构建 devcontainer 环境 + +构建可能需要一些时间,尤其 msr 的插件在网络环境不好的情况下容易安装失败。 + +## 构建 DragonOS! + +直接输入 + +```shell +make run-nographic +``` + +等待构建,最后会自动进入 DragonOS qemu 环境。 + +需要退出qemu环境,请输入 `ctrl+a` 然后按 `x`。 diff --git a/docs/introduction/develop_nix.md b/docs/introduction/develop_nix.md new file mode 100644 index 000000000..4f4d04b58 --- /dev/null +++ b/docs/introduction/develop_nix.md @@ -0,0 +1,77 @@ +# 使用 nix 开发 DragonOS + +nix 的引入使得 DragonOS 的开发环境不再依赖手动维护的 `bootstrap.sh` 。现在任意发行版都可通过安装 nix 环境快速构建运行 DragonOS! + +## 安装 nix 并启用 flake 功能 + +参考 https://nixos.org/download/ 安装 Nix: The Nix package manager. (不是 NixOS !) + +参考 https://wiki.nixos.org/wiki/Flakes#Setup 启用 flakes 功能。 + +- 如果你想体验 nix 带来的声明式管理,又不想更改发行版,尝试 home-manager 并在其上配置启用 flakes、direnv +- 否则可以直接以 nix standalone 的方式安装 flakes,或者每次输入命令时添加 `--experimental-features 'nix-command flakes'` + +## 克隆仓库 + +DragonOS 现在在多个托管平台上都有仓库镜像 +- `https://github.com/DragonOS-Community/DragonOS.git` +- `https://atomgit.com/DragonOS-Community/DragonOS.git` +- `https://cnb.cool/DragonOS-Community/DragonOS.git` + +```shell +git clone https://atomgit.com/DragonOS-Community/DragonOS.git +cd DragonOS +``` + +## 激活内核编译环境 + +```shell +nix develop ./tools/nix-dev-shell +``` + +如果你配置了 `direnv`,首次进入仓库目录会提示需要执行 `direnv allow`,相当于自动进入了 `nix develop` 环境。 + +## 编译内核 + +做一些 dirty 的修复(TODO: 兼容性改进 or 不再使用 Makefile 构建) + +```shell +grep -rlZ '+nightly-2025-08-10' ./build-scripts | xargs -0 sed -i 's/+nightly-2025-08-10//g' +grep -rlZ '+nightly-2025-08-10' ./kernel | xargs -0 sed -i 's/+nightly-2025-08-10//g' +sed -i 's/CCPREFIX=x86_64-linux-gnu-/CCPREFIX=/g' kernel/env.mk +``` + +执行编译 + +```shell +make kernel +``` + +默认状态下,这会将内核 elf 编译到 `./bin/kernel/kernel.elf` + +## 构建 rootfs + +```shell +nix run .#rootfs.x86_64 +``` + +这会生成 `./bin/qemu-system-x86_64.img` + +## 启动内核 + +```shell +nix run .#start.x86_64 +``` + +现在你能看到你的终端载入 DragonOS 了 + +:::{note} +需要退出 DragonOS (QEMU)环境,请输入 `ctrl + a`,然后 `x` +::: + +## 更多 nix 命令用法及 nix script 维护 + +- `cd docs && nix run` 构建文档并启动一个 http 服务器 +- 如果存储空间告急,`nix store gc` 清理悬空的历史构建副本 +- 项目根目录下 `nix flake show` 查看可供构建的目标 +- 更多 diff --git a/docs/requirements.txt b/docs/requirements.txt index 16ab91616..018e06029 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -5,3 +5,4 @@ sphinxcontrib-mermaid==1.0.0 git+https://git.mirrors.dragonos.org.cn/DragonOS-Community/sphinx-multiversion.git@5858b75#egg=sphinx-multiversion openai~=1.79 tqdm~=4.65.0 +linkify-it-py diff --git a/docs/userland/appdev/index.rst b/docs/userland/appdev/index.rst index ccf82118c..4ba661a37 100644 --- a/docs/userland/appdev/index.rst +++ b/docs/userland/appdev/index.rst @@ -1,4 +1,4 @@ -应用程序开发文档 +用户态构建文档(DADK) =================================================== diff --git a/docs/userland/rootfs/add-your-own-app.md b/docs/userland/rootfs/add-your-own-app.md new file mode 100644 index 000000000..44c85de22 --- /dev/null +++ b/docs/userland/rootfs/add-your-own-app.md @@ -0,0 +1,109 @@ +# 添加程序/添加自定义程序! + +由于新的 userland 构建采用了 nix 来管理,添加程序变得非常的简单!下面我们自顶向下地讲解如何添加程序进 DragonOS 里跑 + +## 概念 + +nix 中,一个软件包是一个 derivation,所以只需要用 nix 将你的程序定义为一个 derivation,那么他就是一个可供“安装”的软件包。 +`nixpkgs` 也提供了许多原生的软件包,甚至有一套语法来帮助我们快速地指定静态编译/交叉编译版本的软件包,而不需要手动指定工具链。 +下面,我们先来看看如何快速添加一个 `nixpkgs` 软件包到 DragonOS 中 + +## 添加一个 nixpkgs 软件包 + +首先我们来看到 `user/apps/default.nix` 中定义引用 `nixpkgs` 软件包的部分 + +```{literalinclude} ../../../user/apps/default.nix +:language: nix +:lines: 34-43 +``` + +这里我们先简单将 `static` 和 `cross` 等理解为提供好的现成的软件包调用前缀(即,你在其他nix教程中见到的引用软件包时的 `pkgs` 前缀),他们会帮助我们处理好依赖、交叉编译和静态编译的麻烦事~ +- `cross` : 使用 GNU 动态链接的软件,需要交叉编译时自动处理 +- `cross-musl` : 使用 musl 动态链接的软件,同样自动处理交叉 +- `static` : 使用 musl 静态编译的软件,自动处理交叉 + +显然,在这里,我们注入的都是静态链接的软件包,如 `busybox` 与 `dropbear`。更多的软件包,可以通过 `nix search github:NixOS/nixpkgs/nixos-25.11 ` 或者 https://search.nixos.org/packages?channel=25.11 快速检索。 + +```shell +~ ❯ nix search github:NixOS/nixpkgs/nixos-25.11 dropbear +evaluation warning: darwin.iproute2mac has been renamed to iproute2mac +* legacyPackages.x86_64-linux.dropbear (2025.88) + Small footprint implementation of the SSH 2 protocol +evaluation warning: 'dockerfile-language-server-nodejs' has been renamed to 'dockerfile-language-server' +evaluation warning: beets-stable was aliased to beets, since upstream releases are frequent nowadays +evaluation warning: beets-unstable was aliased to beets, since upstream releases are frequent nowadays +evaluation warning: 'f3d' now build with egl support by default, so `f3d_egl` is deprecated, consider using 'f3d' instead. +evaluation warning: beets-stable was aliased to beets, since upstream releases are frequent nowadays +evaluation warning: beets-unstable was aliased to beets, since upstream releases are frequent nowadays +evaluation warning: 'f3d' now build with egl support by default, so `f3d_egl` is deprecated, consider using 'f3d' instead. +evaluation warning: 'hsa-amd-aqlprofile-bin' has been replaced by 'aqlprofile'. +evaluation warning: 'system' has been renamed to/replaced by 'stdenv.hostPlatform.system' +evaluation warning: 'ethersync' has been renamed to 'teamtype' +evaluation warning: Please replace 'pure-lua' with 'moonlight-nvim' as this name was an error +evaluation warning: windows.mingw_w64_pthreads is deprecated, windows.pthreads should be preferred + +~ took 28s ❯ +``` + +此处检索出来的是 `legacyPackages.x86_64-linux.dropbear`,说明至少这个软件包存在于 x86_64 上,直接 `cross.dropbear` 引用的就是这个软件包。而使用 `static.dropbear` 则会因为没有远程构建缓存而重新构建(但仍然省去自己手动配置的麻烦) + +## 添加一个自定义软件包 + +### C/C++ +上面还可以看到有 `(static.callPackage ./about {})` ,这个 about 软件包即是自定义构建的。我们来看看如何用 nix 取代了它的 Makefile: + +```{literalinclude} ../../../user/apps/about/default.nix +:language: nix +``` + +你还可以用 nix 工具来迁移目前 NixOS 上没有收录的软件包进来!(当然,这很少见,尤其是非 GUI 的软件) + +更多可以参考: +- https://book.divnix.com/ch06-01-simple-c-program.html +- https://ryantm.github.io/nixpkgs/stdenv/stdenv/ +- https://wiki.nixos.org/wiki/C + +### Rust +对于简单的 Rust 程序,直接使用 nix 提供的 rustPlatform.buildRustPackage 构建即可,参考 `user/apps/tests/syscall/gvisor/default.nix` + +```nix +{ lib, pkgs, fenix, system, installDir }: + +let + fenixPkgs = fenix.packages.${system}; + toolchain = fenixPkgs.combine (with fenixPkgs; [ + minimal.rustc + minimal.cargo + ]); + rustPlatform = pkgs.makeRustPlatform { + cargo = toolchain; + rustc = toolchain; + }; + + runner = rustPlatform.buildRustPackage { + pname = "gvisor-test-runner-bin"; + version = "0.1.0"; + + src = ./runner; + cargoLock = { + lockFile = ./runner/Cargo.lock; + }; + + # 你可以在这里选择不把binary装在bin目录下 + postInstall = '' + mkdir -p $out/${installDir} + if [ -f "$out/bin/runner" ]; then + mv "$out/bin/runner" "$out/${installDir}/gvisor-test-runner" + # Clean up empty bin directory if it exists, to avoid clutter in symlinkJoin + rmdir "$out/bin" || true + fi + ''; + }; + + ... +``` + +对于复杂的应用程序,以及交叉编译,可以参考 fenix 的几个例子: +- https://github.com/nix-community/fenix#examples + +TODO: Multiplatform Rust Application diff --git a/docs/userland/rootfs/diskgen.md b/docs/userland/rootfs/diskgen.md new file mode 100644 index 000000000..7a9ec77c9 --- /dev/null +++ b/docs/userland/rootfs/diskgen.md @@ -0,0 +1,34 @@ +# 从软件包到RootFS镜像 + +定义完软件包后,无需做更多修改,直接 `nix run .#rootfs-${target}` 即可构建 RootFS。 + +## 生成 Docker 镜像 + +我们利用 `pkgs.dockerTools.buildImage` 的展平和拷贝属性,将 `user/apps` 下的软件包,以及 `user/sysconfig` +中的配置文件复制到单层 overlayfs 中(Docker原理),`buildImage` 的终产物是一个可以被 `docker import` 的一个 +`tar.gz`。见 `rootfs-tar.nix` + +```{literalinclude} ../../../user/rootfs-tar.nix +:language: nix +``` + +## 解压 Docker tar 并生成文件系统镜像 + +原理:解压 docker 镜像,拿出第一层中的 `layer.tar`,然后使用 `guestfish` 导入到虚拟镜像文件中。见 `user/default.nix` + +```{literalinclude} ../../../user/default.nix +:language: shell +:lines: 23-112 +``` + +## nix script 的背后 + +生成 Docker 镜像时,Docker Image 是作为二进制产物缓存在 /nix/store 中的,因此多次构建可能会快速占用硬盘。 + +hint: 要想释放磁盘空间,执行 `nix store gc` + +但第二步:解压 Docker tar 到文件系统磁盘镜像文件,是以生成一个 shell script 并运行来实现的,生成的 `disk-image-${target}.img` 只会存在一个在 `bin` 目录下;同时从 `.tar.gz` 中解压出来的 `rootfs.tar` 也会存在于 `bin` 目录下,你可以自己解压看看里面都存了些啥。 + +TODO: 后续脚本允许分两步进行,中间允许注入自定义文件(除了直接在 sysconfig 中注入外) + +如果你想看看这个脚本长什么样,你可以在项目根目录执行 `nix build .#rootfs-${target} -o bin/result` 。 nix 会把 script 对应的 derivation 链接到 result 目录下,只需 `cat bin/result/bin/build-rootfs-image` 就能看到全部命令。 diff --git a/docs/userland/rootfs/index.rst b/docs/userland/rootfs/index.rst new file mode 100644 index 000000000..e8075f6a5 --- /dev/null +++ b/docs/userland/rootfs/index.rst @@ -0,0 +1,9 @@ +用户态构建文档(Nix) +=================================================== + +.. toctree:: + :maxdepth: 1 + :caption: 目录 + + diskgen + add-your-own-app diff --git a/user/apps/about/flake.lock b/user/apps/about/flake.lock new file mode 100644 index 000000000..6eab61193 --- /dev/null +++ b/user/apps/about/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1765838191, + "narHash": "sha256-m5KWt1nOm76ILk/JSCxBM4MfK3rYY7Wq9/TZIIeGnT8=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "c6f52ebd45e5925c188d1a20119978aa4ffd5ef6", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-25.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/user/apps/about/flake.nix b/user/apps/about/flake.nix new file mode 100644 index 000000000..081704eb8 --- /dev/null +++ b/user/apps/about/flake.nix @@ -0,0 +1,18 @@ +{ + description = "About utility for DragonOS"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { self, nixpkgs, flake-utils }: + flake-utils.lib.eachDefaultSystem (system: + let + pkgs = import nixpkgs { inherit system; }; + in + { + packages.default = pkgs.pkgsStatic.callPackage ./default.nix { }; + } + ); +} diff --git a/user/apps/default.nix b/user/apps/default.nix index bcee75220..1ed23f5a9 100644 --- a/user/apps/default.nix +++ b/user/apps/default.nix @@ -31,6 +31,11 @@ let crossSystem = lib.systems.examples.riscv64-musl; isStatic = true; } else abort "Unsupported static target: ${target}"; + + gvisor-syscall-tests = (pkgs.callPackage ./tests/syscall/gvisor { + inherit fenix system; + installDir = syscallTestDir; + }); in [ static.busybox static.curl @@ -43,9 +48,6 @@ in [ ] ++ lib.optionals (target == "x86_64") [ # gvisor test case only included on x86_64 - (pkgs.callPackage ./tests/syscall/gvisor { - inherit fenix system; - installDir = syscallTestDir; - }) + gvisor-syscall-tests # TODO: Add debian libcxx deps or FHS ] diff --git a/user/apps/tests/syscall/gvisor/flake.lock b/user/apps/tests/syscall/gvisor/flake.lock new file mode 100644 index 000000000..f9684fdfa --- /dev/null +++ b/user/apps/tests/syscall/gvisor/flake.lock @@ -0,0 +1,99 @@ +{ + "nodes": { + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1765435813, + "narHash": "sha256-C6tT7K1Lx6VsYw1BY5S3OavtapUvEnDQtmQB5DSgbCc=", + "owner": "nix-community", + "repo": "fenix", + "rev": "6399553b7a300c77e7f07342904eb696a5b6bf9d", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "id": "flake-utils", + "type": "indirect" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1765838191, + "narHash": "sha256-m5KWt1nOm76ILk/JSCxBM4MfK3rYY7Wq9/TZIIeGnT8=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "c6f52ebd45e5925c188d1a20119978aa4ffd5ef6", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-25.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "fenix": "fenix", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1765400135, + "narHash": "sha256-D3+4hfNwUhG0fdCpDhOASLwEQ1jKuHi4mV72up4kLQM=", + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "fface27171988b3d605ef45cf986c25533116f7e", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/user/apps/tests/syscall/gvisor/flake.nix b/user/apps/tests/syscall/gvisor/flake.nix new file mode 100644 index 000000000..fca05fa23 --- /dev/null +++ b/user/apps/tests/syscall/gvisor/flake.nix @@ -0,0 +1,23 @@ +{ + description = "gVisor syscall test runner and scripts"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-25.11"; + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { self, nixpkgs, fenix, flake-utils }: + let + pkgs = import nixpkgs { inherit system; }; + installDir = "share/gvisor-tests"; + system = "x86_64-linux"; + in + { + packages.${system}.default = pkgs.callPackage ./default.nix { + inherit fenix system installDir; + }; + }; +} From 6cdbb820c7353e0de745e8fbcc370e2bb9900c06 Mon Sep 17 00:00:00 2001 From: Samuka007 <24754678@student.uwa.edu.au> Date: Sat, 20 Dec 2025 19:46:31 +0800 Subject: [PATCH 14/16] Make QEMU GDB stub optional and disable autotest Add a debug argument (debug ? false) to tools/qemu and only pass "-s" "-S" when debug is enabled. Update flake to set debug = true and autotest = "none" --- flake.nix | 3 ++- tools/qemu/default.nix | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/flake.nix b/flake.nix index 1b83b825c..03669297a 100644 --- a/flake.nix +++ b/flake.nix @@ -33,7 +33,8 @@ # 内核位置 kernel = "${buildDir}/kernel/kernel.elf"; # TODO: make it a drv 用nix构建内核,避免指定相对目录 # 自动测试项目,指定内核启动环境变量参数 AUTO_TEST - autotest = "syscall"; + autotest = "none"; + debug = true; }; startPkg = qemuScripts.${target}; diff --git a/tools/qemu/default.nix b/tools/qemu/default.nix index e4e07baf6..035d5cf4a 100644 --- a/tools/qemu/default.nix +++ b/tools/qemu/default.nix @@ -4,7 +4,8 @@ diskPath, kernel, syscallTestDir, - autotest + autotest, + debug ? false }: let @@ -38,13 +39,14 @@ let # Boot Order "-boot" "order=d" - # GDB Stub - "-s" "-rtc" "clock=host,base=localtime" # Trace events "-d" "cpu_reset,guest_errors,trace:virtio*,trace:e1000e_rx*,trace:e1000e_tx*,trace:e1000e_irq*" "-trace" "fw_cfg*" + ] ++ lib.optionals debug [ + # GDB Stub + "-s" "-S" ]; nographicArgs = lib.optionals isNographic ([ "--nographic" From 4cb602b1de2c1dad15f9da8d1f07f4e961802892 Mon Sep 17 00:00:00 2001 From: Samuka007 <24754678@student.uwa.edu.au> Date: Sun, 21 Dec 2025 12:02:45 +0800 Subject: [PATCH 15/16] Refactor syscall test configuration, update gvisor syscall test version Introduce a `testOpt` overlay to consolidate various test-related options, including syscall test configuration and autotest settings. This change centralizes these settings, making them easier to manage and pass down through the Nix expression. --- flake.nix | 22 +++++++++++++++------- tools/qemu/default.nix | 5 ++--- user/apps/default.nix | 7 ++++--- user/apps/tests/syscall/gvisor/default.nix | 6 +++--- user/default.nix | 4 ++-- user/rootfs-tar.nix | 4 ++-- 6 files changed, 28 insertions(+), 20 deletions(-) diff --git a/flake.nix b/flake.nix index 03669297a..c9bc66252 100644 --- a/flake.nix +++ b/flake.nix @@ -20,26 +20,34 @@ fenix = inputs.fenix; pkgs = nixpkgs.legacyPackages.${system}; lib = pkgs.lib; - syscallTestDir = "/usr/share/gvisor"; rootfsType = "ext4"; buildDir = "./bin"; # Specifying temp file location + testOpt = { + # 自动测试项目,指定内核启动环境变量参数 AUTO_TEST + autotest = "none"; + syscall = { + enable = true; + testDir = "/opt/gvisor"; + version = "20251218"; + }; + }; + mkOutputs = target: let diskPath = "${buildDir}/disk-image-${target}.img"; qemuScripts = import ./tools/qemu/default.nix { - inherit lib pkgs syscallTestDir diskPath; - # 启动相关参数: + inherit lib pkgs testOpt diskPath; + # QEMU 相关参数: # 内核位置 kernel = "${buildDir}/kernel/kernel.elf"; # TODO: make it a drv 用nix构建内核,避免指定相对目录 - # 自动测试项目,指定内核启动环境变量参数 AUTO_TEST - autotest = "none"; - debug = true; + # -s -S + debug = false; }; startPkg = qemuScripts.${target}; rootfsPkg = pkgs.callPackage ./user/default.nix { - inherit lib pkgs nixpkgs fenix system target syscallTestDir rootfsType buildDir diskPath; + inherit lib pkgs nixpkgs fenix system target testOpt rootfsType buildDir diskPath; }; in { diff --git a/tools/qemu/default.nix b/tools/qemu/default.nix index 035d5cf4a..44b9e9c39 100644 --- a/tools/qemu/default.nix +++ b/tools/qemu/default.nix @@ -3,8 +3,7 @@ pkgs, diskPath, kernel, - syscallTestDir, - autotest, + testOpt, debug ? false }: @@ -120,7 +119,7 @@ let EXTRA_CMDLINE="${qemuConfig.cmdlineExtra}" # FIXED: 补全缺失的默认内核参数 AUTO_TEST 和 SYSCALL_TEST_DIR - FINAL_CMDLINE="init=${initProgram} AUTO_TEST=${autotest} SYSCALL_TEST_DIR=${syscallTestDir} $EXTRA_CMDLINE" + FINAL_CMDLINE="init=${initProgram} AUTO_TEST=${testOpt.autotest} SYSCALL_TEST_DIR=${testOpt.syscall.testDir} $EXTRA_CMDLINE" ARCH_FLAGS=( ${lib.escapeShellArgs commonArchArgs} ) ${archSpecificBash} diff --git a/user/apps/default.nix b/user/apps/default.nix index 1ed23f5a9..209ac841b 100644 --- a/user/apps/default.nix +++ b/user/apps/default.nix @@ -5,7 +5,7 @@ system, target, fenix, - syscallTestDir + testOpt }: # Return a list of app derivations to be copied into the rootfs. @@ -34,7 +34,8 @@ let gvisor-syscall-tests = (pkgs.callPackage ./tests/syscall/gvisor { inherit fenix system; - installDir = syscallTestDir; + installDir = testOpt.syscall.testDir; + version = testOpt.syscall.version; }); in [ static.busybox @@ -46,7 +47,7 @@ in [ (static.callPackage ./about {}) ] -++ lib.optionals (target == "x86_64") [ +++ lib.optionals (target == "x86_64" && testOpt.syscall.enable) [ # gvisor test case only included on x86_64 gvisor-syscall-tests # TODO: Add debian libcxx deps or FHS diff --git a/user/apps/tests/syscall/gvisor/default.nix b/user/apps/tests/syscall/gvisor/default.nix index 81002d3a9..3adee8777 100644 --- a/user/apps/tests/syscall/gvisor/default.nix +++ b/user/apps/tests/syscall/gvisor/default.nix @@ -1,4 +1,4 @@ -{ lib, pkgs, fenix, system, installDir }: +{ lib, pkgs, fenix, system, installDir, version ? "20251218" }: let fenixPkgs = fenix.packages.${system}; @@ -12,8 +12,8 @@ let }; testsArchive = pkgs.fetchurl { - url = "https://cnb.cool/DragonOS-Community/test-suites/-/releases/download/release_20250626/gvisor-syscalls-tests.tar.xz"; - sha256 = "sha256-GSZ0N3oUOerb0lXU4LZ0z4ybD/xZdy7TtfstEoffcsk="; + url = "https://cnb.cool/DragonOS-Community/test-suites/-/releases/download/release_${version}/gvisor-syscalls-tests.tar.xz"; + sha256 = "sha256-JVCjDtqF9iNw6B4pXGP39gZRs6rEqtLsrroihraPqQE="; }; # 1. Build the Rust runner separately diff --git a/user/default.nix b/user/default.nix index 4f3d3a013..53147adb6 100644 --- a/user/default.nix +++ b/user/default.nix @@ -6,14 +6,14 @@ target, fenix, buildDir, - syscallTestDir, + testOpt, rootfsType ? "vfat", diskPath, partitionType ? "mbr" }: let - image = import ./rootfs-tar.nix { inherit lib pkgs nixpkgs system target fenix syscallTestDir; }; + image = import ./rootfs-tar.nix { inherit lib pkgs nixpkgs system target fenix testOpt; }; # 构建脚本 - 在bin/目录下构建 buildScript = pkgs.writeShellApplication { diff --git a/user/rootfs-tar.nix b/user/rootfs-tar.nix index a33eee5c0..5e4911127 100644 --- a/user/rootfs-tar.nix +++ b/user/rootfs-tar.nix @@ -1,8 +1,8 @@ -{ lib, pkgs, nixpkgs, system, target, fenix, syscallTestDir }: +{ lib, pkgs, nixpkgs, system, target, fenix, testOpt }: # 产物是一个可以生成 rootfs.tar 的脚本 let - apps = import ./apps { inherit lib pkgs nixpkgs system target fenix syscallTestDir; }; + apps = import ./apps { inherit lib pkgs nixpkgs system target fenix testOpt; }; sys-config = pkgs.runCommand "sysconfig" { src = ./sysconfig; From f4b6d088fb3ffd17b01d3e6dc536caee05ef65cd Mon Sep 17 00:00:00 2001 From: Samuka007 <24754678@student.uwa.edu.au> Date: Sun, 21 Dec 2025 12:24:36 +0800 Subject: [PATCH 16/16] Refactor: Use correct executable names in flake.nix Update `flake.nix` to use the correct executable names for `dragonos-run` and `dragonos-rootfs`. This commit also adds a section to `nix-skills.md` to clarify how to avoid redundant `nix run` commands in the same terminal. --- docs/dev-skills/nix-skills.md | 16 ++++++++++++++++ flake.nix | 4 ++-- tools/qemu/default.nix | 2 +- user/default.nix | 2 +- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/docs/dev-skills/nix-skills.md b/docs/dev-skills/nix-skills.md index dc76a4666..e04dbac24 100644 --- a/docs/dev-skills/nix-skills.md +++ b/docs/dev-skills/nix-skills.md @@ -1,5 +1,21 @@ # Nix 技巧 +## 同一终端内避免反复指定 `nix run .#start-x86_64` + +需要注意的是,仅推荐重复使用 qemu 启动脚本 `dragonos-run`,因为 rootfs 在 `nix shell` eval 完毕后生成的仅仅是最终 `rootfs.tar` 拷贝到磁盘文件的过程,而 `rootfs.tar` 需要重新调用 nix build/shell/run 命令来生成。你的所有对 nix 的修改都需要通过重新执行 nix 命令来进行 eval 与构建,因此 `nix shell .#rootfs-x86_64` 意义不大。 + +```shell +❯ nix shell .#start-x86_64 .#rootfs-x86_64 +❯ which dragonos-rootfs +/nix/store/rpsb6f76hxzcfblghfgch2jl8413w6m4-dragonos-rootfs/bin/dragonos-rootfs +❯ which dragonos-run +/nix/store/1sdnhsjihj2h3mkdx7x0v8zy9ldfijml-dragonos-run/bin/dragonos-run +❯ dragonos-run +# DragonOS QEMU Start +❯ make kernel # 重新构建内核 +❯ dragonos-run # 不涉及启动脚本和rootfs的更改,直接qemu启动 +``` + ## 查看可用构建产物 ```shell diff --git a/flake.nix b/flake.nix index c9bc66252..acc815d4c 100644 --- a/flake.nix +++ b/flake.nix @@ -56,7 +56,7 @@ # 脚本不占什么空间所以重复eval也没关系,并且最终产出的脚本可读性更好. "start-${target}" = { type = "app"; - program = "${startPkg}/bin/run-dragonos"; + program = "${startPkg}/bin/dragonos-run"; meta.description = "以 ${target} 启动DragonOS"; }; # rootfs 中涉及到基于docker镜像的rootfs构建,修改了 user/ 下软件包相关内容后, @@ -64,7 +64,7 @@ # 因此可能会占很多空间,如果要清理空间请执行 nix store gc "rootfs-${target}" = { type = "app"; - program = "${rootfsPkg}/bin/build-rootfs-image"; + program = "${rootfsPkg}/bin/dragonos-rootfs"; meta.description = "构建 ${target} rootfs 镜像"; }; }; diff --git a/tools/qemu/default.nix b/tools/qemu/default.nix index 44b9e9c39..7fc4daeed 100644 --- a/tools/qemu/default.nix +++ b/tools/qemu/default.nix @@ -143,7 +143,7 @@ let ''; script = lib.genAttrs [ "x86_64" "riscv64" ] (arch: mkRunScript { - name = "run-dragonos"; + name = "dragonos-run"; inherit arch; isNographic = if arch == "riscv64" then true else baseConfig.nographic; qemuBin = "${pkgs.qemu_kvm}/bin/qemu-system-${arch}"; diff --git a/user/default.nix b/user/default.nix index 53147adb6..69b082ca9 100644 --- a/user/default.nix +++ b/user/default.nix @@ -17,7 +17,7 @@ let # 构建脚本 - 在bin/目录下构建 buildScript = pkgs.writeShellApplication { - name = "build-rootfs-image"; + name = "dragonos-rootfs"; runtimeInputs = [ pkgs.coreutils pkgs.gnutar pkgs.libguestfs-with-appliance pkgs.findutils ]; text = '' set -euo pipefail