# Makefile for UEFI Operating System # Architecture ARCH = x86_64 # Compiler and tools CC = gcc LD = ld OBJCOPY = objcopy # Directories SRC_DIR = . BUILD_DIR = build EFI_DIR = $(BUILD_DIR)/EFI/BOOT IMAGE_DIR = $(BUILD_DIR)/image # GNU-EFI paths (common locations) EFI_INC = /usr/include/efi EFI_INCLUDES = -I$(EFI_INC) -I$(EFI_INC)/$(ARCH) -I$(EFI_INC)/protocol EFI_LDS = /usr/lib/elf_$(ARCH)_efi.lds EFI_CRT_OBJS = /usr/lib/crt0-efi-$(ARCH).o EFI_LIB_PATHS = -L/usr/lib # Compiler flags CFLAGS = -ffreestanding -fno-stack-protector -fpic \ -fshort-wchar -mno-red-zone -Wall -Wextra \ $(EFI_INCLUDES) -DEFI_FUNCTION_WRAPPER # Linker flags LDFLAGS = -nostdlib -znocombreloc -T $(EFI_LDS) -shared \ -Bsymbolic $(EFI_LIB_PATHS) $(EFI_CRT_OBJS) # Libraries LIBS = -lefi -lgnuefi # Target TARGET = BOOTX64.EFI TARGET_SO = bootx64.so OBJ = $(BUILD_DIR)/main.o KERNEL_TARGET = kernel.elf KERNEL_OBJS = $(BUILD_DIR)/kernel.o $(BUILD_DIR)/string_utils.o $(BUILD_DIR)/commands.o $(BUILD_DIR)/idt.o $(BUILD_DIR)/isr.o $(BUILD_DIR)/memory.o $(BUILD_DIR)/task.o $(BUILD_DIR)/context_switch.o KERNEL_LD = kernel.ld # QEMU settings QEMU = qemu-system-x86_64 # Try to find OVMF firmware (different locations on different distros) OVMF_CODE := $(shell \ if [ -f /usr/share/OVMF/OVMF_CODE_4M.fd ]; then \ echo /usr/share/OVMF/OVMF_CODE_4M.fd; \ elif [ -f /usr/share/OVMF/OVMF_CODE.fd ]; then \ echo /usr/share/OVMF/OVMF_CODE.fd; \ elif [ -f /usr/share/qemu/OVMF_CODE.fd ]; then \ echo /usr/share/qemu/OVMF_CODE.fd; \ elif [ -f /usr/share/edk2-ovmf/x64/OVMF_CODE.fd ]; then \ echo /usr/share/edk2-ovmf/x64/OVMF_CODE.fd; \ else \ echo /usr/share/OVMF/OVMF_CODE.fd; \ fi) OVMF_VARS_TEMPLATE := $(shell \ if [ -f /usr/share/OVMF/OVMF_VARS_4M.fd ]; then \ echo /usr/share/OVMF/OVMF_VARS_4M.fd; \ elif [ -f /usr/share/OVMF/OVMF_VARS.fd ]; then \ echo /usr/share/OVMF/OVMF_VARS.fd; \ elif [ -f /usr/share/qemu/OVMF_VARS.fd ]; then \ echo /usr/share/qemu/OVMF_VARS.fd; \ elif [ -f /usr/share/edk2-ovmf/x64/OVMF_VARS.fd ]; then \ echo /usr/share/edk2-ovmf/x64/OVMF_VARS.fd; \ else \ echo /usr/share/OVMF/OVMF_VARS.fd; \ fi) QEMU_FLAGS = -machine q35 \ -drive if=pflash,format=raw,readonly=on,file=$(OVMF_CODE) \ -drive if=pflash,format=raw,file=$(BUILD_DIR)/OVMF_VARS.fd \ -drive format=raw,file=fat:rw:$(BUILD_DIR) \ -net none \ -no-reboot # Phony targets .PHONY: all clean run setup install-deps iso runiso # Default target all: setup $(EFI_DIR)/$(TARGET) $(BUILD_DIR)/$(KERNEL_TARGET) # ISO target ISO_FILE = $(IMAGE_DIR)/os.iso ESP_IMG = $(IMAGE_DIR)/staging/esp.img iso: all @echo "Creating FAT ESP image for UEFI boot..." @mkdir -p $(IMAGE_DIR)/staging @# Calculate size: EFI binary + kernel + overhead, rounded up to MiB @SIZE_KB=$$(( ($$(stat -c%s $(EFI_DIR)/$(TARGET)) + $$(stat -c%s $(BUILD_DIR)/$(KERNEL_TARGET)) + 1048576) / 1024 )); \ dd if=/dev/zero of=$(ESP_IMG) bs=1K count=$$SIZE_KB 2>/dev/null mkfs.fat -F 12 $(ESP_IMG) >/dev/null mmd -i $(ESP_IMG) ::EFI mmd -i $(ESP_IMG) ::EFI/BOOT mcopy -i $(ESP_IMG) $(EFI_DIR)/$(TARGET) ::EFI/BOOT/BOOTX64.EFI mcopy -i $(ESP_IMG) $(BUILD_DIR)/$(KERNEL_TARGET) ::kernel.elf @echo "Creating ISO image..." xorriso -as mkisofs \ -o $(ISO_FILE) \ -e esp.img \ -no-emul-boot \ $(IMAGE_DIR)/staging @echo "ISO image created: $(ISO_FILE)" # Run from ISO with QEMU runiso: iso @echo "Starting QEMU from ISO..." @echo "Using OVMF firmware: $(OVMF_CODE)" @if [ ! -f $(BUILD_DIR)/OVMF_VARS.fd ]; then \ echo "Creating OVMF_VARS.fd from template..."; \ cp $(OVMF_VARS_TEMPLATE) $(BUILD_DIR)/OVMF_VARS.fd 2>/dev/null || \ dd if=/dev/zero of=$(BUILD_DIR)/OVMF_VARS.fd bs=1M count=64 2>/dev/null; \ fi $(QEMU) -machine q35 \ -drive if=pflash,format=raw,readonly=on,file=$(OVMF_CODE) \ -drive if=pflash,format=raw,file=$(BUILD_DIR)/OVMF_VARS.fd \ -cdrom $(ISO_FILE) \ -net none \ -nographic \ -no-reboot # Create necessary directories setup: @mkdir -p $(BUILD_DIR) @mkdir -p $(EFI_DIR) @mkdir -p $(IMAGE_DIR) # Compile source files $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c @echo "Compiling $<..." $(CC) $(CFLAGS) -c $< -o $@ # Compile kernel source $(BUILD_DIR)/kernel.o: $(SRC_DIR)/kernel.c @echo "Compiling kernel.c..." $(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \ -mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@ $(BUILD_DIR)/string_utils.o: $(SRC_DIR)/string_utils.c @echo "Compiling string_utils.c..." $(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \ -mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@ $(BUILD_DIR)/commands.o: $(SRC_DIR)/commands.c @echo "Compiling commands.c..." $(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \ -mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@ $(BUILD_DIR)/idt.o: $(SRC_DIR)/idt.c @echo "Compiling idt.c..." $(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \ -mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@ $(BUILD_DIR)/isr.o: $(SRC_DIR)/isr.S @echo "Compiling isr.S..." $(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \ -mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@ $(BUILD_DIR)/memory.o: $(SRC_DIR)/memory.c @echo "Compiling memory.c..." $(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \ -mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@ $(BUILD_DIR)/task.o: $(SRC_DIR)/task.c @echo "Compiling task.c..." $(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \ -mno-red-zone -Wall -Wextra $(EFI_INCLUDES) -c $< -o $@ $(BUILD_DIR)/context_switch.o: $(SRC_DIR)/context_switch.S @echo "Assembling context_switch.S..." $(CC) -ffreestanding -fno-stack-protector -fno-pic -fshort-wchar \ -mno-red-zone -Wall -Wextra -c $< -o $@ # Link kernel ELF $(BUILD_DIR)/$(KERNEL_TARGET): $(KERNEL_OBJS) $(KERNEL_LD) @echo "Linking kernel ELF..." $(LD) -nostdlib -T $(KERNEL_LD) $(KERNEL_OBJS) -o $@ # Link to create shared object $(BUILD_DIR)/$(TARGET_SO): $(OBJ) @echo "Linking $@..." $(LD) $(LDFLAGS) $(OBJ) -o $@ $(LIBS) # Convert to EFI application $(EFI_DIR)/$(TARGET): $(BUILD_DIR)/$(TARGET_SO) @echo "Creating EFI application..." $(OBJCOPY) -j .text -j .sdata -j .data -j .dynamic \ -j .dynsym -j .rel -j .rela -j .reloc \ --target=efi-app-$(ARCH) $< $@ @echo "Build complete: $@" # Run with QEMU run: all @echo "Starting QEMU..." @echo "Using OVMF firmware: $(OVMF_CODE)" @if [ ! -f $(BUILD_DIR)/OVMF_VARS.fd ]; then \ echo "Creating OVMF_VARS.fd from template..."; \ cp $(OVMF_VARS_TEMPLATE) $(BUILD_DIR)/OVMF_VARS.fd 2>/dev/null || \ dd if=/dev/zero of=$(BUILD_DIR)/OVMF_VARS.fd bs=1M count=64 2>/dev/null; \ fi @echo "================================================" @echo "QEMU is starting in nographic mode" @echo "To exit QEMU: Press Ctrl+A, then X" @echo "In the OS: Press 'q' to shutdown" @echo "================================================" $(QEMU) $(QEMU_FLAGS) # Clean build artifacts clean: @echo "Cleaning build directory..." rm -rf $(BUILD_DIR) @echo "Clean complete." # Install dependencies (for Debian/Ubuntu) install-deps: @echo "Installing dependencies..." sudo apt-get update sudo apt-get install -y gnu-efi qemu-system-x86 ovmf gcc binutils make xorriso mtools @echo "Dependencies installed." # Help target help: @echo "UEFI Operating System Makefile" @echo "" @echo "Targets:" @echo " all - Build the UEFI application (default)" @echo " run - Build and run with QEMU" @echo " iso - Create a bootable ISO image" @echo " runiso - Build, create ISO, and run in QEMU from ISO" @echo " clean - Remove build artifacts" @echo " install-deps - Install required dependencies (Debian/Ubuntu)" @echo " help - Display this help message" @echo "" @echo "Usage:" @echo " make # Build the OS" @echo " make run # Build and run in QEMU" @echo " make iso # Create ISO image" @echo " make runiso # Build, create ISO, and run in QEMU from ISO" @echo " make clean # Clean build files" @echo " make install-deps # Install dependencies"