PROJECT_NAME = ipxe-stub
SRC_DIR      = src
BUILD_DIR    = build
GNU_EFI_DIR  = includes/deps/gnu-efi
OUTPUT_DIR   = output
KEYS_DIR     = keys
SHIM_DIR     = input/shim
OPENSSL_CNF  = openssl.cnf

SIGNING_KEY  = $(KEYS_DIR)/signing.key
SIGNING_CERT = $(KEYS_DIR)/signing.crt
SIGNING_DER  = $(KEYS_DIR)/signing.der

HOST_ARCH := $(shell uname -m)
ARCH      ?= $(HOST_ARCH)

ifeq ($(ARCH),x86_64)
    TARGET_ARCH    = x86_64
    EFI_BINARY     = BOOTX64.EFI
    STUB_BINARY    = stubx64.efi
    ENROLL_BINARY  = enroll-x64.efi
    ARCH_CFLAGS    = -mno-red-zone
else ifeq ($(ARCH),aarch64)
    TARGET_ARCH    = aarch64
    EFI_BINARY     = BOOTAA64.EFI
    STUB_BINARY    = stubaa64.efi
    ENROLL_BINARY  = enroll-aa64.efi
    ARCH_CFLAGS    =
else ifeq ($(ARCH),ia32)
    TARGET_ARCH    = ia32
    EFI_BINARY     = BOOTIA32.EFI
    STUB_BINARY    = stubia32.efi
    ENROLL_BINARY  = enroll-ia32.efi
    ARCH_CFLAGS    =
else
    $(error Unsupported ARCH=$(ARCH). Use x86_64, aarch64, or ia32)
endif

ifeq ($(HOST_ARCH),x86_64)
    HOST_ARCH_NORM = x86_64
else ifeq ($(HOST_ARCH),aarch64)
    HOST_ARCH_NORM = aarch64
else ifeq ($(HOST_ARCH),i686)
    HOST_ARCH_NORM = ia32
else
    HOST_ARCH_NORM = unknown
endif

ifneq ($(HOST_ARCH_NORM),$(TARGET_ARCH))
    CROSS = 1
else
    CROSS = 0
endif

ifeq ($(CROSS),1)
    ifeq ($(TARGET_ARCH),x86_64)
        CROSS_PREFIX = x86_64-linux-gnu-
    else ifeq ($(TARGET_ARCH),aarch64)
        CROSS_PREFIX = aarch64-linux-gnu-
    else ifeq ($(TARGET_ARCH),ia32)
        CROSS_PREFIX = i686-linux-gnu-
    endif
    CC      = $(CROSS_PREFIX)gcc
    LD      = $(CROSS_PREFIX)ld
    AR      = $(CROSS_PREFIX)ar
    OBJCOPY = $(CROSS_PREFIX)objcopy
else
    CC      = gcc
    LD      = ld
    AR      = ar
    OBJCOPY = objcopy
endif

GNU_EFI_INC     = $(GNU_EFI_DIR)/inc
GNU_EFI_LIB_DIR = $(GNU_EFI_DIR)/$(TARGET_ARCH)/lib
GNU_EFI_GNUEFI  = $(GNU_EFI_DIR)/gnuefi

ARCH_BUILD_DIR = $(BUILD_DIR)/$(TARGET_ARCH)
EFI_BUILD_DIR  = $(ARCH_BUILD_DIR)/efi
EFI_CRT0       = $(EFI_BUILD_DIR)/crt0-efi-$(TARGET_ARCH).o
EFI_RELOC      = $(EFI_BUILD_DIR)/reloc_$(TARGET_ARCH).o
EFI_LIBEFI     = $(GNU_EFI_LIB_DIR)/libefi.a
EFI_LIBGNUEFI  = $(EFI_BUILD_DIR)/libgnuefi.a
EFI_LDS        = $(GNU_EFI_GNUEFI)/elf_$(TARGET_ARCH)_efi.lds

CFLAGS = -I$(GNU_EFI_INC) \
         -I$(GNU_EFI_INC)/$(TARGET_ARCH) \
         -I$(GNU_EFI_INC)/protocol \
         -Iincludes \
         -fno-stack-protector \
         -fpic \
         -fshort-wchar \
         $(ARCH_CFLAGS) \
         -DEFI_FUNCTION_WRAPPER

LDFLAGS = -nostdlib \
          -znocombreloc \
          -T $(EFI_LDS) \
          -shared \
          -Bsymbolic \
          -L$(GNU_EFI_LIB_DIR) \
          -L$(EFI_BUILD_DIR) \
          $(EFI_CRT0)

LIBS = -l:libgnuefi.a -l:libefi.a

OBJCOPY_FLAGS = -j .text \
                -j .sdata \
                -j .data \
                -j .rodata \
                -j .dynamic \
                -j .dynsym \
                -j .rel \
                -j .rela \
                -j .reloc \
                --target=efi-app-$(TARGET_ARCH)

LOADER_OBJ = $(ARCH_BUILD_DIR)/loader.o
LOADER_SO  = $(ARCH_BUILD_DIR)/loader.so
TARGET_EFI = $(OUTPUT_DIR)/$(EFI_BINARY)

STUB_OBJ    = $(ARCH_BUILD_DIR)/stub.o
STUB_SO     = $(ARCH_BUILD_DIR)/stub.so
TARGET_STUB = $(OUTPUT_DIR)/$(STUB_BINARY)

ENROLL_OBJ    = $(ARCH_BUILD_DIR)/enroll.o
ENROLL_SO     = $(ARCH_BUILD_DIR)/enroll.so
TARGET_ENROLL = $(OUTPUT_DIR)/$(ENROLL_BINARY)

.PHONY: all enroll clean distclean all-arches check-deps test-efi info keygen sign iso

all: check-deps $(TARGET_EFI) $(TARGET_STUB) $(TARGET_ENROLL)
	@echo "✓ Built: $(TARGET_EFI)  (loader → $(STUB_BINARY))"
	@echo "✓ Built: $(TARGET_STUB)"
	@echo "✓ Built: $(TARGET_ENROLL)  (→ mm$(TARGET_ARCH).efi on ISO)"

all-arches:
	$(MAKE) all ARCH=x86_64
	$(MAKE) all ARCH=aarch64
	$(MAKE) all ARCH=ia32

enroll: check-deps $(TARGET_ENROLL)
	@echo "✓ Built: $(TARGET_ENROLL)"

check-deps:
	@echo "Host: $(HOST_ARCH)  Target: $(TARGET_ARCH)  Mode: $(if $(filter 1,$(CROSS)),cross,native)"
ifeq ($(CROSS),1)
	@which $(CC) > /dev/null 2>&1 || \
		(echo "Install: sudo apt install gcc-$(TARGET_ARCH)-linux-gnu binutils-$(TARGET_ARCH)-linux-gnu" && exit 1)
endif

$(EFI_LIBEFI):
	@echo "→ Building libefi.a"
	@if [ "$(CROSS)" = "1" ]; then \
		$(MAKE) -C $(GNU_EFI_DIR) ARCH=$(TARGET_ARCH) CROSS_COMPILE=$(CROSS_PREFIX) lib; \
	else \
		$(MAKE) -C $(GNU_EFI_DIR) ARCH=$(TARGET_ARCH) lib; \
	fi

$(EFI_CRT0): | $(EFI_BUILD_DIR)
	$(CC) -I$(GNU_EFI_INC) -I$(GNU_EFI_INC)/$(TARGET_ARCH) \
		-fno-stack-protector -fpic \
		-c -o $@ $(GNU_EFI_GNUEFI)/crt0-efi-$(TARGET_ARCH).S

$(EFI_RELOC): | $(EFI_BUILD_DIR)
	$(CC) -I$(GNU_EFI_INC) -I$(GNU_EFI_INC)/$(TARGET_ARCH) \
		-fno-stack-protector -fpic -fshort-wchar $(ARCH_CFLAGS) \
		-c -o $@ $(GNU_EFI_GNUEFI)/reloc_$(TARGET_ARCH).c

$(EFI_LIBGNUEFI): $(EFI_RELOC) | $(EFI_BUILD_DIR)
	$(AR) rcs $@ $(EFI_RELOC)

$(BUILD_DIR) $(ARCH_BUILD_DIR) $(OUTPUT_DIR) $(EFI_BUILD_DIR):
	@mkdir -p $@

$(LOADER_OBJ): src/loader.c | $(ARCH_BUILD_DIR)
	$(CC) $(CFLAGS) -DSTUB_NAME='"$(STUB_BINARY)"' -c $< -o $@

$(LOADER_SO): $(LOADER_OBJ) $(EFI_CRT0) $(EFI_LIBGNUEFI) $(EFI_LIBEFI)
	$(LD) $(LDFLAGS) $(LOADER_OBJ) -o $@ $(LIBS)

$(TARGET_EFI): $(LOADER_SO) | $(OUTPUT_DIR)
	$(OBJCOPY) $(OBJCOPY_FLAGS) $(LOADER_SO) $@

$(STUB_OBJ): src/stub.c | $(ARCH_BUILD_DIR)
	$(CC) $(CFLAGS) -c $< -o $@

$(STUB_SO): $(STUB_OBJ) $(EFI_CRT0) $(EFI_LIBGNUEFI) $(EFI_LIBEFI)
	$(LD) $(LDFLAGS) $(STUB_OBJ) -o $@ $(LIBS)

$(TARGET_STUB): $(STUB_SO) | $(OUTPUT_DIR)
	$(OBJCOPY) $(OBJCOPY_FLAGS) $(STUB_SO) $@

$(ENROLL_OBJ): src/enroll.c | $(ARCH_BUILD_DIR)
	$(CC) $(CFLAGS) -c $< -o $@

$(ENROLL_SO): $(ENROLL_OBJ) $(EFI_CRT0) $(EFI_LIBGNUEFI) $(EFI_LIBEFI)
	$(LD) $(LDFLAGS) $(ENROLL_OBJ) -o $@ $(LIBS)

$(TARGET_ENROLL): $(ENROLL_SO) | $(OUTPUT_DIR)
	$(OBJCOPY) $(OBJCOPY_FLAGS) $(ENROLL_SO) $@

test-efi:
	@if [ -f "$(TARGET_EFI)" ]; then \
		echo -n "  Type: "; file $(TARGET_EFI) | grep -oE "PE32\+? executable.*EFI" || echo "unknown"; \
		echo -n "  Size: "; ls -lh $(TARGET_EFI) | awk '{print $$5}'; \
	fi

keygen:
	@if [ -f $(SIGNING_KEY) ]; then \
		echo "Keys already exist. Delete keys/ to regenerate."; exit 1; \
	fi
	bash gen-key.sh
	@echo "  openssl.cnf — config used for key generation"
	@echo "✓ Keys generated in $(KEYS_DIR)/"
	@echo "  signing.key  — keep private"
	@echo "  signing.crt  — PEM cert"
	@echo "  signing.der  — DER cert (place at EFI/BOOT/cert.der on ISO)"

sign: all-arches
	@if [ ! -f $(SIGNING_KEY) ]; then \
		echo "No signing key found. Run: make keygen"; exit 1; \
	fi
	@which sbsign > /dev/null 2>&1 || \
		(echo "Install: sudo apt install sbsigntool" && exit 1)

	@for efi in $(OUTPUT_DIR)/BOOT*.EFI $(OUTPUT_DIR)/stub*.efi; do \
		[ -f "$$efi" ] || continue; \
		sbsign --key $(SIGNING_KEY) --cert $(SIGNING_CERT) \
			--output "$$efi" "$$efi" 2>&1 | grep -v "^warning:"; \
		echo "✓ Signed: $$efi"; \
	done

	@[ -f $(OUTPUT_DIR)/enroll-x64.efi ] && \
		sbsign --key $(SIGNING_KEY) --cert $(SIGNING_CERT) \
			--output $(OUTPUT_DIR)/mmx64.efi $(OUTPUT_DIR)/enroll-x64.efi 2>&1 | grep -v "^warning:" && \
		echo "✓ Signed: $(OUTPUT_DIR)/mmx64.efi" || true

	@[ -f $(OUTPUT_DIR)/enroll-aa64.efi ] && \
		sbsign --key $(SIGNING_KEY) --cert $(SIGNING_CERT) \
			--output $(OUTPUT_DIR)/mmaa64.efi $(OUTPUT_DIR)/enroll-aa64.efi 2>&1 | grep -v "^warning:" && \
		echo "✓ Signed: $(OUTPUT_DIR)/mmaa64.efi" || true

	@[ -f $(OUTPUT_DIR)/enroll-ia32.efi ] && \
		sbsign --key $(SIGNING_KEY) --cert $(SIGNING_CERT) \
			--output $(OUTPUT_DIR)/mmia32.efi $(OUTPUT_DIR)/enroll-ia32.efi 2>&1 | grep -v "^warning:" && \
		echo "✓ Signed: $(OUTPUT_DIR)/mmia32.efi" || true

iso: sign
	@which xorriso > /dev/null 2>&1 || \
		(echo "Install: sudo apt install xorriso" && exit 1)
	@if [ ! -f $(SIGNING_DER) ]; then \
		echo "No signing.der found. Run: make keygen"; exit 1; \
	fi
	@rm -rf iso_root
	@mkdir -p iso_root/EFI/BOOT iso_root/IPXE
	@# Our signed stubs — firmware picks the right arch automatically
	cp $(OUTPUT_DIR)/BOOTX64.EFI  iso_root/EFI/BOOT/BOOTX64.EFI
	@[ -f $(OUTPUT_DIR)/BOOTAA64.EFI ] && \
		cp $(OUTPUT_DIR)/BOOTAA64.EFI iso_root/EFI/BOOT/BOOTAA64.EFI || true
	@[ -f $(OUTPUT_DIR)/BOOTIA32.EFI ] && \
		cp $(OUTPUT_DIR)/BOOTIA32.EFI iso_root/EFI/BOOT/BOOTIA32.EFI || true
	@# stub.efi — arch-neutral, loaded by all BOOT*.EFI stubs
	cp $(OUTPUT_DIR)/stub.efi iso_root/EFI/BOOT/stub.efi
		@# Pre-signed + renamed MOK manager binaries
	@[ -f $(OUTPUT_DIR)/mmx64.efi ] && \
		cp $(OUTPUT_DIR)/mmx64.efi iso_root/EFI/BOOT/mmx64.efi || true
	@[ -f $(OUTPUT_DIR)/mmaa64.efi ] && \
		cp $(OUTPUT_DIR)/mmaa64.efi iso_root/EFI/BOOT/mmaa64.efi || true
	@[ -f $(OUTPUT_DIR)/mmia32.efi ] && \
		cp $(OUTPUT_DIR)/mmia32.efi iso_root/EFI/BOOT/mmia32.efi || true
	@# Signing cert — enroll.efi reads this; also enrollable via UEFI setup
	cp $(SIGNING_DER) iso_root/EFI/BOOT/cert.der
	@# iPXE binaries
	@if [ -f ipxe/ipxe.efi ]; then cp ipxe/ipxe.efi iso_root/IPXE/ipxe.efi; \
	else echo "Warning: ipxe/ipxe.efi not found — IPXE/ will be empty"; fi
	@if [ -f ipxe/ipxe-snp.efi ]; then cp ipxe/ipxe-snp.efi iso_root/IPXE/ipxe-snp.efi; \
	else echo "Warning: ipxe/ipxe-snp.efi not found"; fi
	xorriso -as mkisofs \
		-o output/ipxe-boot.iso \
		-e EFI/BOOT/BOOTX64.EFI \
		-no-emul-boot \
		-isohybrid-gpt-basdat \
		iso_root
	@echo "✓ ISO: output/ipxe-boot.iso"

clean:
	rm -rf $(BUILD_DIR) $(OUTPUT_DIR) iso_root

distclean: clean
	$(MAKE) -C $(GNU_EFI_DIR) clean > /dev/null 2>&1 || true

info:
	@echo "Target: $(TARGET_ARCH)  Binary: $(EFI_BINARY)  Compiler: $(CC)"
