# 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
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 $@

# 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"
