diff --git a/Makefile b/Makefile index 718c3b21..ea1d45f2 100644 --- a/Makefile +++ b/Makefile @@ -32,17 +32,17 @@ rootfs: curl -f -L $(ROOTFS_URL)/$(ROOTFS_IMG).xz -O; \ xz -d $(ROOTFS_IMG).xz; \ fi - @cp $(ROOTFS_IMG) arceos/disk.img + @cp $(ROOTFS_IMG) make/disk.img img: @echo -e "\033[33mWARN: The 'img' target is deprecated. Please use 'rootfs' instead.\033[0m" @$(MAKE) --no-print-directory rootfs defconfig justrun clean: - @make -C arceos $@ + @$(MAKE) -C make $@ build run debug disasm: defconfig - @make -C arceos $@ + @$(MAKE) -C make $@ # Aliases rv: diff --git a/make/Makefile b/make/Makefile new file mode 100644 index 00000000..8290a6d7 --- /dev/null +++ b/make/Makefile @@ -0,0 +1,208 @@ +# Available arguments: +# * General options: +# - `ARCH`: Target architecture: x86_64, riscv64, aarch64, loongarch64 +# - `MYPLAT`: Package name of the target platform crate. +# - `PLAT_CONFIG`: Path to the platform configuration file. +# - `SMP`: Override maximum CPU number specified in the platform config. For +# statically configured platforms, this is also the number of CPUs to boot +# and for platforms with runtime CPU detection, this is the upper limit of +# CPUs. +# - `MODE`: Build mode: release, debug +# - `LOG:` Logging level: warn, error, info, debug, trace +# - `V`: Verbose level: (empty), 1, 2 +# - `TARGET_DIR`: Artifact output directory (cargo target directory) +# - `EXTRA_CONFIG`: Extra config specification file +# - `OUT_CONFIG`: Final config file that takes effect +# - `UIMAGE`: To generate U-Boot image +# - `LD_SCRIPT`: Use a custom linker script file. +# * App options: +# - `A` or `APP`: Path to the application +# - `FEATURES`: Features os ArceOS modules to be enabled. +# - `APP_FEATURES`: Features of (rust) apps to be enabled. +# * QEMU options: +# - `BLK`: Enable storage devices (virtio-blk) +# - `NET`: Enable network devices (virtio-net) +# - `GRAPHIC`: Enable display devices and graphic output (virtio-gpu) +# - `BUS`: Device bus type: mmio, pci +# - `MEM`: Memory size (default is 128M) +# - `DISK_IMG`: Path to the virtual disk image +# - `ACCEL`: Enable hardware acceleration (KVM on linux) +# - `QEMU_LOG`: Enable QEMU logging (log file is "qemu.log") +# - `NET_DUMP`: Enable network packet dump (log file is "netdump.pcap") +# - `NET_DEV`: QEMU netdev backend types: user, tap, bridge +# - `VFIO_PCI`: PCI device address in the format "bus:dev.func" to passthrough +# - `VHOST`: Enable vhost-net for tap backend (only for `NET_DEV=tap`) +# * Network options: +# - `IP`: ArceOS IPv4 address (default is 10.0.2.15 for QEMU user netdev) +# - `GW`: Gateway IPv4 address (default is 10.0.2.2 for QEMU user netdev) + +# General options +ARCH ?= x86_64 +MYPLAT ?= +PLAT_CONFIG ?= +SMP ?= +MODE ?= release +LOG ?= warn +V ?= +DWARF ?= +LTO ?= +TARGET_DIR ?= $(PWD)/target +EXTRA_CONFIG ?= +OUT_CONFIG ?= $(PWD)/.axconfig.toml +UIMAGE ?= n + +# App options +A ?= examples/helloworld +APP ?= $(A) +FEATURES ?= +APP_FEATURES ?= +NO_AXSTD ?= n + +# QEMU options +BLK ?= n +NET ?= n +GRAPHIC ?= n +INPUT ?= n +VSOCK ?= n +BUS ?= pci +MEM ?= +ACCEL ?= +ICOUNT ?= n +QEMU_ARGS ?= + +DISK_IMG ?= disk.img +QEMU_LOG ?= n +NET_DUMP ?= n +NET_DEV ?= user +VFIO_PCI ?= +VHOST ?= n + +# Network options +IP ?= 10.0.2.15 +GW ?= 10.0.2.2 + +# App type +ifeq ($(wildcard $(APP)),) + $(error Application path "$(APP)" is not valid) +endif + +ifneq ($(wildcard $(APP)/Cargo.toml),) + APP_TYPE := rust + AX_LIB ?= axstd +else + APP_TYPE := c + AX_LIB ?= axlibc +endif + +.DEFAULT_GOAL := all + +ifneq ($(filter $(or $(MAKECMDGOALS), $(.DEFAULT_GOAL)), all build disasm run justrun debug defconfig oldconfig),) +# Install dependencies +include deps.mk +# Platform resolving +include platform.mk +# Configuration generation +include config.mk +# Feature parsing +include features.mk +endif + +# Target +ifeq ($(ARCH), x86_64) + TARGET := x86_64-unknown-none +else ifeq ($(ARCH), aarch64) + TARGET := aarch64-unknown-none-softfloat +else ifeq ($(ARCH), riscv64) + TARGET := riscv64gc-unknown-none-elf +else ifeq ($(ARCH), loongarch64) + TARGET := loongarch64-unknown-none-softfloat +else + $(error "ARCH" must be one of "x86_64", "riscv64", "aarch64" or "loongarch64") +endif + +export AX_ARCH=$(ARCH) +export AX_PLATFORM=$(PLAT_NAME) +export AX_MODE=$(MODE) +export AX_LOG=$(LOG) +export AX_TARGET=$(TARGET) +export AX_IP=$(IP) +export AX_GW=$(GW) + +ifneq ($(filter $(MAKECMDGOALS),unittest unittest_no_fail_fast clippy doc doc_check_missing),) + # When running unit tests or other tests unrelated to a specific platform, + # set `AX_CONFIG_PATH` to empty for dummy config + unexport AX_CONFIG_PATH +else + export AX_CONFIG_PATH=$(OUT_CONFIG) +endif + +# Binutils +CROSS_COMPILE ?= $(ARCH)-linux-musl- +CC := $(CROSS_COMPILE)gcc +AR := $(CROSS_COMPILE)ar +RANLIB := $(CROSS_COMPILE)ranlib +LD := rust-lld -flavor gnu + +OBJDUMP ?= rust-objdump -d --print-imm-hex --x86-asm-syntax=intel +OBJCOPY ?= rust-objcopy --binary-architecture=$(ARCH) +GDB ?= gdb + +# Paths +OUT_DIR ?= $(APP) +LD_SCRIPT ?= $(TARGET_DIR)/$(TARGET)/$(MODE)/linker_$(PLAT_NAME).lds + +APP_NAME := $(shell basename $(APP)) +OUT_ELF := $(OUT_DIR)/$(APP_NAME)_$(PLAT_NAME).elf +OUT_BIN := $(patsubst %.elf,%.bin,$(OUT_ELF)) +OUT_UIMG := $(patsubst %.elf,%.uimg,$(OUT_ELF)) +ifeq ($(UIMAGE), y) + FINAL_IMG := $(OUT_UIMG) +else + FINAL_IMG := $(OUT_BIN) +endif + +all: build + +include utils.mk +include build.mk +include qemu.mk + +defconfig: + $(call defconfig) + +oldconfig: + $(call oldconfig) + +build: $(OUT_DIR) $(FINAL_IMG) + +disasm: + $(OBJDUMP) $(OUT_ELF) | less + +run: build justrun + +justrun: + $(call run_qemu) + +debug: build + $(call run_qemu_debug) & + $(GDB) $(OUT_ELF) \ + -ex 'target remote localhost:1234' \ + -ex 'b __axplat_main' \ + -ex 'continue' \ + -ex 'disp /16i $$pc' + +disk_img: +ifneq ($(wildcard $(DISK_IMG)),) + @printf "$(YELLOW_C)warning$(END_C): disk image \"$(DISK_IMG)\" already exists!\n" +else + $(call make_disk_image,fat32,$(DISK_IMG)) +endif + +clean: clean_c + rm -rf $(APP)/*.bin $(APP)/*.elf $(OUT_CONFIG) + cargo clean + +.PHONY: all defconfig oldconfig \ + build disasm run justrun debug \ + clippy doc doc_check_missing fmt fmt_c unittest unittest_no_fail_fast \ + disk_img clean diff --git a/make/build.mk b/make/build.mk new file mode 100644 index 00000000..167f463c --- /dev/null +++ b/make/build.mk @@ -0,0 +1,86 @@ +# Main building script + +include cargo.mk + +ifeq ($(APP_TYPE), c) + include build_c.mk +else + rust_package := $(shell cat $(APP)/Cargo.toml | sed -n 's/^name = "\([a-z0-9A-Z_\-]*\)"/\1/p') + rust_elf := $(TARGET_DIR)/$(TARGET)/$(MODE)/$(rust_package) +endif + +ifneq ($(filter $(MAKECMDGOALS),doc doc_check_missing),) + # run `make doc` + $(if $(V), $(info RUSTFLAGS: "$(RUSTFLAGS)") $(info RUSTDOCFLAGS: "$(RUSTDOCFLAGS)")) + export RUSTFLAGS + export RUSTDOCFLAGS +else ifneq ($(filter $(MAKECMDGOALS),unittest unittest_no_fail_fast),) + # run `make unittest` + $(if $(V), $(info RUSTFLAGS: "$(RUSTFLAGS)")) + export RUSTFLAGS +else ifneq ($(filter $(or $(MAKECMDGOALS), $(.DEFAULT_GOAL)), all build run justrun debug),) + # run `make build` and other above goals + ifneq ($(V),) + $(info APP: "$(APP)") + $(info APP_TYPE: "$(APP_TYPE)") + $(info FEATURES: "$(FEATURES)") + $(info PLAT_CONFIG: "$(PLAT_CONFIG)") + $(info arceos features: "$(AX_FEAT)") + $(info lib features: "$(LIB_FEAT)") + $(info app features: "$(APP_FEAT)") + endif + ifeq ($(APP_TYPE), c) + $(if $(V), $(info CFLAGS: "$(CFLAGS)") $(info LDFLAGS: "$(LDFLAGS)")) + else ifeq ($(APP_TYPE), rust) + RUSTFLAGS += $(RUSTFLAGS_LINK_ARGS) + endif + ifeq ($(DWARF), y) + RUSTFLAGS += -C force-frame-pointers -C debuginfo=2 -C strip=none + endif + $(if $(V), $(info RUSTFLAGS: "$(RUSTFLAGS)")) + export RUSTFLAGS + ifeq ($(LTO), y) + export CARGO_PROFILE_RELEASE_LTO=true + export CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1 + endif +endif + +_cargo_build: oldconfig + @printf " $(GREEN_C)Building$(END_C) App: $(APP_NAME), Arch: $(ARCH), Platform: $(PLAT_NAME), App type: $(APP_TYPE)\n" +ifeq ($(APP_TYPE), rust) + $(call cargo_build,$(APP),$(AX_FEAT) $(LIB_FEAT) $(APP_FEAT)) + @cp $(rust_elf) $(OUT_ELF) +else ifeq ($(APP_TYPE), c) + $(call cargo_build,ulib/axlibc,$(AX_FEAT) $(LIB_FEAT)) +endif + +$(OUT_DIR): + $(call run_cmd,mkdir,-p $@) + +_dwarf: $(OUT_ELF) +ifeq ($(DWARF), y) + $(call run_cmd,./dwarf.sh,$(OUT_ELF) $(OBJCOPY)) +endif + +$(OUT_BIN): _cargo_build $(OUT_ELF) _dwarf + $(call run_cmd,$(OBJCOPY),$(OUT_ELF) --strip-all -O binary $@) + @if [ ! -s $(OUT_BIN) ]; then \ + echo 'Empty kernel image "$(notdir $(FINAL_IMG))" is built, please check your build configuration'; \ + exit 1; \ + fi + +ifeq ($(ARCH), aarch64) + uimg_arch := arm64 +else ifeq ($(ARCH), riscv64) + uimg_arch := riscv +else + uimg_arch := $(ARCH) +endif + +$(OUT_UIMG): $(OUT_BIN) + $(call run_cmd,mkimage,\ + -A $(uimg_arch) -O linux -T kernel -C none \ + -a $(subst _,,$(shell axconfig-gen "$(OUT_CONFIG)" -r plat.kernel-base-paddr)) \ + -d $(OUT_BIN) $@) + +.PHONY: _cargo_build _dwarf diff --git a/make/cargo.mk b/make/cargo.mk new file mode 100644 index 00000000..4e40a461 --- /dev/null +++ b/make/cargo.mk @@ -0,0 +1,29 @@ +# Cargo features and build args + +ifeq ($(V),1) + verbose := -v +else ifeq ($(V),2) + verbose := -vv +else + verbose := +endif + +build_args-release := --release + +build_args := \ + -Z unstable-options \ + --target $(TARGET) \ + --target-dir $(TARGET_DIR) \ + $(build_args-$(MODE)) \ + $(verbose) + +RUSTFLAGS_LINK_ARGS := -C link-arg=-T$(LD_SCRIPT) -C link-arg=-no-pie -C link-arg=-znostart-stop-gc +RUSTDOCFLAGS := -Z unstable-options --enable-index-page -D rustdoc::broken_intra_doc_links + +ifeq ($(MAKECMDGOALS), doc_check_missing) + RUSTDOCFLAGS += -D missing-docs +endif + +define cargo_build + $(call run_cmd,cargo -C $(1) build,$(build_args) --features "$(strip $(2))") +endef diff --git a/make/config.mk b/make/config.mk new file mode 100644 index 00000000..169ad954 --- /dev/null +++ b/make/config.mk @@ -0,0 +1,39 @@ +# Config generation + +config_args := \ + defconfig.toml $(PLAT_CONFIG) $(EXTRA_CONFIG) \ + -w 'arch="$(ARCH)"' \ + -w 'platform="$(PLAT_NAME)"' \ + -o "$(OUT_CONFIG)" + +ifneq ($(MEM),) + config_args += -w 'plat.phys-memory-size=$(shell ./strtosz.py $(MEM))' +else + MEM := $(shell axconfig-gen $(PLAT_CONFIG) -r plat.phys-memory-size 2>/dev/null | tr -d _ | xargs printf "%dB") +endif + +ifneq ($(SMP),) + config_args += -w 'plat.max-cpu-num=$(SMP)' +else + SMP := $(shell axconfig-gen $(PLAT_CONFIG) -r plat.max-cpu-num 2>/dev/null) + ifeq ($(SMP),) + $(error "`plat.max-cpu-num` is not defined in the platform configuration file, \ + this option must be specified even for platforms with runtime CPU detection.") + endif +endif + +define defconfig + $(call run_cmd,axconfig-gen,$(config_args)) +endef + +ifeq ($(wildcard $(OUT_CONFIG)),) + define oldconfig + $(call defconfig) + endef +else + define oldconfig + $(if $(filter "$(PLAT_NAME)",$(shell axconfig-gen "$(OUT_CONFIG)" -r platform)),\ + $(call run_cmd,axconfig-gen,$(config_args) -c "$(OUT_CONFIG)"),\ + $(error "ARCH" or "MYPLAT" has been changed, please run "make defconfig" again)) + endef +endif diff --git a/make/defconfig.toml b/make/defconfig.toml new file mode 100644 index 00000000..b5f51411 --- /dev/null +++ b/make/defconfig.toml @@ -0,0 +1,6 @@ +# Stack size of each task. +task-stack-size = 0x40000 # uint + +# Number of timer ticks per second (Hz). A timer tick may contain several timer +# interrupts. +ticks-per-sec = 100 # uint diff --git a/make/deps.mk b/make/deps.mk new file mode 100644 index 00000000..bbeee115 --- /dev/null +++ b/make/deps.mk @@ -0,0 +1,19 @@ +# Necessary dependencies for the build system + +# Tool to parse information about the target package +ifeq ($(shell cargo axplat --version 2>/dev/null),) + $(info Installing cargo-axplat...) + $(shell cargo install cargo-axplat) +endif + +# Tool to generate platform configuration files +ifeq ($(shell axconfig-gen --version 2>/dev/null),) + $(info Installing axconfig-gen...) + $(shell cargo install axconfig-gen) +endif + +# Cargo binutils +ifeq ($(shell cargo install --list | grep cargo-binutils),) + $(info Installing cargo-binutils...) + $(shell cargo install cargo-binutils) +endif diff --git a/make/dwarf.sh b/make/dwarf.sh new file mode 100755 index 00000000..82d5eea6 --- /dev/null +++ b/make/dwarf.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +ELF=$1 +OBJCOPY=$2 + +if [ -z "$ELF" ] || [ -z "$OBJCOPY" ]; then + echo "Usage: $0 " + exit 1 +fi + +if [ ! -f "$ELF" ]; then + echo "Error: ELF file '$ELF' does not exist." + exit 1 +fi + +SECTIONS=( + debug_abbrev + debug_addr + debug_aranges + debug_info + debug_line + debug_line_str + debug_ranges + debug_rnglists + debug_str + debug_str_offsets +) + +for section in "${SECTIONS[@]}"; do + $OBJCOPY $ELF --dump-section .$section=$section.bin 2> /dev/null || touch $section.bin & +done +wait +$OBJCOPY $ELF --strip-debug + +cmd=($OBJCOPY $ELF) +for section in "${SECTIONS[@]}"; do + cmd+=(--update-section $section=$section.bin) + cmd+=(--rename-section $section=.$section) +done +${cmd[@]} + +for section in "${SECTIONS[@]}"; do + rm -f $section.bin +done diff --git a/make/features.mk b/make/features.mk new file mode 100644 index 00000000..83d396e8 --- /dev/null +++ b/make/features.mk @@ -0,0 +1,71 @@ +# Features resolving. +# +# Inputs: +# - `FEATURES`: a list of features to be enabled split by spaces or commas. +# The features can be selected from the crate `axfeat` or the user library +# (crate `axstd` or `axlibc`). +# - `APP_FEATURES`: a list of features to be enabled for the Rust app. +# +# Outputs: +# - `AX_FEAT`: features to be enabled for ArceOS modules (crate `axfeat`). +# - `LIB_FEAT`: features to be enabled for the user library (crate `axstd`, `axlibc`). +# - `APP_FEAT`: features to be enabled for the Rust app. + +ifeq ($(APP_TYPE),c) + ax_feat_prefix := axfeat/ + lib_features := fp-simd irq alloc multitask fs net fd pipe select epoll +else + ifeq ($(NO_AXSTD),y) + ax_feat_prefix := axfeat/ + else + ax_feat_prefix := axstd/ + endif + lib_features := +endif + +lib_feat_prefix := $(AX_LIB)/ + +override FEATURES := $(shell echo $(FEATURES) | tr ',' ' ') + +ifeq ($(APP_TYPE), c) + ifneq ($(wildcard $(APP)/features.txt),) # check features.txt exists + override FEATURES += $(shell cat $(APP)/features.txt) + endif + ifneq ($(filter fs net pipe select epoll,$(FEATURES)),) + override FEATURES += fd + endif +endif + +override FEATURES := $(strip $(FEATURES)) + +ax_feat := +lib_feat := + +ifneq ($(MYPLAT),) + ax_feat += myplat +else + ax_feat += defplat +endif + +ifeq ($(filter $(LOG),off error warn info debug trace),) + $(error "LOG" must be one of "off", "error", "warn", "info", "debug", "trace") +endif + +ifeq ($(BUS),mmio) + ax_feat += bus-mmio +endif + +ifeq ($(DWARF),y) + ax_feat += dwarf +endif + +ifeq ($(shell test $(SMP) -gt 1; echo $$?),0) + lib_feat += smp +endif + +ax_feat += $(filter-out $(lib_features),$(FEATURES)) +lib_feat += $(filter $(lib_features),$(FEATURES)) + +AX_FEAT := $(strip $(addprefix $(ax_feat_prefix),$(ax_feat))) +LIB_FEAT := $(strip $(addprefix $(lib_feat_prefix),$(lib_feat))) +APP_FEAT := $(strip $(shell echo $(APP_FEATURES) | tr ',' ' ')) diff --git a/make/platform.mk b/make/platform.mk new file mode 100644 index 00000000..265c2f17 --- /dev/null +++ b/make/platform.mk @@ -0,0 +1,59 @@ +# Architecture and platform resolving + +ifeq ($(APP_TYPE), rust) + cargo_manifest_dir := $(APP) +else + cargo_manifest_dir := $(CURDIR) +endif + +define resolve_config + $(if $(wildcard $(PLAT_CONFIG)),\ + $(PLAT_CONFIG),\ + $(shell cargo axplat info -C $(cargo_manifest_dir) -c $(PLAT_PACKAGE))) +endef + +define validate_config + $(eval package := $(shell axconfig-gen $(PLAT_CONFIG) -r package 2>/dev/null)) \ + $(if $(strip $(package)),,$(error PLAT_CONFIG=$(PLAT_CONFIG) is not a valid platform configuration file)) \ + $(if $(filter "$(PLAT_PACKAGE)",$(package)),,\ + $(error `PLAT_PACKAGE` field mismatch: expected $(PLAT_PACKAGE), got $(package))) +endef + +ifeq ($(MYPLAT),) + # `MYPLAT` is not specified, use the default platform for each architecture + ifeq ($(ARCH), x86_64) + PLAT_PACKAGE := axplat-x86-pc + else ifeq ($(ARCH), aarch64) + PLAT_PACKAGE := axplat-aarch64-qemu-virt + else ifeq ($(ARCH), riscv64) + PLAT_PACKAGE := axplat-riscv64-qemu-virt + else ifeq ($(ARCH), loongarch64) + PLAT_PACKAGE := axplat-loongarch64-qemu-virt + else + $(error "ARCH" must be one of "x86_64", "riscv64", "aarch64" or "loongarch64") + endif + PLAT_CONFIG := $(strip $(call resolve_config)) + # We don't need to check whether `PLAT_CONFIG` is valid here, as the `PLAT_PACKAGE` + # is a valid pacakage. + + $(call validate_config) +else + # `MYPLAT` is specified, treat it as a package name + PLAT_PACKAGE := $(MYPLAT) + PLAT_CONFIG := $(strip $(call resolve_config)) + ifeq ($(wildcard $(PLAT_CONFIG)),) + $(error "MYPLAT=$(MYPLAT) is not a valid platform package name") + endif + $(call validate_config) + + # Read the architecture name from the configuration file + _arch := $(patsubst "%",%,$(shell axconfig-gen $(PLAT_CONFIG) -r arch)) + ifeq ($(origin ARCH),command line) + ifneq ($(ARCH),$(_arch)) + $(error "ARCH=$(ARCH)" is not compatible with "MYPLAT=$(MYPLAT)") + endif + endif + ARCH := $(_arch) +endif + +PLAT_NAME := $(patsubst "%",%,$(shell axconfig-gen $(PLAT_CONFIG) -r platform)) diff --git a/make/qemu.mk b/make/qemu.mk new file mode 100644 index 00000000..4eb31b71 --- /dev/null +++ b/make/qemu.mk @@ -0,0 +1,126 @@ +# QEMU arguments + +QEMU := qemu-system-$(ARCH) + +ifeq ($(BUS), mmio) + vdev-suffix := device +else ifeq ($(BUS), pci) + vdev-suffix := pci +else + $(error "BUS" must be one of "mmio" or "pci") +endif + +ifeq ($(ARCH), x86_64) + machine := q35 +else ifeq ($(ARCH), riscv64) + machine := virt +else ifeq ($(ARCH), aarch64) + ifeq ($(PLAT_NAME), aarch64-raspi4) + machine := raspi4b + else + machine := virt + endif +else ifeq ($(ARCH), loongarch64) + machine := virt +endif + +qemu_args-x86_64 := \ + -machine $(machine) \ + -kernel $(OUT_ELF) + +qemu_args-riscv64 := \ + -machine $(machine) \ + -bios default \ + -kernel $(FINAL_IMG) + +qemu_args-aarch64 := \ + -cpu cortex-a72 \ + -machine $(machine) \ + -kernel $(FINAL_IMG) + +qemu_args-loongarch64 := \ + -machine $(machine) \ + -kernel $(FINAL_IMG) + +qemu_args-y := -m $(MEM) -smp $(SMP) $(qemu_args-$(ARCH)) + +qemu_args-$(BLK) += \ + -device virtio-blk-$(vdev-suffix),drive=disk0 \ + -drive id=disk0,if=none,format=raw,file=$(DISK_IMG) + +qemu_args-$(NET) += \ + -device virtio-net-$(vdev-suffix),netdev=net0 + +ifeq ($(NET_DEV), user) + qemu_args-$(NET) += -netdev user,id=net0,hostfwd=tcp::5555-:5555,hostfwd=udp::5555-:5555 +else ifeq ($(NET_DEV), tap) + qemu_args-$(NET) += -netdev tap,id=net0,script=scripts/net/qemu-ifup.sh,downscript=no,vhost=$(VHOST),vhostforce=$(VHOST) + QEMU := sudo $(QEMU) +else ifeq ($(NET_DEV), bridge) + qemu_args-$(NET) += -netdev bridge,id=net0,br=virbr0 + QEMU := sudo $(QEMU) +else + $(error "NET_DEV" must be one of "user", "tap", or "bridge") +endif + +ifneq ($(VFIO_PCI),) + qemu_args-y += --device vfio-pci,host=$(VFIO_PCI) + QEMU := sudo $(QEMU) +endif + +ifeq ($(NET_DUMP), y) + qemu_args-$(NET) += -object filter-dump,id=dump0,netdev=net0,file=netdump.pcap +endif + +qemu_args-$(GRAPHIC) += \ + -device virtio-gpu-$(vdev-suffix) -vga none \ + -serial mon:stdio + +ifeq ($(GRAPHIC), n) + qemu_args-y += -nographic +endif + +qemu_args-$(INPUT) += \ + -device virtio-mouse-pci -device virtio-keyboard-pci + +qemu_args-$(VSOCK) += \ + -device vhost-vsock-pci,id=virtiosocket0,guest-cid=103 + +ifeq ($(QEMU_LOG), y) + qemu_args-y += -D qemu.log -d in_asm,int,mmu,pcall,cpu_reset,guest_errors +endif + +qemu_args-$(ICOUNT) += -icount shift=1 + +qemu_args-y += $(QEMU_ARGS) + +qemu_args-debug := $(qemu_args-y) -s -S + +ifeq ($(ACCEL),) + ifneq ($(findstring -microsoft, $(shell uname -r | tr '[:upper:]' '[:lower:]')),) + ACCEL := n # Don't enable kvm for WSL/WSL2 + else ifeq ($(ARCH), x86_64) + ACCEL := $(if $(filter $(shell uname -m),x86_64),y,n) + else ifeq ($(ARCH), aarch64) + ACCEL := $(if $(filter $(shell uname -m),arm64 aarch64),y,n) + else + ACCEL := n + endif +endif + +# Do not use KVM for debugging +ifeq ($(shell uname), Darwin) + qemu_args-$(ACCEL) += -cpu host -accel hvf +else ifneq ($(wildcard /dev/kvm),) + qemu_args-$(ACCEL) += -cpu host -accel kvm +endif + +define run_qemu + @printf " $(CYAN_C)Running$(END_C) on qemu...\n" + $(call run_cmd,$(QEMU),$(qemu_args-y)) +endef + +define run_qemu_debug + @printf " $(CYAN_C)Debugging$(END_C) on qemu...\n" + $(call run_cmd,$(QEMU),$(qemu_args-debug)) +endef diff --git a/make/strtosz.py b/make/strtosz.py new file mode 100755 index 00000000..71b6ce5d --- /dev/null +++ b/make/strtosz.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 + +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument("size", type=str, help="Input string representing size") +args = parser.parse_args() + +size = args.size.strip().lower() + +if size.startswith("0x"): + if size[-1] != "b": + raise ValueError("Hexadecimal size must end with 'b' or 'B'") + number = int(size[:-1], 16) + multiplier = 1 +else: + # If last character is a digit, append 'm' (megabytes) + if size[-1].isdigit(): + size += "m" + + suffixes = { + "b": 0, + "k": 1, + "m": 2, + "g": 3, + "t": 4, + "p": 5, + "e": 6, + } + if size[-1] not in suffixes: + raise ValueError("Invalid size suffix. Use one of b, k, m, g, t, p, e") + multiplier = 1024 ** suffixes[size[-1]] + number = float(size[:-1]) + +print(int(number * multiplier)) diff --git a/make/utils.mk b/make/utils.mk new file mode 100644 index 00000000..21bac486 --- /dev/null +++ b/make/utils.mk @@ -0,0 +1,23 @@ +# Utility definitions and functions + +GREEN_C := \033[92;1m +CYAN_C := \033[96;1m +YELLOW_C := \033[93;1m +GRAY_C := \033[90m +WHITE_C := \033[37m +END_C := \033[0m + +define run_cmd + @printf '$(WHITE_C)$(1)$(END_C) $(GRAY_C)$(2)$(END_C)\n' + @$(1) $(2) +endef + +define make_disk_image_fat32 + @printf " $(GREEN_C)Creating$(END_C) FAT32 disk image \"$(1)\" ...\n" + @dd if=/dev/zero of=$(1) bs=1M count=64 + @mkfs.fat -F 32 $(1) +endef + +define make_disk_image + $(if $(filter $(1),fat32), $(call make_disk_image_fat32,$(2))) +endef