#!/bin/bash # Script originally written by zxq5, I added separate scripts to remove `jq` dependency. # Colors GREEN='\033[0;32m' BLUE='\033[0;34m' YELLOW='\033[0;33m' RED='\033[0;31m' BOLD='\033[1m' NC='\033[0m' # No Color # Error handling set -e trap 'echo -e "${RED}${BOLD}error${NC}: build failed" >&2' ERR # Get absolute path to project root script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd) project_root=$(cd "$script_dir/.." &>/dev/null && pwd) echo -e "${GREEN}${BOLD} Running kernel in release mode." # Logging functions info() { echo -e "${BLUE}${BOLD}info${NC}: $1" } compiling() { echo -e "${GREEN}${BOLD}Compiling${NC}: $1" } warning() { echo -e "${YELLOW}${BOLD}warning${NC}: $1" >&2 } building() { echo -e "${GREEN}${BOLD}Building${NC}: $1" } copying() { echo -e "${GREEN}${BOLD} Copying${NC}: $1 to $2" } error() { echo -e "${RED}${BOLD}error${NC}: $1" >&2 exit 1 } copy_file() { copying $1 $2 cp "$1" "$2" || error $3 } build_dir="$project_root/build" iso_root="$build_dir/iso_root" # Check if we're running tests is_test=0 if [[ $1 == *"deps"* ]]; then is_test=1 kernel_path="$1" else # Build the kernel normally cd "$project_root" # cargo build kernel_path="$build_dir/target/x86_64-kernel/release/kernel" fi # Check for required tools check_tools() { local missing=0 for tool in xorriso git qemu-system-x86_64; do if ! command -v $tool >/dev/null 2>&1; then error "required tool '$tool' is not installed" missing=1 fi done if [ $missing -eq 1 ]; then error "missing required tools" fi } # Create build directory structure info "Creating build directory structure" mkdir -p "$iso_root/boot/limine" mkdir -p "$iso_root/EFI/BOOT" # Clone Limine if needed if [ ! -d "$build_dir/limine" ]; then compiling "limine bootloader" cd "$build_dir" git clone https://github.com/limine-bootloader/limine.git --branch=v9.x-binary --depth=1 "$build_dir/limine" || error "failed to clone limine" make -C "$build_dir/limine" || error "failed to build limine" cd "$project_root" fi # Copy files info "Copying files to ISO root" copy_file "$kernel_path" "$iso_root/boot/kernel" "failed to copy kernel" copy_file "$project_root/config/limine.conf" "$iso_root/boot/limine/limine.conf" "failed to copy limine config" copy_file "$build_dir/limine/limine-bios-cd.bin" "$iso_root/boot/limine/" "failed to copy limine-bios-cd.bin" copy_file "$build_dir/limine/limine-uefi-cd.bin" "$iso_root/boot/limine/" "failed to copy limine-uefi-cd.bin" copy_file "$build_dir/limine/limine-bios.sys" "$iso_root/boot/limine/" "failed to copy limine-bios.sys" copy_file "$build_dir/limine/BOOTX64.EFI" "$iso_root/EFI/BOOT/" "failed to copy BOOTX64.EFI" copy_file "$build_dir/limine/BOOTIA32.EFI" "$iso_root/EFI/BOOT/" "failed to copy BOOTIA32.EFI" # Create ISO building "bootable ISO image" xorriso -as mkisofs -R -r -J -b boot/limine/limine-bios-cd.bin \ -no-emul-boot -boot-load-size 4 -boot-info-table -hfsplus \ -apm-block-size 2048 --efi-boot boot/limine/limine-uefi-cd.bin \ -efi-boot-part --efi-boot-image --protective-msdos-label \ "$iso_root" -o "$build_dir/image.iso" || error "failed to create ISO" # Install Limine info "Installing Limine bootloader" "$build_dir/limine/limine" bios-install "$build_dir/image.iso" || error "failed to install limine" # Check if KVM is available if [ "${KVM_FLAG:-enable}" = "disable" ]; then warning "KVM acceleration disabled by user" kvm_flag="" elif [ -c "/dev/kvm" ] && [ -w "/dev/kvm" ]; then info "KVM acceleration enabled" kvm_flag="-enable-kvm" else warning "KVM acceleration not available (is kvm module loaded?)" kvm_flag="" fi # I'm lazy but I just remove GDB flags when running this script. debug_flags="" if [ $USE_LEGACY_BIOS ]; then boot_flags="" else # Check for the presence of the OVMF firmware. if [ ! -f $build_dir/RELEASEX64_OVMF_CODE.fd ]; then info "Downloading OVMF UEFI firmware for QEMU" info "To disable this, set USE_LEGACY_BIOS=1." pushd $build_dir curl https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF_CODE.fd -LO || error "failed to download OVMF firmware for UEFI" curl https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF_VARS.fd -LO || error "failed to download OVMF firmware for UEFI" popd fi boot_flags="-drive if=pflash,format=raw,readonly=on,file=$build_dir/RELEASEX64_OVMF_CODE.fd \ -drive if=pflash,format=raw,file=$build_dir/RELEASEX64_OVMF_VARS.fd" fi # Set up test-specific flags if [ $is_test -eq 1 ]; then test_flags="-device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none" serial_flags="-serial stdio" else test_flags="" # serial_flags="-serial tcp:127.0.0.1:1234,server -monitor telnet:127.0.0.1:1235,server" serial_flags="-serial stdio" fi # Run in QEMU if [[ ${QEMU_FLAGS} == *-S* ]]; then info "Running OS in QEMU with GDB debugging enabled" info "To connect GDB, run: gdb" info "At the GDB prompt, type: target remote localhost:1234" else info "Running OS in QEMU..." fi check_test_res() { qemu_exit_code=$? if [ $qemu_exit_code -eq 33 ]; then # Success case (0x10 << 1) | 1 = 33 info "All tests passed" exit 0 elif [ $qemu_exit_code -eq 35 ]; then # Failure case (0x11 << 1) | 1 = 35 warning "Some tests failed" exit 1 else # Any other exit code is treated as a failure warning "Some tests failed" exit 1 fi } kvm_flag="" trap 'check_test_res "tests completed"' ERR cd "$project_root" qemu-system-x86_64 -M q35 \ ${kvm_flag} \ -cdrom "$build_dir/image.iso" \ -boot d \ -m 2G \ ${serial_flags} \ -no-reboot \ ${test_flags} \ ${debug_flags} \ ${QEMU_FLAGS:-}