diff --git a/Makefile b/Makefile index d588b2c1..e693ea80 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ USER_DIR = user ISO_DIR = $(BUILD_DIR)/iso_root # The ISO target. -IMAGE_TARGET = $(BUILD_DIR)/../image.$(TARGETL).iso +IMAGE_TARGET ?= $(BUILD_DIR)/../image.$(TARGETL).iso # The init ramdisk directory. INITRD_DIR = $(BUILD_DIR)/initrd_root diff --git a/boron/Makefile b/boron/Makefile index 761597de..7a5dfe39 100644 --- a/boron/Makefile +++ b/boron/Makefile @@ -38,6 +38,14 @@ LINKER_FILE = linker.$(TARGETL).ld ISO_DIR=$(BUILD_DIR)/iso_root IMAGE_TARGET=$(BUILD_DIR)/image.iso +ifeq ($(TARGET),AMD64) + GENERATE_SYMBOLS = nasm +else ifeq ($(TARGET),I386) + GENERATE_SYMBOLS = nasm +else + GENERATE_SYMBOLS = gas +endif + # This is the name that our final kernel executable will have. # Change as needed. override KERNEL := $(BUILD_DIR)/kernel.$(TARGETL).elf @@ -150,10 +158,10 @@ NASMFLAGS += $(ARCH_ASFLAGS) # Use find to glob all *.c, *.S, and *.asm files in the directory and extract the object names. EXCLUDE_WRONG_ARCH = '(' '(' -path 'source/ke/$(TARGETL)/*' ')' -o '(' -path 'source/mm/$(TARGETL)/*' ')' -o '(' -not -path 'source/ke/*/*' -not -path 'source/mm/*/*' ')' ')' -override CFILES := $(shell find $(SRC_DIR) -not -path '*/.*' $(EXCLUDE_WRONG_ARCH) -type f -name '*.c') -override CXXFILES := $(shell find $(SRC_DIR) -not -path '*/.*' $(EXCLUDE_WRONG_ARCH) -type f -name '*.cpp') -override ASFILES := $(shell find $(SRC_DIR) -not -path '*/.*' $(EXCLUDE_WRONG_ARCH) -type f -name '*.S') -override NASMFILES := $(shell find $(SRC_DIR) -not -path '*/.*' $(EXCLUDE_WRONG_ARCH) -type f -name '*.asm') +override CFILES := $(shell find -L $(SRC_DIR) -not -path '*/.*' $(EXCLUDE_WRONG_ARCH) -type f -name '*.c') +override CXXFILES := $(shell find -L $(SRC_DIR) -not -path '*/.*' $(EXCLUDE_WRONG_ARCH) -type f -name '*.cpp') +override ASFILES := $(shell find -L $(SRC_DIR) -not -path '*/.*' $(EXCLUDE_WRONG_ARCH) -type f -name '*.S') +override NASMFILES := $(shell find -L $(SRC_DIR) -not -path '*/.*' $(EXCLUDE_WRONG_ARCH) -type f -name '*.asm') override OBJ := $(patsubst %.o,%.$(TARGETL).o,$(patsubst $(SRC_DIR)/%,$(BUILD_DIR)/%,$(CFILES:.c=.o) $(CXXFILES:.cpp=.o) $(ASFILES:.S=.o) $(NASMFILES:.asm=.o))) override HEADER_DEPS := $(patsubst %.o,%.d,$(OBJ)) @@ -175,13 +183,15 @@ VER_BUILD = $(shell cat $(BUILD_NUMBER_FILE)) CFLAGS += -D__BORON_MAJOR=$(VER_MAJOR) -D__BORON_MINOR=$(VER_MINOR) -D__BORON_BUILD=$(VER_BUILD) # Link rules for the final kernel executable. +ifeq ($(GENERATE_SYMBOLS),nasm) + $(KERNEL): $(SYMBOLS) @echo "[LD]\tBuilding $(KERNEL)" @$(BLD) $(OBJ) $(SYMBOLS) $(LDFLAGS) -o $@ $(SYMBOLS): $(KERNEL2) @echo "[NM]\tDumping and compiling symbols" - @nm -P $(KERNEL2) | $(SCRIPTS_DIR)/generate_symbols.py $(TARGETL) > $(BUILD_DIR)/_symtab.$(TARGETL).asm + @nm -P $(KERNEL2) | $(SCRIPTS_DIR)/generate_symbols_nasm.py $(TARGETL) > $(BUILD_DIR)/_symtab.$(TARGETL).asm @$(BASM) $(NASMFLAGS) $(BUILD_DIR)/_symtab.$(TARGETL).asm -o $(BUILD_DIR)/_symtab.$(TARGETL).o $(KERNEL2): $(KERNEL_PARTIAL) @@ -193,6 +203,35 @@ $(KERNEL_PARTIAL): $(OBJ) $(LINKER_FILE) @echo "[LD]\tPartially linking kernel" @$(BLD) -m $(LINK_ARCH) -r $(OBJ) $(LDFLAGSBASE) -o $@ +else ifeq ($(GENERATE_SYMBOLS),gas) + +# TODO: deduplicate +$(KERNEL): $(SYMBOLS) + @echo "[LD]\tBuilding $(KERNEL)" + @$(BLD) $(OBJ) $(SYMBOLS) $(LDFLAGS) -o $@ + +$(SYMBOLS): $(KERNEL2) + @echo "[NM]\tDumping and compiling symbols" + @nm -P $(KERNEL2) | $(SCRIPTS_DIR)/generate_symbols_gas.py $(TARGETL) > $(BUILD_DIR)/_symtab.$(TARGETL).S + @$(BCC) $(CPPFLAGS) $(CFLAGS) -c $(BUILD_DIR)/_symtab.$(TARGETL).S -o $(BUILD_DIR)/_symtab.$(TARGETL).o + +$(KERNEL2): $(KERNEL_PARTIAL) + @echo "[LD]\tLinking kernel to extract symbols" + @$(BLD) $(KERNEL_PARTIAL) -static $(LDFLAGS) -o $@ + +# Link rules for the amalgam object file. +$(KERNEL_PARTIAL): $(OBJ) $(LINKER_FILE) + @echo "[LD]\tPartially linking kernel" + @$(BLD) -m $(LINK_ARCH) -r $(OBJ) $(LDFLAGSBASE) -o $@ + +else + +$(KERNEL): $(OBJ) $(LINKER_FILE) + @echo "[LD]\tLinking kernel" + @$(BLD) $(OBJ) $(LDFLAGS) -o $@ + +endif + $(BUILD_DIR)/ke/version.$(TARGETL).o: $(filter-out $(BUILD_DIR)/ke/version.$(TARGETL).o, $(OBJ)) $(SRC_DIR)/ke/version.c @echo "[CC]\tCompiling $(SRC_DIR)/ke/version.c" @mkdir -p $(dir $@) diff --git a/boron/address_space_arm.txt b/boron/address_space_arm.txt new file mode 100644 index 00000000..db8572dd --- /dev/null +++ b/boron/address_space_arm.txt @@ -0,0 +1,62 @@ +BORON Operating System Address Space (i386) + +** Kernel Mode ** + ++------------------------------+ - 0xFFFFFFFF +| virtually linear page tables | ++------------------------------+ - 0xFF800000 +| unused | ++------------------------------+ - 0xF0000000 +| more dynamic pool space | ++------------------------------+ - 0xE0000000 +| unused | ++------------------------------+ - 0xD8000000 +| page frame data base | ++------------------------------+ - 0xD4000000 +| system module DLLs | ++------------------------------+ - 0xD2000000 +| unused | ++------------------------------+ - 0xD1900000 +| map of UART MMIO | ++------------------------------+ - 0xD1800000 +| pool header slab magazines | ++------------------------------+ - 0xD1000000 +| fast mapping in 16MB windows | ++------------------------------+ - 0xD0000000 +| map of first 256 MB of phys | +| kernel code & data | ++------------------------------+ - 0xC0000000 +| dynamic pool space | ++------------------------------+ - 0x80000000 +| higher half direct map | ++------------------------------+ - 0x00000000 + +Notes: +- if we use the multiboot boot protocol, then "kernel code & data" really means "first 8MB of physical memory" + +** User Mode ** + +Currently this is nothing more than a plan. +Now, a user process has complete control over its own +address space. (They will even be able to unmap the +PEB/TEBs if they want as well!) + +However, this is the plan for normal user processes. + ++-----------------------------+ - 0x80000000 +| process environment block | ++-----------------------------+ - 0x7ffe0000 +| thread environment blocks | ++-----------------------------+ - 0x7fe00000 +| operating system DLLs | ++-----------------------------+ - 0x78000000 +| thread stacks | ++-----------------------------+ - 0x70000000 +| user DLLs | ++-----------------------------+ - 0x60000000 +| user heap + mappings | ++-----------------------------+ - 0x10000000 +| program executable | ++-----------------------------+ - 0x00001000 +| | ++-----------------------------+ - 0x00000000 diff --git a/boron/include/arch.h b/boron/include/arch.h index 3285cb55..7d337717 100644 --- a/boron/include/arch.h +++ b/boron/include/arch.h @@ -8,6 +8,10 @@ #include #elif defined TARGET_I386 #include +#elif defined TARGET_ARM +#include +#else +#error Define your architecture here! #endif // ==== Forward declarations. Depending on the platform, we'll include platform specific definitions. ==== @@ -15,38 +19,15 @@ typedef struct KREGISTERS_tag KREGISTERS, *PKREGISTERS; // List of registers. // Functions that do different things based on architecture, // but exist everywhere -#if defined TARGET_AMD64 || defined TARGET_I386 - -FORCE_INLINE -void KeWaitForNextInterrupt(void) -{ - ASM("hlt":::"memory"); -} - -FORCE_INLINE -void KeSpinningHint(void) -{ - ASM("pause":::"memory"); -} - -FORCE_INLINE -void KeInvalidatePage(void* Address) -{ - ASM("invlpg (%0)"::"r"((uintptr_t)Address):"memory"); -} - -#else - void KeWaitForNextInterrupt(void); void KeSpinningHint(void); void KeInvalidatePage(void* Page); - -#endif - void KeSetCPUPointer(void* CpuPointer); void* KeGetCPUPointer(void); uintptr_t KeGetCurrentPageTable(void); void KeFlushTLB(void); +void KeSweepIcache(void); +void KeSweepDcache(void); void KeSetCurrentPageTable(uintptr_t PageTable); bool KeDisableInterrupts(); // returns old state void KeRestoreInterrupts(bool OldState); diff --git a/boron/include/arch/amd64.h b/boron/include/arch/amd64.h index 5e41e362..2a1f6ef8 100644 --- a/boron/include/arch/amd64.h +++ b/boron/include/arch/amd64.h @@ -100,18 +100,17 @@ typedef uint64_t MMPTE_HW, *PMMPTE_HW; #define MM_PTE_READWRITE (1ULL << 1) #define MM_PTE_USERACCESS (1ULL << 2) #define MM_PTE_WRITETHRU (1ULL << 3) -#define MM_PTE_CDISABLE (1ULL << 4) +#define MM_PTE_NOCACHE (1ULL << 4) #define MM_PTE_ACCESSED (1ULL << 5) #define MM_PTE_DIRTY (1ULL << 6) #define MM_PTE_PAT (1ULL << 7) #define MM_PTE_PAGESIZE (1ULL << 7) // in terms of PML3/PML2 entries, for 1GB/2MB pages respectively. Not Used by the kernel #define MM_PTE_GLOBAL (1ULL << 8) // doesn't invalidate the pages from the TLB when CR3 is changed #define MM_PTE_ISFROMPMM (1ULL << 9) // if the allocated memory is managed by the PFN database -#define MM_PTE_COW (1ULL << 10) // if this page is to be copied after a write -- TODO: We are supposed to be phasing this one out. +#define MM_PTE_COW (1ULL << 10) // if this page is to be copied after a write (UNUSED) #define MM_PTE_TRANSITION (1ULL << 11) // if this page is in transition (3) (UNUSED) #define MM_PTE_NOEXEC (1ULL << 63) // aka eXecute Disable #define MM_PTE_PKMASK (15ULL<< 59) // protection key mask. We will not use it. -#define MM_PTE_ISPOOLHDR (1ULL << 58) // if the PTE actually contains the address of a pool entry (subtracted MM_KERNEL_SPACE_BASE from it) (NOTE: This is supposed to be MM_DPTE_ISPOOLHDR) #define MM_PTE_ADDRESSMASK (0x000FFFFFFFFFF000) // description of the other bits that aren't 1 in the mask: // 63 - execute disable @@ -119,6 +118,10 @@ typedef uint64_t MMPTE_HW, *PMMPTE_HW; // 58..52 - more available bits #define MM_PTE_PFN(Pte) (((Pte) & MM_PTE_ADDRESSMASK) / PAGE_SIZE) +#define MM_PTE_NEWPFN(Pfn) (((Pfn) * PAGE_SIZE) & MM_PTE_ADDRESSMASK) + +#define MM_PTE_CHECKFROMPMM(Pte) ((Pte) & MM_PTE_ISFROMPMM) +#define MM_PTE_ISPRESENT(Pte) (((Pte) & MM_PTE_PRESENT) != 0) // Disabled PTE (present bit is zero): // bits 0..2 and 63 - Permission bits as usual @@ -141,6 +144,7 @@ typedef uint64_t MMPTE_HW, *PMMPTE_HW; #define MM_DPTE_COMMITTED (1ULL << 8) #define MM_DPTE_BACKEDBYFILE (1ULL << 9) #define MM_DPTE_SWAPPED (1ULL << 10) +#define MM_DPTE_ISPOOLHDR (1ULL << 58) // if the PTE actually contains the address of a pool entry (subtracted MM_KERNEL_SPACE_BASE from it) #define MM_DPTE_WASPRESENT (1ULL << 62) #endif @@ -326,4 +330,22 @@ KARCH_DATA, *PKARCH_DATA; #include #include +FORCE_INLINE +void KeWaitForNextInterrupt(void) +{ + ASM("hlt":::"memory"); +} + +FORCE_INLINE +void KeSpinningHint(void) +{ + ASM("pause":::"memory"); +} + +FORCE_INLINE +void KeInvalidatePage(void* Address) +{ + ASM("invlpg (%0)"::"r"((uintptr_t)Address):"memory"); +} + #endif//NS64_ARCH_AMD64_H diff --git a/boron/include/arch/arm.h b/boron/include/arch/arm.h new file mode 100644 index 00000000..99f002ef --- /dev/null +++ b/boron/include/arch/arm.h @@ -0,0 +1,308 @@ +#pragma once + +#ifndef TARGET_ARM +#error "Don't include this if you aren't building for armv6/armv7!" +#endif + +#include + +#ifdef KERNEL + +// start PML2 index will be 512. The PFN database's is 776 +#define MI_GLOBAL_AREA_START (512) +#define MI_GLOBAL_AREA_START_2ND (896) + +#define MI_RECURSIVE_PAGING_START (1023) + +#define MI_PML2_LOCATION ((uintptr_t)0xFF800000U) // Debbie (L2 page tables) +#define MI_PML1_LOCATION ((uintptr_t)0xFFC00000U) // Jibbie (L1 page table + Jibbie + Debbie) + +#define MI_PML1_MIRROR_LOCATION ((uintptr_t)(0xFFC04000U)) +#define MI_PML2_MIRROR_LOCATION ((uintptr_t)(0xFFC05000U)) + +#define MI_PML_ADDRMASK ((uintptr_t)0xFFFFF000U) + +#endif // KERNEL + +// MmGetHHDMOffsetAddr and other HHDM-related calls are implemented two-fold: +// +// - The first 256 MB of RAM are mapped in an offset identity mapping +// +// - The rest of the address space is accessible via a 16 MB window fast mapping. +#define MI_IDENTMAP_START ((uintptr_t) 0xC0000000) +#define MI_IDENTMAP_SIZE ((uintptr_t) 0x10000000) + +#define MI_IDENTMAP_START_PHYS ((uintptr_t) 0x00000000) + +#define MI_FASTMAP_START ((uintptr_t) 0xD0000000) +#define MI_FASTMAP_MASK ((uintptr_t) 0xFFFF0000) +#define MI_FASTMAP_SIZE (64 * 1024) + +typedef union +{ + // the ARMARM mentions L1 as being the top level. + // i386.h has them swapped, so keep that in mind! + struct + { + uintptr_t PageOffset : 12; + uintptr_t Level2Index : 8; + uintptr_t Level1Index : 12; + }; + + uintptr_t Long; +} +MMADDRESS_CONVERT; + +#define MM_KERNEL_SPACE_BASE (0x80000000U) +#define MM_USER_SPACE_END (0x7FFFFFFFU) + +#define MM_PFNDB_BASE (0xD4000000U) + +// -- L1 PTEs -- +#define MM_ARM_PTEL1_TYPE (3U << 0) +#define MM_ARM_PTEL1_COARSE_PAGE_TABLE (1U << 0) // Type = b01, Coarse Page Table +#define MM_ARM_PTEL1_SECTION_SETUP ((3U << 2) | (7U << 12) | (1U << 10) | (2U << 0)) // CB = 0b11, TEX = 0b111, AP = 0b01, APX=0, Type = 0b10 + +// -- L2 PTEs -- + +#ifdef TARGET_ARMV5 + +// ARMv5 first level PTEs aren't different, but second level PTEs are. +#define MM_ARM_PTEL2_B (1 << 2) +#define MM_ARM_PTEL2_C (1 << 3) + +#define MM_ARM_PTEL2_AP_NOACCESS ((0U << 4)) // super N/A, user N/A +#define MM_ARM_PTEL2_AP_SUPERREADWRITE ((1U << 4)) // super R/W, user N/A +#define MM_ARM_PTEL2_AP_USERREADONLY ((2U << 4)) // super R/W, user R/O +#define MM_ARM_PTEL2_AP_USERREADWRITE ((3U << 4)) // super R/W, user R/W + +#define MM_ARM_PTEL2_AP_ALL(x) ((x) | ((x) << 2) | ((x) << 4) | ((x) << 6)) +#define MM_ARM_PTEL2_AP_ALL_NOACCESS (0) +#define MM_ARM_PTEL2_AP_ALL_SUPERREADWRITE MM_ARM_PTEL2_AP_ALL(MM_ARM_PTEL2_AP_SUPERREADWRITE) +#define MM_ARM_PTEL2_AP_ALL_USERREADONLY MM_ARM_PTEL2_AP_ALL(MM_ARM_PTEL2_AP_USERREADONLY) +#define MM_ARM_PTEL2_AP_ALL_USERREADWRITE MM_ARM_PTEL2_AP_ALL(MM_ARM_PTEL2_AP_USERREADWRITE) + +#define MM_ARM_AP_MASK MM_ARM_PTEL2_AP_ALL(3U) + +#define MM_ARM_PTEL2_TYPE_TRANSFAULT (0U << 0) +#define MM_ARM_PTEL2_TYPE_LARGEPAGE (1U << 0) +#define MM_ARM_PTEL2_TYPE_SMALLPAGE (2U << 0) +#define MM_ARM_PTEL2_TYPE_EXSMALLPAGE (3U << 0) + +#else // ARMv6 + +// AP = PTE[5:4], APX = PTE[9] +#define MM_ARM_PTEL2_AP_NOACCESS ((0U << 4)) // super N/A, user N/A +#define MM_ARM_PTEL2_AP_SUPERREADWRITE ((1U << 4)) // super R/W, user N/A +#define MM_ARM_PTEL2_AP_USERREADONLY ((2U << 4)) // super R/W, user R/O +#define MM_ARM_PTEL2_AP_USERREADWRITE ((3U << 4)) // super R/W, user R/W +#define MM_ARM_PTEL2_AP_SUPERREADONLY ((1U << 4) | (1U << 9)) // super R/O, user N/A +#define MM_ARM_PTEL2_AP_BOTHREADONLY ((3U << 4) | (1U << 9)) // super R/O, user R/O + +#define MM_ARM_AP_MASK ((1U << 9) | (3U << 4)) + +// TEX = PTE[8:6], C = PTE[3], B = PTE[2] +#define MM_ARM_PTEL2_TEXCB_STRONGORDER ((0U << 6) | (0U << 2)) +#define MM_ARM_PTEL2_TEXCB_SHAREDDEVICE ((0U << 6) | (1U << 2)) +#define MM_ARM_PTEL2_TEXCB_CACHEABLE ((7U << 6) | (3U << 2)) // b11 inner and outer policy. +#define MM_ARM_PTEL2_TEXCB_NORMALMEM ((1U << 6) | (3U << 2)) // TEX=0b001, CB=0b11 + +#define MM_ARM_PTEL2_TEX_MASK (7U << 6) +#define MM_ARM_PTEL2_CB_MASK (3U << 2) + +// Type +#define MM_ARM_PTEL2_TYPE_TRANSFAULT (0U << 0) +#define MM_ARM_PTEL2_TYPE_LARGEPAGE (1U << 0) +#define MM_ARM_PTEL2_TYPE_SMALLPAGE (2U << 0) +#define MM_ARM_PTEL2_TYPE_SMALLPAGENX (3U << 0) + +#endif + +// Disabled PTEs +// bits 9 and 5:4 - Permission bits as usual +// bit 2 - Is pool header +// bit 3 - Is committed +// bit 6 - Was present +// bit 7 - Is decommitted (was previously committed but is no longer) + +#define MM_ARM_DPTE_ISPOOLHDR (1U << 2) +#define MM_ARM_DPTE_COMMITTED (1U << 3) +#define MM_ARM_DPTE_WASPRESENT (1U << 6) +#define MM_ARM_DPTE_DECOMMITTED (1U << 7) + +// TODO: just assumes all of them are from PMM +#define MM_ARM_PTE_CHECKFROMPMM(Pte) (true) + +#define MM_ARM_PTE_PFN(Pte) (((Pte) >> 12) & 0xFFFFF) +#define MM_ARM_PTE_NEWPFN(Pfn) ((Pfn) << 12) +#define MM_ARM_PTE_ADDRESSMASK (0xFFFFF000U) + +// for the DFSR/IFSR, we currently only care about whether the page fault +// was due to a write or not. +#define MM_FAULT_WRITE (1U << 11) // RW bit +#define MM_FAULT_INSNFETCH (1U << 31) // fake bit we append. NOT part of IFSR/DFSR. + +// strange sizes in here! +#define MM_L1PT_SIZE (0x4000) // 16K +#define MM_L2PT_SIZE (0x0400) // 1K + +#define PAGE_SIZE (0x1000) // 4K + +typedef uint32_t MMPTE_HW, *PMMPTE_HW; + +#define PT_L1_IDX(addr) (((addr) >> 20) & 0xFFF) +#define PT_L2_IDX(addr) (((addr) >> 12) & 0xFF) + +/* +// This struct represents all of the available registers at a time. +// +// User mode (EL0) has access to these registers. Interrupt handlers and +// the system may use different registers entirely. For example, FIQ mode +// replaces R8-R14 with its own set of registers using the ARM banking +// mechanism, namely R8_fiq - R14_fiq. This way, most FIQ handlers needn't +// even save any registers. +// The supervisor, abort, IRQ, and undefined modes each have a private copy +// of R13 (SP) and R14 (LR) as well (e.g. supervisor mode uses R13_svc and +// R14_svc) +struct KREGISTERS_tag +{ + uint32_t R0, R1, R2, R3; + uint32_t R4, R5, R6, R7; + uint32_t R8, R9, R10, R11; + uint32_t R12, R13; + + union { + struct { + uint32_t R14, R15; + }; + struct { + uint32_t Lr, Pc; + }; + }; + + uint32_t Cpsr; + uint32_t IntNumber; +}; +*/ + +// This restrained struct represents only the registers saved in each IRQ handler. +struct KREGISTERS_tag +{ + uint32_t Lr_Svc; + uint32_t Cpsr; + uint32_t R0; + uint32_t R1; + uint32_t R2; + uint32_t R3; + uint32_t R4; + uint32_t R5; + uint32_t R6; + uint32_t R7; + uint32_t R8; + uint32_t R9; + uint32_t R10; + uint32_t R11; + uint32_t R12; + uint32_t Lr; +}; + +typedef struct +{ + int Dummy; +} +KTHREAD_ARCH_CONTEXT; + +typedef struct +{ + int Dummy; +} +KARCH_DATA, *PKARCH_DATA; + +#ifdef TARGET_ARMV5 + +#define DISABLE_INTERRUPTS() ASM(\ + "mrs r12, cpsr\n" \ + "orr r12, r12, #0x80\n" \ + "msr cpsr_c, r12\n" ::: "r12", "memory" \ +) +#define ENABLE_INTERRUPTS() ASM(\ + "mrs r12, cpsr\n" \ + "bic r12, r12, #0x80\n" \ + "msr cpsr_c, r12\n" ::: "r12", "memory" \ +) + +#else + +#define DISABLE_INTERRUPTS() ASM("cpsid i" ::: "memory"); +#define ENABLE_INTERRUPTS() ASM("cpsie i" ::: "memory"); + +#endif + +FORCE_INLINE +void KeWaitForNextInterrupt() +{ +#ifdef TARGET_ARMV5 + unsigned int zero = 0; + ASM("mcr p15, 0, %0, c7, c0, 4" : : "r" (zero) : "memory"); +#else + ASM("wfi":::"memory"); +#endif +} + +FORCE_INLINE +void KeSpinningHint() +{ + ASM("nop":::"memory"); +} + +FORCE_INLINE +void KeInvalidatePage(void* Address) { + //uint32_t Zero = 0; + //ASM( + // "mcr p15, 0, %1, c7, c10, 4\n" // data synchronization barrier (DSB) + // "mcr p15, 0, %0, c8, c7, 1\n" + // "mcr p15, 0, %1, c7, c10, 4\n" // DSB + // "mcr p15, 0, %1, c7, c5, 4\n" // flush prefetch buffer + // : + // : "r"(Address), "r"(Zero) + // : "memory" + //); + (void) Address; + void KeFlushTLB(void); + KeFlushTLB(); +} + +FORCE_INLINE +uint32_t KiReadIfsr() { + uint32_t val; + ASM("mrc p15, 0, %0, c5, c0, 1" : "=r"(val)); + return val; +} + +FORCE_INLINE +uint32_t KiReadIfar() { + uint32_t val; + ASM("mrc p15, 0, %0, c6, c0, 2" : "=r"(val)); + return val; +} + +FORCE_INLINE +uint32_t KiReadDfsr() { + uint32_t val; + ASM("mrc p15, 0, %0, c5, c0, 0" : "=r"(val)); + return val; +} + +FORCE_INLINE +uint32_t KiReadDfar() { + uint32_t val; + ASM("mrc p15, 0, %0, c6, c0, 0" : "=r"(val)); + return val; +} + +static_assert(sizeof(int) == 4); + +// Special trap numbers: +#define TRAP_CODE_PREFETCH_ABORT (0x20) // Prefetch Abort means page fault fetching an instruction +#define TRAP_CODE_DATA_ABORT (0x21) // Data Abort means page fault fetching data or writing to data +#define TRAP_CODE_SYSTEM_SERVICE (0x22) // System Service (user mode ran "svc") \ No newline at end of file diff --git a/boron/include/arch/arm/ipl.h b/boron/include/arch/arm/ipl.h new file mode 100644 index 00000000..f1b5f05f --- /dev/null +++ b/boron/include/arch/arm/ipl.h @@ -0,0 +1,40 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + arch/arm/ipl.h + +Abstract: + This header file contains the constant IPL definitions + for the arm platform. + +Author: + iProgramInCpp - 24 December 2025 +***/ +#ifndef BORON_ARCH_IPL_ARM_H +#define BORON_ARCH_IPL_ARM_H + +// TODO: Currently copied from i386. +typedef enum KIPL_tag +{ + IPL_UNDEFINED = -1, + IPL_NORMAL = 0x0, // business as usual + IPL_APC = 0x3, // asynch procedure calls. Page faults only allowed up to this IPL + IPL_DPC = 0x4, // deferred procedure calls and the scheduler + IPL_DEVICES0 = 0x5, // tier 1 for devices (keyboard, mouse) + IPL_DEVICES1 = 0x6, // tier 2 for devices + IPL_DEVICES2 = 0x7, + IPL_DEVICES3 = 0x8, + IPL_DEVICES4 = 0x9, + IPL_DEVICES5 = 0xA, + IPL_DEVICES6 = 0xB, + IPL_DEVICES7 = 0xC, + IPL_DEVICES8 = 0xD, + IPL_CLOCK = 0xE, // for clock timers + IPL_NOINTS = 0xF, // total control of the CPU. Interrupts are disabled in this IPL and this IPL only. + IPL_COUNT, +} +KIPL, *PKIPL; + +#endif//BORON_ARCH_IPL_ARM_H diff --git a/boron/include/arch/i386.h b/boron/include/arch/i386.h index 09258c46..3b228457 100644 --- a/boron/include/arch/i386.h +++ b/boron/include/arch/i386.h @@ -318,3 +318,21 @@ KARCH_DATA, *PKARCH_DATA; #include #include #include + +FORCE_INLINE +void KeWaitForNextInterrupt(void) +{ + ASM("hlt":::"memory"); +} + +FORCE_INLINE +void KeSpinningHint(void) +{ + ASM("pause":::"memory"); +} + +FORCE_INLINE +void KeInvalidatePage(void* Address) +{ + ASM("invlpg (%0)"::"r"((uintptr_t)Address):"memory"); +} diff --git a/boron/include/arch/ipl.h b/boron/include/arch/ipl.h index cd7a7f87..a58b7256 100644 --- a/boron/include/arch/ipl.h +++ b/boron/include/arch/ipl.h @@ -19,6 +19,8 @@ Module name: #include #elif defined TARGET_I386 #include +#elif defined TARGET_ARM +#include #else #error Implement ipl.h for your architecture! #endif diff --git a/boron/include/except.h b/boron/include/except.h index 1a67add9..3ec0cbc2 100644 --- a/boron/include/except.h +++ b/boron/include/except.h @@ -7,6 +7,7 @@ void KeOnUnknownInterrupt(PKREGISTERS); void KeOnDoubleFault(PKREGISTERS); void KeOnProtectionFault(PKREGISTERS); +void KeOnUndefinedInstruction(PKREGISTERS); void KeOnPageFault(PKREGISTERS); #endif//NS64_EXCEPT_H diff --git a/boron/include/hal.h b/boron/include/hal.h index 9588f27d..09afde2c 100644 --- a/boron/include/hal.h +++ b/boron/include/hal.h @@ -21,6 +21,7 @@ void HalRequestIpi(uint32_t LapicId, uint32_t Flags, int Vector); void HalInitSystemUP(); void HalInitSystemMP(); void HalDisplayString(const char* Message); +void HalDisplayString2(const char* Message); void HalCrashSystem(const char* Message); bool HalUseOneShotIntTimer(); void HalProcessorCrashed() NO_RETURN; @@ -35,6 +36,14 @@ void HalIoApicSetIrqRedirect(uint8_t Vector, uint8_t Irq, uint32_t LapicId, bool void HalPicRegisterInterrupt(uint8_t Vector, KIPL Ipl); void HalPicDeregisterInterrupt(uint8_t Vector, KIPL Ipl); #endif +#ifdef TARGET_ARM +int HalGetMaximumInterruptCount(); +void HalOnUpdateIpl(KIPL NewIpl, KIPL OldIpl); +void HalVicRegisterInterrupt(int Vector, KIPL Ipl); +void HalVicDeregisterInterrupt(int Vector, KIPL Ipl); +PKREGISTERS HalOnInterruptRequest(PKREGISTERS Registers); +PKREGISTERS HalOnFastInterruptRequest(PKREGISTERS Registers); +#endif #ifdef IS_HAL void HalSetVftable(const HAL_VFTABLE* Table); diff --git a/boron/include/hal/data.h b/boron/include/hal/data.h index 39c87833..43637e7b 100644 --- a/boron/include/hal/data.h +++ b/boron/include/hal/data.h @@ -18,9 +18,13 @@ Module name: // HAL Control Block typedef struct KHALCB_tag { +#if defined TARGET_AMD64 || defined TARGET_I386 // LAPIC and TSC frequencies, in ticks/ms. uint64_t LapicFrequency; uint64_t TscFrequency; +#else + int Dummy; +#endif } KHALCB, *PKHALCB; diff --git a/boron/include/hal/init.h b/boron/include/hal/init.h index 4454e2db..86fbd503 100644 --- a/boron/include/hal/init.h +++ b/boron/include/hal/init.h @@ -44,6 +44,15 @@ typedef void(*PFHAL_PIC_REGISTER_INTERRUPT)(uint8_t Vector, KIPL Ipl); typedef void(*PFHAL_PIC_DEREGISTER_INTERRUPT)(uint8_t Vector, KIPL Ipl); #endif +#ifdef TARGET_ARM +typedef int(*PFHAL_GET_MAXIMUM_INTERRUPT_COUNT)(void); +typedef void(*PFHAL_ON_UPDATE_IPL)(KIPL NewIpl, KIPL OldIpl); +typedef void(*PFHAL_VIC_REGISTER_INTERRUPT)(int Vector, KIPL Ipl); +typedef void(*PFHAL_VIC_DEREGISTER_INTERRUPT)(int Vector, KIPL Ipl); +typedef PKREGISTERS(*PFHAL_ON_INTERRUPT_REQUEST)(PKREGISTERS); +typedef PKREGISTERS(*PFHAL_ON_FAST_INTERRUPT_REQUEST)(PKREGISTERS); +#endif + #if defined TARGET_AMD64 || defined TARGET_I386 #include "pci.h" #endif @@ -87,6 +96,14 @@ typedef struct PFHAL_PCI_READ_BAR_ADDRESS PciReadBarAddress; PFHAL_PCI_READ_BAR_IO_ADDRESS PciReadBarIoAddress; #endif +#ifdef TARGET_ARM + PFHAL_GET_MAXIMUM_INTERRUPT_COUNT GetMaximumInterruptCount; + PFHAL_ON_UPDATE_IPL OnUpdateIpl; + PFHAL_VIC_REGISTER_INTERRUPT VicRegisterInterrupt; + PFHAL_VIC_DEREGISTER_INTERRUPT VicDeregisterInterrupt; + PFHAL_ON_INTERRUPT_REQUEST OnInterruptRequest; + PFHAL_ON_FAST_INTERRUPT_REQUEST OnFastInterruptRequest; +#endif } HAL_VFTABLE, *PHAL_VFTABLE; diff --git a/boron/include/ke/dpc.h b/boron/include/ke/dpc.h index d13cc60f..0177b2ac 100644 --- a/boron/include/ke/dpc.h +++ b/boron/include/ke/dpc.h @@ -16,7 +16,6 @@ Module name: #define BORON_KE_DPC_H #include -#include <_limine.h> typedef struct KDPC_tag KDPC, *PKDPC; diff --git a/boron/include/ke/int.h b/boron/include/ke/int.h index 86cde364..07242d12 100644 --- a/boron/include/ke/int.h +++ b/boron/include/ke/int.h @@ -120,3 +120,9 @@ int KeSynchronizeExecution( PKSYNCHRONIZE_ROUTINE Routine, void* SynchronizeContext ); + +#if defined KERNEL || defined IS_HAL + +void KeDispatchInterruptRequest(int Number); + +#endif diff --git a/boron/include/ke/locks.h b/boron/include/ke/locks.h index 161a7d97..f79dccef 100644 --- a/boron/include/ke/locks.h +++ b/boron/include/ke/locks.h @@ -17,12 +17,19 @@ Module name: #include +#define SPINLOCK_TRACK_PC + // simple spin locks, for when contention is rare typedef struct { - bool Locked; + uint32_t Locked; + #if defined(DEBUG) && defined(SPINLOCK_TRACK_PC) +#ifdef IS_64_BIT uint64_t Pc : 48; // Locking program counter - debug only +#else + uintptr_t Pc; +#endif #endif } KSPIN_LOCK, *PKSPIN_LOCK; diff --git a/boron/include/ke/prcb.h b/boron/include/ke/prcb.h index f31f8c4b..2048791d 100644 --- a/boron/include/ke/prcb.h +++ b/boron/include/ke/prcb.h @@ -16,8 +16,6 @@ Module name: #define BORON_KE_PRCB_H #include -#include <_limine.h> - #include #include diff --git a/boron/include/ke/sched.h b/boron/include/ke/sched.h index d279eaab..0981e74b 100644 --- a/boron/include/ke/sched.h +++ b/boron/include/ke/sched.h @@ -88,4 +88,8 @@ NO_RETURN void KeSchedulerCommit(); void KeTimerTick(); +#if defined KERNEL || defined IS_HAL +void KeDispatchPendingSoftInterrupts(); +#endif + #endif//BORON_KE_SCHED_H diff --git a/boron/include/ke/thread.h b/boron/include/ke/thread.h index 6e7e0faa..5c4b79dd 100644 --- a/boron/include/ke/thread.h +++ b/boron/include/ke/thread.h @@ -69,6 +69,11 @@ struct KTHREAD_tag KTHREAD_STACK Stack; +#ifdef TARGET_ARM + void* InterruptStack; + uintptr_t AbtStack, UndStack, IrqStack, FiqStack; +#endif + void* StackPointer; // Pass this into KiSwitchThreadStack. int WaitType; @@ -169,8 +174,27 @@ struct KTHREAD_tag // User-space pointer to the TEB (thread environment block). void* TebPointer; + +#ifdef TARGET_ARM + // ARM implements instruction page faults and data page faults in + // separate ways, so tell them apart using this flag. + bool HandlingInstructionFault; +#endif }; +#ifdef TARGET_ARM + +// add another page to kernel stack sizes on ARM, because we're appending +// interrupt stacks to the end of the normal kernel stack +#define KERNEL_STACK_SIZE (PAGE_SIZE * 3) +#define KERNEL_INTERRUPT_STACK_SIZE (PAGE_SIZE) + +#else + +#define KERNEL_STACK_SIZE (PAGE_SIZE * 2) + +#endif + // Creates an empty, uninitialized, thread object. // TODO Use the object manager for this purpose and expose the thread object there. PKTHREAD KeAllocateThread(); @@ -205,9 +229,14 @@ void KeTerminateThread2(PKTHREAD Thread, KPRIORITY Increment); void KeSetSuspendedThread(PKTHREAD Thread, bool IsSuspended); // Switch this thread into user mode. -#ifdef TARGET_I386 +#if defined TARGET_I386 || defined TARGET_ARM -// You must pass UserContext in another way. +// On i386, you must pass UserContext (parameter to the entry point) in another way, +// such as placing it onto the user stack. Only ReturnCode can be provided from here. +// +// On armv6, UserContext and ReturnCode would share the same register, so there is one +// unified parameter. This is fine, since the kernel never tries to provide both at +// the same time. NO_RETURN void KeDescendIntoUserMode(void* InstructionPointer, void* StackPointer, uintptr_t ReturnCode); #else diff --git a/boron/include/main.h b/boron/include/main.h index 0969b9c7..7e729a12 100644 --- a/boron/include/main.h +++ b/boron/include/main.h @@ -88,9 +88,6 @@ void DbgPrint(const char* msg, ...); #include #include -// TODO: not sure this belongs here, but we'll take it. -#define KERNEL_STACK_SIZE (PAGE_SIZE * 2) // Note: Must be a multiple of PAGE_SIZE. - #ifdef IS_DRIVER // Force the driver entry prototype. diff --git a/boron/include/mm/pmm.h b/boron/include/mm/pmm.h index 93b7c055..5dfa09b4 100644 --- a/boron/include/mm/pmm.h +++ b/boron/include/mm/pmm.h @@ -126,4 +126,14 @@ void MmRegisterMMIOAsMemory(uintptr_t Base, uintptr_t Size); // TODO: Not sure where to place this. Do we really need a new file? void MmInitializeModifiedPageWriter(void); +// Allocates a contiguous memory region with an address alignment. Note that +// this is slow and so it should be called cautiously. Thanks for your stupid +// unconventional page table layout, ARM! Someday I may opt to implement a +// buddy system, but not as of now. +MMPFN MmAllocatePhysicalContiguousRegion(int PageCount, uintptr_t Alignment); + +// Frees a physical contiguous region. It basically frees every PFN between +// PfnStart and PfnStart + PageCount - 1. +void MmFreePhysicalContiguousRegion(MMPFN PfnStart, int PageCount); + #endif//BORON_MM_PMM_H diff --git a/boron/include/mm/pool.h b/boron/include/mm/pool.h index 006429dc..c12e96d8 100644 --- a/boron/include/mm/pool.h +++ b/boron/include/mm/pool.h @@ -63,10 +63,6 @@ void* MmAllocatePool(int PoolFlags, size_t Size); void MmFreePool(void* Pointer); // Shorthand function to allocate a thread's kernel stack using default parameters. -FORCE_INLINE -void* MmAllocateKernelStack() -{ - return MmAllocatePoolBig(POOL_FLAG_NON_PAGED, KERNEL_STACK_SIZE / PAGE_SIZE, POOL_TAG("ThSt")); -} +void* MmAllocateKernelStack(); #define MmFreeThreadStack(p) MmFreePoolBig(p) diff --git a/boron/include/mm/pte.h b/boron/include/mm/pte.h index 6a7c78e9..f27f5e52 100644 --- a/boron/include/mm/pte.h +++ b/boron/include/mm/pte.h @@ -155,3 +155,6 @@ MM_PTE_API bool MmIsEqualPte(MMPTE Pte1, MMPTE Pte2); // Checks if the PTE has unsupported parameters. MM_PTE_API bool MmIsUnsupportedHigherLevelPte(MMPTE Pte); + +// Flushes PTE modifications. +MM_PTE_API void MmFlushTlbUpdates(); diff --git a/boron/linker.arm.ld b/boron/linker.arm.ld new file mode 100644 index 00000000..5401aa72 --- /dev/null +++ b/boron/linker.arm.ld @@ -0,0 +1,68 @@ +ENTRY(KiBeforeSystemStartup) + +PHDRS { + ipldata PT_LOAD FLAGS(4 | 2); /* R | W */ + ipltext PT_LOAD FLAGS(4 | 1); /* R | W */ + text PT_LOAD FLAGS(4 | 1); /* R | X */ + data PT_LOAD FLAGS(4 | 2); /* R | W */ +} + +SECTIONS { + /* change this for other platforms?! or just make the bootloader do this instead + of the kernel? TODO */ + . = 0x00100000; + KiKernelStart = 0xC0000000 + .; + + .iplbss ALIGN (16K) : AT (ADDR(.iplbss)) { + KEEP(*(.iplbss*)) + } :ipldata + + .ipltext ALIGN (4K) : AT (ADDR(.ipltext)) { + __iplbss_start = .; + KEEP(*(.ipltext*)) + __iplbss_end = .; + } :ipltext + + . += 0xC0000000; + + .text ALIGN (4K) : AT (ADDR (.text) - 0xC0000000) { + KiTextInitStart = .; + *(.text.init) + KiTextInitEnd = .; + . = ALIGN(CONSTANT(MAXPAGESIZE)); + + KiTextPageStart = .; + *(.text.page) + KiTextPageEnd = .; + . = ALIGN(CONSTANT(MAXPAGESIZE)); + + *(.text*) + } :text + + .rodata ALIGN (4K) : AT (ADDR (.rodata) - 0xC0000000) { + KiInitArrayStart = .; + *(.init_array .init_array.*) + KiInitArrayEnd = .; + KiFiniArrayStart = .; + *(.fini_array .fini_array.*) + KiFiniArrayEnd = .; + + *(.rodata*) + PROVIDE(KiSymbolTable = .); + PROVIDE(KiSymbolTableEnd = .); + } :text + + .data ALIGN (4K) : AT (ADDR (.data) - 0xC0000000) { + *(.data*) + } :data + + .bss ALIGN (4K) : AT (ADDR (.bss) - 0xC0000000) { + *(COMMON) + *(.bss*) + + /* Hack to keep the PsSystemProcess symbol while adding an object header on top */ + PROVIDE(PsSystemProcess = PspSystemProcessObject + 64); + } :data + + KiKernelEnd = .; +} diff --git a/boron/scripts/generate_symbols_gas.py b/boron/scripts/generate_symbols_gas.py new file mode 100644 index 00000000..f2b6e600 --- /dev/null +++ b/boron/scripts/generate_symbols_gas.py @@ -0,0 +1,92 @@ +#!/usr/bin/python3 +# The Boron Operating System - Copyright (C) 2026 iProgramInCpp +# +# This Python script generates the symbol definitions. +# Note: The input piped into it MUST be the output of `nm` with the `-P` switch. + +import sys + +def SymKey(s): + return s[0] # Return the address member + +if len(sys.argv) < 1: + print('bad usage') + exit() + +if sys.argv[1] == 'arm': + DefineWord = '.word' +elif sys.argv[1] == 'i386': + DefineWord = '.long' +else: + DefineWord = '.quad' + +print(' /*********** The Boron Operating System ***********/') +print(' .section .rodata') +print(' .global KiSymbolTable') +print(' .global KiSymbolTableEnd') +print('KiSymbolTable:') + +Names = " .section .rodata\n" + +SymbolList = [] + +for Line in sys.stdin: + Line = Line.rstrip() + Tokens = Line.split() + + Name = Tokens[0] + Type = Tokens[1] + Address = int(Tokens[2], 16) + + if len(Tokens) < 4: + Size = 1 + else: + Size = int(Tokens[3], 16) + + if Name == '$d' or Name == '$a': + continue + + if Type == 'T' or Type == 't': + SymbolList.append((Address, Size, Name)) + +SymbolList.sort(key=SymKey) + +Count = 0 +for Symbol in SymbolList: + Address = Symbol[0] + Size = Symbol[1] + Name = Symbol[2] + + if Count < len(SymbolList) - 1: + UpdateSize = False + + # Attempt to correct the size of small asm functions + # that aren't aligned to sixteen bytes. Their size is reported + # as bigger than it actually is for some reason + if Address + Size > SymbolList[Count + 1][0]: + UpdateSize = True + + # If the symbol has at most 15 bytes until the next symbol, + # expand the size to include the padding. + # Some no_return functions are missed without this fix. + Thing = (Address + Size + 0xF) & 0xFFFFFFFFFFFFFFF0 + + if Thing == SymbolList[Count + 1][0]: + UpdateSize = True + + # TODO FIXME: If we're KiTrapCommon or siblings, update the size anyway + if Name.startswith('KiTrapCommon'): + UpdateSize = True + + if UpdateSize: + Size = SymbolList[Count + 1][0] - Address + + + print(f' {DefineWord} 0x{Address:x}') + print(f' {DefineWord} 0x{Size:x}') + print(f' {DefineWord} name_{Count}') + Names += f'name_{Count}: .asciz "{Name}"\n' + Count += 1 + +print('KiSymbolTableEnd:') +print(Names) diff --git a/boron/scripts/generate_symbols.py b/boron/scripts/generate_symbols_nasm.py similarity index 100% rename from boron/scripts/generate_symbols.py rename to boron/scripts/generate_symbols_nasm.py diff --git a/boron/source/build_number b/boron/source/build_number index a848267e..a894381f 100644 --- a/boron/source/build_number +++ b/boron/source/build_number @@ -1 +1 @@ -1281 +1721 diff --git a/boron/source/hal/hal.c b/boron/source/hal/hal.c index 32dbf944..f152d4ad 100644 --- a/boron/source/hal/hal.c +++ b/boron/source/hal/hal.c @@ -90,12 +90,13 @@ uint64_t HalGetTickCount() { #ifdef TICK_DEBUG static uint64_t LastTickCount = 0; + + bool Restore = KeDisableInterrupts(); #endif uint64_t TickCount = HalpVftable.GetTickCount(); #ifdef TICK_DEBUG - bool Restore = KeDisableInterrupts(); if (LastTickCount > TickCount) { KeCrash( @@ -197,6 +198,40 @@ uintptr_t HalPciReadBarAddress(PPCI_ADDRESS Address, int BarIndex) } #endif +#ifdef TARGET_ARM + +int HalGetMaximumInterruptCount() +{ + return HalpVftable.GetMaximumInterruptCount(); +} + +void HalOnUpdateIpl(KIPL NewIpl, KIPL OldIpl) +{ + HalpVftable.OnUpdateIpl(NewIpl, OldIpl); +} + +void HalVicRegisterInterrupt(int Vector, KIPL Ipl) +{ + HalpVftable.VicRegisterInterrupt(Vector, Ipl); +} + +void HalVicDeregisterInterrupt(int Vector, KIPL Ipl) +{ + HalpVftable.VicDeregisterInterrupt(Vector, Ipl); +} + +PKREGISTERS HalOnInterruptRequest(PKREGISTERS Registers) +{ + return HalpVftable.OnInterruptRequest(Registers); +} + +PKREGISTERS HalOnFastInterruptRequest(PKREGISTERS Registers) +{ + return HalpVftable.OnFastInterruptRequest(Registers); +} + +#endif + NO_RETURN void HalProcessorCrashed() { HalpVftable.ProcessorCrashed(); diff --git a/boron/source/ke/amd64/debug.c b/boron/source/ke/amd64/debug.c index 4f0b0c9a..3680b0a1 100644 --- a/boron/source/ke/amd64/debug.c +++ b/boron/source/ke/amd64/debug.c @@ -3,7 +3,7 @@ Copyright (C) 2023 iProgramInCpp Module name: - ke/amd64/debug.c + ke/amd64/debug.c Abstract: This module implements architecture specific debugging @@ -111,6 +111,12 @@ void DbgPrintDouble(const char* String) HalDisplayString(String); } +void DbgDumpPageTables() +{ + // TODO + DbgPrint("You really need this? Add it. It's supposed to dump the current page table."); +} + void DbgPrintStackTrace(uintptr_t Rbp) { if (Rbp == 0) diff --git a/boron/source/ke/amd64/traps.c b/boron/source/ke/amd64/traps.c index 94f9cf3c..abd0de32 100644 --- a/boron/source/ke/amd64/traps.c +++ b/boron/source/ke/amd64/traps.c @@ -183,6 +183,12 @@ PKREGISTERS KiHandleProtectionFault(PKREGISTERS Regs) return Regs; } +PKREGISTERS KiHandleUndefinedInstructionFault(PKREGISTERS Regs) +{ + KeOnUndefinedInstruction(Regs); + return Regs; +} + PKREGISTERS KiHandlePageFault(PKREGISTERS Regs) { KeOnPageFault(Regs); diff --git a/boron/source/ke/arm/boot.c b/boron/source/ke/arm/boot.c new file mode 100644 index 00000000..822fb751 --- /dev/null +++ b/boron/source/ke/arm/boot.c @@ -0,0 +1,57 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + ke/armv6/boot.c + +Abstract: + This module contains the bootstrap code that converts + bootloader parameter data into kernel specific definitions. + +Author: + iProgramInCpp - 26 December 2025 +***/ +#include "../ki.h" +#include "../../mm/mi.h" + +INIT void KeMarkCrashedAp(UNUSED uint32_t ProcessorIndex) {} +INIT void KeJumpstartAp(UNUSED uint32_t ProcessorIndex) {} + +extern char KiKernelStart[], KiKernelEnd[], KiMemoryStart[]; + +LOADER_PARAMETER_BLOCK KeLoaderParameterBlock; + +extern PLOADER_PARAMETER_BLOCK KiBootloaderLpb; + +#define P2V(Ptr) ((Ptr) ? (void*)((uintptr_t)(Ptr) + 0xC0000000) : NULL) +#define FIXUP(Addr) ((Addr) = P2V(Addr)) + +INIT +void KiInitLoaderParameterBlock() +{ + MiInitializeBaseIdentityMapping(); + + PLOADER_PARAMETER_BLOCK LpbSrc = P2V(KiBootloaderLpb); + KeLoaderParameterBlock = *LpbSrc; + + // Fix up certain addresses to be virtual. + PLOADER_PARAMETER_BLOCK Lpb = &KeLoaderParameterBlock; + FIXUP(Lpb->MemoryRegions); + FIXUP(Lpb->Framebuffers); + FIXUP(Lpb->CommandLine); + FIXUP(Lpb->LoaderInfo.Name); + FIXUP(Lpb->LoaderInfo.Version); + FIXUP(Lpb->ModuleInfo.List); + FIXUP(Lpb->ModuleInfo.Kernel.Path); + FIXUP(Lpb->ModuleInfo.Kernel.String); + FIXUP(Lpb->ModuleInfo.Kernel.Address); + FIXUP(Lpb->Multiprocessor.List); + + for (size_t i = 0; i < Lpb->ModuleInfo.Count; i++) + { + FIXUP(Lpb->ModuleInfo.List[i].Path); + FIXUP(Lpb->ModuleInfo.List[i].String); + FIXUP(Lpb->ModuleInfo.List[i].Address); + } +} diff --git a/boron/source/ke/arm/cpu.c b/boron/source/ke/arm/cpu.c new file mode 100644 index 00000000..4dbeed52 --- /dev/null +++ b/boron/source/ke/arm/cpu.c @@ -0,0 +1,43 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + ke/armv6/cpu.c + +Abstract: + This module implements certain utility functions, + as well as certain parts of UP initialization code. + +Author: + iProgramInCpp - 28 December 2025 +***/ +#include +#include +#include +#include +#include "../../ke/ki.h" + +#ifdef CONFIG_SMP +#error SMP not supported for this platform! +#endif + +static void* KiCpuPointer; + +void* KeGetCPUPointer() +{ + return KiCpuPointer; +} + +void KeSetCPUPointer(void* Ptr) +{ + KiCpuPointer = Ptr; +} + +INIT +void KeInitCPU() +{ + KiSwitchToAddressSpaceProcess(KeGetSystemProcess()); + + // TODO +} diff --git a/boron/source/ke/arm/debug.c b/boron/source/ke/arm/debug.c new file mode 100644 index 00000000..b97853b7 --- /dev/null +++ b/boron/source/ke/arm/debug.c @@ -0,0 +1,257 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + ke/arm/debug.c + +Abstract: + This module implements architecture specific debugging + routines. + +Author: + iProgramInCpp - 26 December 2025 +***/ +#include + +// TODO: do not hardcode the UART's location in the kernel. + +//#define USE_PL011 +#define USE_EXYNOS4210 + +void DbgPrintDouble(const char* String) +{ + DbgPrintString(String); + HalDisplayString(String); +} + +void DbgPrintStackTrace(uintptr_t Rbp) +{ + (void) Rbp; + + DbgPrintDouble("TODO: DbgPrintStackTrace\n"); +} + +void DbgDumpPageTables() +{ + uintptr_t Ttbr0 = (uintptr_t) KeGetCurrentPageTable(); + + KeFlushTLB(); + KeSweepIcache(); + KeSweepDcache(); + + // we're lucky that (right now) the entire system memory fits inside 256MB + // otherwise we'd be screwed + + DbgPrint("Dumping page table at %p:", Ttbr0); + + uintptr_t* L1 = (uintptr_t*) (0xC0000000 | Ttbr0); + for (int i = 0; i < 4096; i++) + { + uintptr_t L1Pte = L1[i]; + uintptr_t PhysPage; + int L1Type = L1Pte & 0b11; + if (L1Type == 0b00) + // Unmapped + continue; + + if (L1Type == 0b10) + { + // Section + int Ap = (L1Pte >> 10) & 0b11; + int Tex = (L1Pte >> 12) & 0b111; + int Cb = (L1Pte >> 2) & 0b11; + + int SuperSection = L1Pte & (1 << 18); + PhysPage = L1Pte & ~((1 << 20) - 1); + + DbgPrint(" %p - %d - %p - %s AP:%x TEX:%x CB:%x RawPte:%p", + i << 20, + i, + PhysPage, + SuperSection ? "super-section" : "section", + Ap, + Tex, + Cb, + L1Pte + ); + + continue; + } + else if (L1Type == 0b11) + { + DbgPrint(" %p - %d - Reserved (this entry's presence is an error) RawPte:%p", i << 20, i, L1Pte); + continue; + } + + // Coarse Page Table + PhysPage = L1Pte & ~((1 << 10) - 1); + DbgPrint(" %p - %d - %p - Coarse Page Table RawPte:%p", i << 20, i, PhysPage, L1Pte); + + uintptr_t* L2 = (uintptr_t*) (0xC0000000 | PhysPage); + + for (int j = 0; j < 256; j++) + { + uintptr_t Pte = L2[j]; + uintptr_t RealAddr = (i << 20) | (j << 12); + int Type = Pte & 0b11; + + if (Type == 0b00) + // Unmapped + continue; + + PhysPage = Pte & ~((1 << 12) - 1); + if (Type == 0b01) + { + DbgPrint( + " %p - %d - %d - %p - Large Page (this entry's presence is an error) RawPte:%p", + RealAddr, + i, + j, + PhysPage, + Pte + ); + continue; + } + + bool IsNX = Type == 0b11; + int Ap = (Pte >> 4) & 0b11; + int Tex = (Pte >> 6) & 0b111; + int Cb = (Pte >> 2) & 0b11; + int Apx = (Pte >> 9) & 0b1; + int S = (Pte >> 10) & 0b1; + int nG = (Pte >> 11) & 0b1; + + DbgPrint( + " %p - %d - %d - %p - page NX:%d TEX:%x CB:%x AP:%x APX:%x S:%d NG:%d RawPte:%p", + RealAddr, + i, + j, + PhysPage, + IsNX, + Tex, + Cb, + Ap, + Apx, + S, + nG, + Pte + ); + } + } +} + +#ifdef DEBUG + +#ifdef USE_PL011 + +#define UART0_BASE (0xD18F1000) +#define UART0_DR (*(volatile unsigned int *)(UART0_BASE + 0x00)) +#define UART0_FR (*(volatile unsigned int *)(UART0_BASE + 0x18)) +#define UART0_IBRD (*(volatile unsigned int *)(UART0_BASE + 0x24)) +#define UART0_FBRD (*(volatile unsigned int *)(UART0_BASE + 0x28)) +#define UART0_LCRH (*(volatile unsigned int *)(UART0_BASE + 0x2C)) +#define UART0_CR (*(volatile unsigned int *)(UART0_BASE + 0x30)) +#define UART0_ICR (*(volatile unsigned int *)(UART0_BASE + 0x44)) + +void DbgPrintChar(char c) +{ + while (UART0_FR & (1 << 5)) { + ASM("" ::: "memory"); + } + + UART0_DR = c; +} + +void DbgInit() +{ + UART0_CR = 0; + UART0_ICR = 0x7FF; + UART0_IBRD = 13; + UART0_FBRD = 2; + UART0_LCRH = (3 << 5) | (1 << 4); + UART0_CR = (1 << 0) | (1 << 8) | (1 << 9); +} + +#elif defined USE_EXYNOS4210 + +#define UART0_BASE (0xD1800000) +#define UART0_LCON (*(volatile unsigned int *)(UART0_BASE + 0x00)) +#define UART0_CON (*(volatile unsigned int *)(UART0_BASE + 0x04)) +#define UART0_FCON (*(volatile unsigned int *)(UART0_BASE + 0x08)) +#define UART0_MCON (*(volatile unsigned int *)(UART0_BASE + 0x0C)) +#define UART0_TRSTAT (*(volatile unsigned int *)(UART0_BASE + 0x10)) +#define UART0_ERSTAT (*(volatile unsigned int *)(UART0_BASE + 0x14)) +#define UART0_FSTAT (*(volatile unsigned int *)(UART0_BASE + 0x18)) +#define UART0_MSTAT (*(volatile unsigned int *)(UART0_BASE + 0x1C)) +#define UART0_TXH (*(volatile unsigned int *)(UART0_BASE + 0x20)) +#define UART0_RXH (*(volatile unsigned int *)(UART0_BASE + 0x24)) +#define UART0_BRDIV (*(volatile unsigned int *)(UART0_BASE + 0x28)) +#define UART0_FRACVAL (*(volatile unsigned int *)(UART0_BASE + 0x2C)) +#define UART0_INTP (*(volatile unsigned int *)(UART0_BASE + 0x30)) +#define UART0_INTSP (*(volatile unsigned int *)(UART0_BASE + 0x34)) +#define UART0_INTM (*(volatile unsigned int *)(UART0_BASE + 0x38)) + +void DbgPrintChar(char c) +{ + while (!(UART0_TRSTAT & (1 << 1))) { + ASM("" ::: "memory"); + } + + UART0_TXH = c; +} + +void DbgInit() +{ + UART0_FCON = 0; + UART0_MCON = 0; + UART0_LCON = 0x3; // 8N1 + UART0_CON = (1 << 2) | (1 << 0); + UART0_BRDIV = 53; + UART0_FRACVAL = 4; + DbgPrintString("ARMBoron is alive!\n"); +} + +#else + +// no debug for you, scream into the void +void DbgPrintChar(char c) +{ + (void) c; +} + +void DbgInit() +{ +} + +#endif + +void DbgPrintString(const char* str) +{ + while (*str) + { + if (*str == '\n') + DbgPrintChar('\r'); + DbgPrintChar(*str); + str++; + } +} + +KSPIN_LOCK KiPrintLock; +KSPIN_LOCK KiDebugPrintLock; + +void DbgPrintStringLocked(const char* str) +{ +#ifndef DONT_LOCK + KIPL OldIpl; + KeAcquireSpinLock(&KiDebugPrintLock, &OldIpl); +#endif + + DbgPrintString(str); + +#ifndef DONT_LOCK + KeReleaseSpinLock(&KiDebugPrintLock, OldIpl); +#endif +} + +#endif diff --git a/boron/source/ke/arm/init.c b/boron/source/ke/arm/init.c new file mode 100644 index 00000000..6048475c --- /dev/null +++ b/boron/source/ke/arm/init.c @@ -0,0 +1,32 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + ke/armv6/init.c + +Abstract: + This module implements the architecture specific UP-init + and MP-init routines. + +Author: + iProgramInCpp - 29 December 2023 +***/ +#include +#include + +void KiInitializeInterruptSystem(); + +INIT +void KeInitArchUP() +{ + KiInitializeInterruptSystem(); +} + +INIT +void KeInitArchMP() +{ + KeInitCPU(); + KeLowerIPL(IPL_NORMAL); + HalInitSystemMP(); +} \ No newline at end of file diff --git a/boron/source/ke/arm/intobj.c b/boron/source/ke/arm/intobj.c new file mode 100644 index 00000000..fcc30b53 --- /dev/null +++ b/boron/source/ke/arm/intobj.c @@ -0,0 +1,175 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + ke/armv6/intobj.c + +Abstract: + This module implements the interrupt object for + the armv6 platform. + + N.B. The interval timer and DPC dispatch functions + do not use the interrupt object. + +Author: + iProgramInCpp - 29 December 2025 +***/ +#include +#include + +typedef struct +{ + LIST_ENTRY List; + KSPIN_LOCK Lock; +} +INTERRUPT_LIST, *PINTERRUPT_LIST; + +static INTERRUPT_LIST KiInterruptList[256]; + +INIT +void KiInitializeInterruptSystem() +{ + for (int i = 0; i < 256; i++) + { + InitializeListHead(&KiInterruptList[i].List); + KeInitializeSpinLock(&KiInterruptList[i].Lock); + } +} + +void KeInitializeInterrupt( + PKINTERRUPT Interrupt, + PKSERVICE_ROUTINE ServiceRoutine, + void* ServiceContext, + PKSPIN_LOCK SpinLock, + int Vector, + KIPL InterruptIpl, + bool SharedVector) +{ + if (Vector >= HalGetMaximumInterruptCount()) + { + DbgPrint("WARNING: KeInitializeInterrupt -- interrupt vector %d will not be called", Vector); + return; + } + + ASSERT(InterruptIpl > IPL_DPC && "The caller may not override this IPL"); + ASSERT(InterruptIpl <= IPL_CLOCK && "The caller may not override this IPL"); + + Interrupt->Connected = false; + Interrupt->SharedVector = SharedVector; + Interrupt->Vector = Vector; + Interrupt->Ipl = InterruptIpl; + Interrupt->ServiceRoutine = ServiceRoutine; + Interrupt->ServiceContext = ServiceContext; + Interrupt->SpinLock = SpinLock; +} + +bool KeConnectInterrupt(PKINTERRUPT Interrupt) +{ + ASSERT(!Interrupt->Connected && "It's already connected!"); + + PINTERRUPT_LIST InterruptList = &KiInterruptList[Interrupt->Vector]; + + KIPL Ipl, IplUnused; + Ipl = KeRaiseIPL(Interrupt->Ipl); + KeAcquireSpinLock(&InterruptList->Lock, &IplUnused); + + // Check if the vector may be shared. + if (!IsListEmpty(&InterruptList->List)) + { + // If the head has SharedVector == false, return false here. + PKINTERRUPT Head = CONTAINING_RECORD(InterruptList->List.Flink, KINTERRUPT, Entry); + + if (!Head->SharedVector) + { + KeReleaseSpinLock(&InterruptList->Lock, IplUnused); + KeLowerIPL(Ipl); + return false; + } + } + else + { + HalVicRegisterInterrupt(Interrupt->Vector, Interrupt->Ipl); + } + + // Connect the interrupt now. + InsertTailList(&InterruptList->List, &Interrupt->Entry); + Interrupt->Connected = true; + + KeReleaseSpinLock(&InterruptList->Lock, IplUnused); + KeLowerIPL(Ipl); + + return true; +} + +void KeDisconnectInterrupt(PKINTERRUPT Interrupt) +{ + ASSERT(Interrupt->Connected && "You need to have connected the interrupt to disconnect it!"); + + PINTERRUPT_LIST InterruptList = &KiInterruptList[Interrupt->Vector]; + KIPL Ipl, IplUnused; + Ipl = KeRaiseIPL(Interrupt->Ipl); + KeAcquireSpinLock(&InterruptList->Lock, &IplUnused); + + // Disconnect the interrupt now. + RemoveEntryList(&Interrupt->Entry); + Interrupt->Connected = false; + + if (IsListEmpty(&InterruptList->List)) + HalVicDeregisterInterrupt(Interrupt->Vector, Interrupt->Ipl); + + KeReleaseSpinLock(&InterruptList->Lock, IplUnused); + KeLowerIPL(Ipl); +} + +int KeSynchronizeExecution( + PKINTERRUPT Interrupt, + KIPL SynchronizeIpl, + PKSYNCHRONIZE_ROUTINE Routine, + void* SynchronizeContext) +{ + if (SynchronizeIpl < Interrupt->Ipl) + SynchronizeIpl = Interrupt->Ipl; + + KIPL Ipl, IplUnused; + Ipl = KeRaiseIPL(SynchronizeIpl); + KeAcquireSpinLock(Interrupt->SpinLock, &IplUnused); + + int Result = Routine(SynchronizeContext); + + KeReleaseSpinLock(Interrupt->SpinLock, IplUnused); + KeLowerIPL(Ipl); + + return Result; +} + +void KeDispatchInterruptRequest(int Number) +{ + PINTERRUPT_LIST InterruptList = &KiInterruptList[Number]; + KIPL Ipl; + + KeAcquireSpinLock(&InterruptList->Lock, &Ipl); + + for (PLIST_ENTRY Entry = InterruptList->List.Flink; + Entry != &InterruptList->List; + Entry = Entry->Flink) + { + PKINTERRUPT Interrupt = CONTAINING_RECORD(Entry, KINTERRUPT, Entry); + KIPL Unused; + KeAcquireSpinLock(Interrupt->SpinLock, &Unused); + + Interrupt->ServiceRoutine(Interrupt, Interrupt->ServiceContext); + + KeReleaseSpinLock(Interrupt->SpinLock, Unused); + } + + KeReleaseSpinLock(&InterruptList->Lock, Ipl); + + // Acknowledge the interrupt. + HalEndOfInterrupt(Number); +} + +void KeOnUpdateIPL(KIPL NewIpl, KIPL OldIpl) +{ + HalOnUpdateIpl(NewIpl, OldIpl); +} diff --git a/boron/source/ke/arm/misc.S b/boron/source/ke/arm/misc.S new file mode 100644 index 00000000..16ab7f86 --- /dev/null +++ b/boron/source/ke/arm/misc.S @@ -0,0 +1,60 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + ke/armv6/misc.S + +Abstract: + This module implements miscellaneous hardware functions + for the armv6 architecture. + +Author: + iProgramInCpp - 28 December 2025 +***/ + + .section .text + + @ HPAGEMAP KeSetCurrentPageTable(); + .global KeGetCurrentPageTable +KeGetCurrentPageTable: + mrc p15, 0, r0, c2, c0, 0 @ Read TTBR0 + bic r0, r0, #255 @ Clear lower 8 bits because that contains our cache policy + bx lr + + @ void KeSetCurrentPageTable(HPAGEMAP PageMap); + .global KeSetCurrentPageTable +KeSetCurrentPageTable: + mcr p15, 0, r0, c7, c10, 0 @ clean dcache + mcr p15, 0, r0, c7, c10, 4 @ DSB - data sync barrier + mcr p15, 0, r0, c2, c0, 0 @ write TTBR0 + @ fallthrough to KeFlushTLB + + @ void KeFlushTLB(void); + .global KeFlushTLB +KeFlushTLB: + mov r0, #0 + mcr p15, 0, r0, c7, c10, 0 @ clean dcache + mcr p15, 0, r0, c7, c10, 4 @ DSB - data sync barrier + mcr p15, 0, r0, c8, c7, 0 @ invalidate unified TLB + mcr p15, 0, r0, c7, c10, 4 @ DSB again + mcr p15, 0, r0, c7, c5, 4 @ flush prefetch buffer + bx lr + + @void KeSweepIcache(void); + .global KeSweepIcache +KeSweepIcache: + mov r0, #0 + mcr p15, 0, r0, c7, c5, 0 @ flush icache + branch predictor cache + mcr p15, 0, r0, c7, c5, 4 @ flush prefetch buffer + bx lr + + @void KeSweepDcache(void); + .global KeSweepDcache +KeSweepDcache: + mov r0, #0 + mcr p15, 0, r0, c7, c10, 0 @ clean dcache + mcr p15, 0, r0, c7, c10, 4 @ DSB + mcr p15, 0, r0, c7, c10, 5 @ DMB + mcr p15, 0, r0, c7, c6, 0 @ invalidate dcache + bx lr \ No newline at end of file diff --git a/boron/source/ke/arm/probe.c b/boron/source/ke/arm/probe.c new file mode 100644 index 00000000..d19b9934 --- /dev/null +++ b/boron/source/ke/arm/probe.c @@ -0,0 +1,118 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + ke/armv6/probe.c + +Abstract: + This module implements the high level routines for address probing. + + Probing a set of addresses checks that they are usable in kernel mode. + It also brings all of the demand-pages back in to memory. It works by + attempting to read from / write to the memory. If the memory is not + accessible, an invalid page fault is raised by hardware. This makes + probing about as cheap as just copying the raw data. + +Author: + iProgramInCpp - 26 December 2025 +***/ +#include +#include + +bool MmIsAddressRangeValid(uintptr_t Address, size_t Size, KPROCESSOR_MODE AccessMode) +{ + // Size=0 is invalid. + if (Size == 0) { + DbgPrint("MmIsAddressRangeValid FAILURE: Size 0"); + return false; + } + + // Check for overflow. + uintptr_t AddressEnd = Address + Size; + if (AddressEnd < Address) { + DbgPrint("MmIsAddressRangeValid FAILURE: AddressEnd %p < Address %p", AddressEnd, Address); + return false; + } + + if (AccessMode == MODE_USER && AddressEnd > MM_USER_SPACE_END) { + DbgPrint("MmIsAddressRangeValid FAILURE: AccessMode==MODEUSER AddressEnd %p Address %p Size: %zu RA:%p", AddressEnd, Address, Size, CallerAddress()); + return false; + } + + return true; +} + +// Defined in arch/armv6/probe2.S +int MmProbeAddressSub(void* Address, size_t Length, bool ProbeWrite); + +// This is the front-end for the probing code. The actual probing +// is performed in assembly, because it's impossible to predict what +// kind of stack layout the C version would use. (It could differ +// depending on compiler version, for example.) +BSTATUS MmProbeAddress(void* Address, size_t Length, bool ProbeWrite, KPROCESSOR_MODE AccessMode) +{ + if (!MmIsAddressRangeValid((uintptr_t)Address, Length, AccessMode)) + return STATUS_INVALID_PARAMETER; + + const uintptr_t MaxPtr = (uintptr_t) ~0ULL; // 0b1111...1111 + const uintptr_t HalfMaxPtr = MaxPtr >> 1; // 0b0111...1111 + const uintptr_t MSBPtrSet = ~HalfMaxPtr; // 0b1000...0000 + + // If the size is bigger than or equal to MAX_ADDRESS>>1 + if (Length > HalfMaxPtr) + return STATUS_INVALID_PARAMETER; + + uintptr_t AddressLimit = (uintptr_t) Address + Length; + + // If the address and the address limit are in + // different halves of the address space + if (((uintptr_t)Address ^ AddressLimit) == MSBPtrSet) + return STATUS_INVALID_PARAMETER; + + KeGetCurrentThread()->Probing = true; + + int Code = MmProbeAddressSub (Address, Length, ProbeWrite); + + if (Code != STATUS_SUCCESS) + { + KeGetCurrentThread()->Probing = false; + return Code; + } + + KeGetCurrentThread()->Probing = false; + return STATUS_SUCCESS; +} + +// Defined in arch/armv6/probe2.S +int MmSafeCopySub(void* Address, const void* Source, size_t Length); + +BSTATUS MmSafeCopy(void* Address, const void* Source, size_t Length, KPROCESSOR_MODE AccessMode, bool VerifyDest) +{ + if (VerifyDest) + { + if (!MmIsAddressRangeValid((uintptr_t)Address, Length, AccessMode)) + return STATUS_INVALID_PARAMETER; + } + else + { + if (!MmIsAddressRangeValid((uintptr_t)Source, Length, AccessMode)) + return STATUS_INVALID_PARAMETER; + } + + // Let the page fault handler know we are probing. + KeGetCurrentThread()->Probing = true; + + // This is just a regular old memcpy. Nothing different about it, + // other than the return value. It's a five instruction marvel. + int Code = MmSafeCopySub(Address, Source, Length); + + // If it returned through a path different than usual (i.e. it was + // detoured through MmProbeAddressSubEarlyReturn), then it's going + // to return STATUS_FAULT, which we'll mirror when returning. + + // No longer probing. + KeGetCurrentThread()->Probing = false; + + return Code; +} diff --git a/boron/source/ke/arm/probe2.S b/boron/source/ke/arm/probe2.S new file mode 100644 index 00000000..851541f5 --- /dev/null +++ b/boron/source/ke/arm/probe2.S @@ -0,0 +1,62 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + ke/armv6/probe.S + +Abstract: + This module implements the MmSafeCopySub and MmProbeAddressSub + functions for the armv6 architecture. + +Author: + iProgramInCpp - 26 December 2025 +***/ + + .section .text + + @ TODO: optimize this. Currently it does byte-by-byte copies, but this + @ is bad. However, typically we don't need to copy too much data at once + @ so this is fine for now. + + @ void MmSafeCopySub(void* Destination, const void* Source, size_t ByteCount); + .global MmSafeCopySub +MmSafeCopySub: + cmp r2, #0 + beq 2f +1: + ldrb r3, [r1], #1 @ load a byte from src + strb r3, [r0], #1 @ store byte to dst + subs r2, r2, #1 + bne 1b +2: + mov r0, #0 + bx lr + + @ int MmProbeAddressSub(void* Address, size_t Length, bool ProbeWrite); + .global MmProbeAddressSub +MmProbeAddressSub: + push {r4} + add r3, r0, r1 @ AddressEnd = Address + Length + cmp r0, r3 + bhs 3f +1: + ldrb r4, [r0] @ probe the read + cmp r2, #0 @ check if need to write + beq 2f + strb r4, [r0] @ probe the write +2: + add r0, r0, #4096 @ add a page size + cmp r0, r3 + blo 1b @ Address < AddressEnd +3: + mov r0, #0 + pop {r4} + bx lr + + @ Returns early from MmProbeAddressSub and MmSafeCopySub. + @ Called by the invalid page fault handler. + .global MmProbeAddressSubEarlyReturn +MmProbeAddressSubEarlyReturn: + pop {r4} + bx lr diff --git a/boron/source/ke/arm/start.S b/boron/source/ke/arm/start.S new file mode 100644 index 00000000..68309acf --- /dev/null +++ b/boron/source/ke/arm/start.S @@ -0,0 +1,245 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + ke/armv6/start.S + +Abstract: + This module implements the entry point of the Boron kernel + for the armv6 architecture. + +Author: + iProgramInCpp - 26 December 2025 +***/ +.global KiBeforeSystemStartup +.extern KiSystemStartup +.extern KiUndefinedInstructionHandler +.extern KiSoftwareInterruptHandler +.extern KiPrefetchAbortHandler +.extern KiDataAbortHandler +.extern KiIrqHandler +.extern KiFiqHandler + +/* define a Level 1 "Section" PTE */ +/*#define L1PTE(Address) (((Address) & 0xFFF00000) | 0b111010000001110)*/ + +/* TEX = 0b001, AP = 0b01, P = 0, Domain = 0, CB = 0b11 */ +/* .equ L1PTE_FLAGS_SEC, 0b111010000001110 */ +.equ L1PTE_FLAGS_SEC, 0b001010000001110 + +/* P = 0, Domain = 0, Type = 0b01 */ +.equ L1PTE_FLAGS_CPT, 0b0000000001 + +.section .ipltext, "ax", %progbits + +/* The exception handler table is mapped to 0xFFFF0000. Must be aligned to 4KB */ +.global KiExceptionHandlerTable +KiExceptionHandlerTable: + b KiBeforeSystemStartup + b KiUndefinedInstructionHandler + b KiSoftwareInterruptHandler + b KiPrefetchAbortHandler + b KiDataAbortHandler + .word 0 + b KiIrqHandler + b KiFiqHandler + +KiBeforeSystemStartup: + cpsid if + + /* store the LPB pointer from r0 */ + ldr r1, =(KiBootloaderLpb - 0xC0000000) + str r0, [r1, #0x0000] + + /* set up the root page table */ + ldr r2, =L1PTE_FLAGS_SEC + + /* map first 4MB with identity */ + ldr r0, =KiRootPageTable + ldr r1, =0x000000 + orr r1, r1, r2 + str r1, [r0, #0x0000] + ldr r1, =0x100000 + orr r1, r1, r2 + str r1, [r0, #0x0004] + ldr r1, =0x200000 + orr r1, r1, r2 + str r1, [r0, #0x0008] + ldr r1, =0x300000 + orr r1, r1, r2 + str r1, [r0, #0x000C] + + /* TEMP: map framebuffer to 0x400000 */ + ldr r1, =0x0FD00000 + ldr r3, =0b000010000000010 /* map fb differently */ + orr r1, r1, r3 + str r1, [r0, #0x0010] + + /* map first 4MB with 0xC0000000 offset */ + ldr r0, =(KiRootPageTable + 0x3000) + ldr r1, =0x000000 + orr r1, r1, r2 + str r1, [r0, #0x0000] + ldr r1, =0x100000 + orr r1, r1, r2 + str r1, [r0, #0x0004] + ldr r1, =0x200000 + orr r1, r1, r2 + str r1, [r0, #0x0008] + ldr r1, =0x300000 + orr r1, r1, r2 + str r1, [r0, #0x000C] + +/* + @ map PL011 for debugging purposes + @ raspi1ap machine features it at offset 0x101f1000 + @ we need to map it to 0xD1800000 + ldr r0, =(KiRootPageTable + 0x3460) + ldr r1, =0x10100000 + orr r1, r1, r2 + str r1, [r0, #0x0000] +*/ + + /* map UART0 for debugging purposes */ + /* iPod touch 1G maps it at 0x3CC00000 */ + /* TODO: DO NOT hardcode this */ + ldr r0, =(KiRootPageTable + 0x3460) + ldr r1, =0x3CC00000 + orr r1, r1, r2 + str r1, [r0, #0x0000] + + mcr p15, 0, r1, c7, c10, 0 /* clean dcache */ + mcr p15, 0, r1, c7, c10, 4 /* data sync buffer */ + + ldr r0, =KiRootPageTable + mcr p15, 0, r0, c2, c0, 0 /* write TTBR0 */ + mcr p15, 0, r1, c8, c7, 0 /* TLB invalidate */ + + mov r0, #0x1 + mcr p15, 0, r0, c3, c0, 0 /* Domain Access Control: Domain 0 = Client */ + + mov r1, #0x0 + mcr p15, 0, r1, c2, c0, 2 /* clear TTBCR to always use TTBR0 */ + mcr p15, 0, r1, c7, c10, 0 /* clean dcache */ + mcr p15, 0, r1, c7, c10, 4 /* data sync buffer */ + mcr p15, 0, r1, c7, c6, 0 /* d-cache sweep */ + mcr p15, 0, r1, c8, c7, 0 /* TLB invalidate */ + mcr p15, 0, r1, c7, c5, 0 /* i-cache sweep */ + + mrc p15, 0, r0, c1, c0, 0 /* read SCTLR */ + orr r0, r0, #1 /* enable MMU */ + orr r0, r0, #(1<<2) /* enable dcache, icache */ + orr r0, r0, #(1<<12) /* enable icache */ + bic r0, r0, #(1<<28) /* clear TEX remap, if needed */ + orr r0, r0, #(1<<13) /* enable V - interrupt vectors start at 0xFFFF0000 */ + +#ifndef TARGET_ARMV5 + orr r0, r0, #(1<<23) /* enable XP - disables ARMv5 backwards compatible mode */ +#endif + + mcr p15, 0, r0, c1, c0, 0 /* write SCTLR */ + + mcr p15, 0, r1, c7, c10, 0 /* clean dcache */ + mcr p15, 0, r1, c7, c10, 4 /* data sync buffer */ + mcr p15, 0, r1, c8, c7, 0 /* TLB invalidate */ + mcr p15, 0, r1, c7, c6, 0 /* d-cache sweep */ + mcr p15, 0, r1, c7, c5, 0 /* i-cache sweep */ + mcr p15, 0, r1, c7, c10, 4 /* data sync buffer */ + + /* Set up a simple stack */ + ldr sp, =KiInitStackBottom + +#ifdef TARGET_ARMV5 + /* Initialize each exception's stack register. These are temporary until threads start running. */ + mrs r12, cpsr + bic r12, r12, #0x1F + orr r12, r12, #0x11 @ FIQ + msr cpsr, r12 + ldr sp, =KiFiqStackBottom + + mrs r12, cpsr + bic r12, r12, #0x1F + orr r12, r12, #0x12 @ IRQ + msr cpsr, r12 + ldr sp, =KiIrqStackBottom + + mrs r12, cpsr + bic r12, r12, #0x1F + orr r12, r12, #0x17 @ ABT + msr cpsr, r12 + ldr sp, =KiAbortStackBottom + + mrs r12, cpsr + bic r12, r12, #0x1F + orr r12, r12, #0x1B @ UND + msr cpsr, r12 + ldr sp, =KiUndefinedInstructionStackBottom + + mrs r12, cpsr + bic r12, r12, #0x1F + orr r12, r12, #0x13 @ SVC + msr cpsr, r12 +#else + /* Initialize each exception's stack register. These are temporary until threads start running. */ + cps #0x11 /* FIQ */ + ldr sp, =KiFiqStackBottom + cps #0x12 /* IRQ */ + ldr sp, =KiIrqStackBottom + cps #0x17 /* ABT */ + ldr sp, =KiAbortStackBottom + cps #0x1B /* UND */ + ldr sp, =KiUndefinedInstructionStackBottom + cps #0x13 /* switch back to SVC mode */ +#endif + + /* jump to KiSystemStartup in .text at 0xC0000000 */ + ldr r0, =KiSystemStartup + bx r0 + +.section .iplbss, "aw", %nobits +.global KiRootPageTable +.global KiRootPageTableDebbie +.global KiRootPageTableJibbie +.global KiPoolHeadersPageTables + +/* KEEP THIS at the start of iplbss! */ +.align 16 +KiRootPageTable: + .space 16384 + +/* Root page table Debbie */ +KiRootPageTableDebbie: + .space 4096 + +/* Root page table Jibbie */ +KiRootPageTableJibbie: + .space 4096 + +/* Page tables for pool headers */ +KiPoolHeadersPageTables: + .space 4 * 2048 + +/* ^^^ I promise I will find better names for them soon. */ + +.section .bss +.align 16 +KiInitStack: + .space 2048 +KiInitStackBottom: + +/* define each exception's separate stack */ + .space 64 +KiUndefinedInstructionStackBottom: + .space 64 +KiSoftwareInterruptStackBottom: + .space 64 +KiAbortStackBottom: + .space 64 +KiIrqStackBottom: + .space 64 +KiFiqStackBottom: + + .global KiBootloaderLpb +KiBootloaderLpb: + .space 4 diff --git a/boron/source/ke/arm/syscall.c b/boron/source/ke/arm/syscall.c new file mode 100644 index 00000000..d0038425 --- /dev/null +++ b/boron/source/ke/arm/syscall.c @@ -0,0 +1,175 @@ +/*** + The Boron Operating System + Copyright (C) 2025-2026 iProgramInCpp + +Module name: + ke/arm/syscall.c + +Abstract: + This module implements the system service dispatcher + for the arm architecture. + +Author: + iProgramInCpp - 30 December 2025 +***/ +#include "../ki.h" + +#include +#include +#include +#include +#include +#include + +//#define ENABLE_SYSCALL_TRACE + +const void* const KiSystemServiceTable[] = +{ + OSAllocateVirtualMemory, + OSCheckIsTerminalFile, + OSClose, + OSCreateEvent, + OSCreateMutex, + OSCreatePipe, + OSCreateProcess, + OSCreateTerminal, + OSCreateTerminalIoHandles, + OSCreateThread, + OSDeviceIoControl, + OSDuplicateHandle, + OSExitProcess, + OSExitThread, + OSFreeVirtualMemory, + OSGetAlignmentFile, + OSGetCurrentPeb, + OSGetCurrentTeb, + OSGetExitCodeProcess, + OSGetLengthFile, + OSGetMappedFileHandle, + OSGetTickCount, + OSGetTickFrequency, + OSGetVersionNumber, + OSMapViewOfObject, + OSOpenEvent, + OSOpenFile, + OSOpenMutex, + OSOutputDebugString, + OSPulseEvent, + OSQueryEvent, + OSQueryMutex, + OSReadDirectoryEntries, + OSReadFile, + OSReadVirtualMemory, + OSReleaseMutex, + OSResetEvent, + OSSeekFile, + OSSetCurrentPeb, + OSSetCurrentTeb, + OSSetEvent, + OSSetExitCode, + OSSetPebProcess, + OSSetSuspendedThread, + OSSleep, + OSTerminateThread, + OSTouchFile, + OSWaitForMultipleObjects, + OSWaitForSingleObject, + OSWriteFile, + OSWriteVirtualMemory, + OSForkProcess, + OSQueryVirtualMemoryInformation, + OSCloseAllUninheritableHandles, + OSCheckIsValidHandle, + OSCreateFile, + OSCreateDirectory, + OSCreateSymbolicLink, + OSSetImageNameProcess, + OSQuerySystemInformation, +}; + +#define KI_SYSCALL_COUNT ARRAY_COUNT(KiSystemServiceTable) + +typedef int(*KI_SYSCALL_HANDLER)( + uintptr_t p1, + uintptr_t p2, + uintptr_t p3, + uintptr_t p4, + uintptr_t p5, + uintptr_t p6, + uintptr_t p7, + uintptr_t p8, + uintptr_t p9, + uintptr_t ip, + uintptr_t sp +); + +extern void KePrintSystemServiceDebug(size_t Call); // cpu.c + +void KiCheckTerminatedUserMode() +{ + // Before entering user mode, check if the thread was terminated first. + if (KeGetCurrentThread()->PendingTermination) + KiTerminateUserModeThread(KeGetCurrentThread()->IncrementTerminated); +} + +void KiSystemServiceHandler(PKREGISTERS Regs, uintptr_t Sp_Usr) +{ + uintptr_t CallNumber = 0; + uintptr_t Opcode; + BSTATUS Status = MmSafeCopy(&Opcode, (void*)(Regs->Lr - 4), sizeof(uintptr_t), MODE_USER, false); + if (FAILED(Status)) + { + Regs->R0 = Status; + return; + } + + // Keep Immediate (the system call will be executed like `svc #callNumber`) + Opcode &= 0xFFFFFF; + + // This is the call number + CallNumber = Opcode; + + if (CallNumber >= KI_SYSCALL_COUNT) + { + Regs->R0 = STATUS_INVALID_PARAMETER; + return; + } + +#ifdef ENABLE_SYSCALL_TRACE + KePrintSystemServiceDebug(CallNumber); +#endif + + // Now call the function. + // + // Note: Most system call handlers completely ignore the 7th and 8th parameters, except for + // the OSForkProcess handler. + KI_SYSCALL_HANDLER Handler = KiSystemServiceTable[CallNumber]; + int Return = Handler( + Regs->R0, + Regs->R1, + Regs->R2, + Regs->R3, + Regs->R4, + Regs->R5, + Regs->R6, + Regs->R7, + Regs->R8, + Regs->Lr, + Sp_Usr + ); + Regs->R0 = Return; + + KiCheckTerminatedUserMode(); +} + +#ifdef ENABLE_SYSCALL_TRACE + +void KePrintSystemServiceDebug(size_t Syscall) +{ + // Format: "[ThreadPointer] - Syscall [Number] (FunctionPointer) ([FunctionName])" + const void* Handler = KiSystemServiceTable[Syscall]; + const char* FunctionName = DbgLookUpRoutineNameByAddressExact((uintptr_t) Handler); + DbgPrint("SYSCALL: %p - %d %p %s", KeGetCurrentThread(), (int) Syscall, Handler, FunctionName); +} + +#endif diff --git a/boron/source/ke/arm/thredsup.c b/boron/source/ke/arm/thredsup.c new file mode 100644 index 00000000..ddcaa454 --- /dev/null +++ b/boron/source/ke/arm/thredsup.c @@ -0,0 +1,66 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + ke/armv6/thredsup.c + +Abstract: + This module implements the architecture specific + thread state setup routine. + +Author: + iProgramInCpp - 29 December 2025 +***/ +#include +#include +#include + +typedef struct { + uint32_t R4; + uint32_t R5; + uint32_t R6; + uint32_t R7; + uint32_t R8; + uint32_t R9; + uint32_t R10; + uint32_t R11; + uint32_t R12; + uint32_t Lr; +} +ARM_INIT_CONTEXT, *PARM_INIT_CONTEXT; + +NO_RETURN void KiThreadEntryPoint(); + +void KiSetupRegistersThread(PKTHREAD Thread) +{ + uintptr_t StackBottom = ((uintptr_t) Thread->Stack.Top + (uintptr_t) Thread->Stack.Size - 0x10) & ~0xF; + + PARM_INIT_CONTEXT Context = (void*)(StackBottom - sizeof(ARM_INIT_CONTEXT)); + memset(Context, 0, sizeof Context); + + // R4 contains the thread entry point and R5 contains the context. + Context->R4 = (uint32_t) Thread->StartRoutine; + Context->R5 = (uint32_t) Thread->StartContext; + Context->Lr = (uint32_t) KiThreadEntryPoint; + + Thread->StackPointer = (void*) Context; + + memset(&Thread->ArchContext, 0, sizeof Thread->ArchContext); + + uintptr_t IntStack = (uintptr_t) Thread->InterruptStack; + Thread->AbtStack = IntStack + KERNEL_INTERRUPT_STACK_SIZE * 1 / 4; + Thread->UndStack = IntStack + KERNEL_INTERRUPT_STACK_SIZE * 2 / 4; + Thread->IrqStack = IntStack + KERNEL_INTERRUPT_STACK_SIZE * 3 / 4; + Thread->FiqStack = IntStack + KERNEL_INTERRUPT_STACK_SIZE; +} + +void KiSwitchArchSpecificContext(PKTHREAD NewThread, PKTHREAD OldThread) +{ + if (!OldThread) + return; + + // TODO + (void) NewThread; + (void) OldThread; +} diff --git a/boron/source/ke/arm/tlbs.c b/boron/source/ke/arm/tlbs.c new file mode 100644 index 00000000..41750b5c --- /dev/null +++ b/boron/source/ke/arm/tlbs.c @@ -0,0 +1,37 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + ke/armv6/tlbs.c + +Abstract: + This module contains the armv6 platform's specific + TLB shootdown routine. + +Author: + iProgramInCpp - 29 December 2025 +***/ +#include + +#ifdef CONFIG_SMP +#error 32-bit ARM SMP is not supported! +#endif + +#define MAX_TLBS_LENGTH 4096 + +void KeIssueTLBShootDown(uintptr_t Address, size_t Length) +{ + if (Length == 0) + Length = 1; + + if (Length >= MAX_TLBS_LENGTH) + { + KeFlushTLB(); + } + else + { + for (size_t i = 0; i < Length; i++) + KeInvalidatePage((void*)(Address + i * PAGE_SIZE)); + } +} diff --git a/boron/source/ke/arm/trap.S b/boron/source/ke/arm/trap.S new file mode 100644 index 00000000..cc6542d0 --- /dev/null +++ b/boron/source/ke/arm/trap.S @@ -0,0 +1,360 @@ +/*** + The Boron Operating System + Copyright (C) 2025-2026 iProgramInCpp + +Module name: + ke/arm/trap.S + +Abstract: + This module implements functions related to interrupt management + for the armv6 architecture. + +Author: + iProgramInCpp - 26 December 2025 +***/ + + .section .text + + @ bool KeDisableInterrupts(); + .global KeDisableInterrupts +KeDisableInterrupts: + mrs r12, cpsr + tst r12, #(1 << 7) @ test I bit + moveq r0, #1 @ 1 if enabled + movne r0, #0 @ 0 if disabled +#ifdef TARGET_ARMV5 + orr r12, r12, #0x80 @ set I bit + msr cpsr_c, r12 +#else + cpsid if +#endif + bx lr + + @ void KeRestoreInterrupts(bool OldState); + .global KeRestoreInterrupts +KeRestoreInterrupts: + cmp r0, #0 + beq 1f +#ifdef TARGET_ARMV5 + mrs r0, cpsr + bic r0, r0, #0x80 @ clear I bit +#else + cpsie if +#endif +1: bx lr + + @ NO_RETURN void KeDescendIntoUserMode( + @ void* InstructionPointer, + @ void* StackPointer, + @ void* UserContext + @ ); + .global KeDescendIntoUserMode +KeDescendIntoUserMode: + @ Enter system mode. + cps #0x1F + + @ Prepare SP, LR, and R0. + mov sp, r1 + mov lr, r0 + mov r0, r2 + + @ Prepare SPSR to enter user mode. + @ M = 0x10, A = 1 (imprecise abort) + mov r1, #0x110 + msr spsr_cxsf, r1 + + @ Finally, enter user mode. + movs pc, lr + + @ void KiSwitchThreadStack(void** OldStack, void** NewStack); + .global KiSwitchThreadStack +KiSwitchThreadStack: + stmdb sp!, {r4-r12, lr} @ save callee saved regs for old thread + str sp, [r0] + ldr sp, [r1] + ldmia sp!, {r4-r12, lr} @ restore callee saved regs from new thread + bx lr + + @ void KiSwitchThreadStackForever(void* NewStack) + .global KiSwitchThreadStackForever +KiSwitchThreadStackForever: + mov sp, r0 + ldmia sp!, {r4-r12, lr} + bx lr + + @ void KiThreadEntryPoint([register r4] ThreadEntryPoint, [register r5] ThreadContext); + .global KiThreadEntryPoint + .extern KiUnlockDispatcher +KiThreadEntryPoint: + mov r0, #0 + bl KiUnlockDispatcher @ unlock the dispatcher and lower to normal IPL + mov r0, r5 + cpsie if @ enable interrupts now + bx r4 + + @ void KiSaveInterruptStacks( + @ uintptr_t* AbortStack, + @ uintptr_t* UndefinedStack, + @ uintptr_t* IrqStack, + @ uintptr_t* FiqStack + @ ); + .global KiSaveInterruptStacks +KiSaveInterruptStacks: + +#ifdef TARGET_ARMV5 + #warning TODO: update this to match armv6 behavior + mrs r12, cpsr + orr r12, r12, #0xC0 @ set I and F bits + bic r12, r12, #0x1F + orr r12, r12, #0x17 @ abort mode + msr cpsr_c, r12 + str sp, [r0] + + bic r12, r12, #0x1F + orr r12, r12, #0x1B @ undefined mode + msr cpsr_c, r12 + str sp, [r1] + + bic r12, r12, #0x1F + orr r12, r12, #0x12 @ IRQ mode + msr cpsr_c, r12 + str sp, [r2] + + bic r12, r12, #0x1F + orr r12, r12, #0x11 @ FIQ mode + msr cpsr_c, r12 + str sp, [r3] + + bic r12, r12, #0xDF @ clear I and F bits as well as mode bits (0xC0 | 0x1F) + orr r12, r12, #0x13 @ supervisor mode + msr cpsr_c, r12 +#else + cps #0x17 @ Abort Mode + str sp, [r0] + cps #0x1B @ Undefined Mode + str sp, [r1] + cps #0x12 @ IRQ Mode + str sp, [r2] + cps #0x11 @ FIQ Mode + str sp, [r3] + cps #0x13 @ Supervisor Mode +#endif + bx lr + + @ void KiRestoreInterruptStacks( + @ uintptr_t AbortStack, + @ uintptr_t UndefinedStack, + @ uintptr_t IrqStack, + @ uintptr_t FiqStack + @ ); + .global KiRestoreInterruptStacks +KiRestoreInterruptStacks: + +#ifdef TARGET_ARMV5 + #warning TODO: update this to match armv6 behavior + mrs r12, cpsr + orr r12, r12, #0xC0 @ set I and F bits + bic r12, r12, #0x1F + orr r12, r12, #0x17 @ abort mode + msr cpsr_c, r12 + mov sp, r0 + + bic r12, r12, #0x1F + orr r12, r12, #0x1B @ undefined mode + msr cpsr_c, r12 + mov sp, r1 + + bic r12, r12, #0x1F + orr r12, r12, #0x12 @ IRQ mode + msr cpsr_c, r12 + mov sp, r2 + + bic r12, r12, #0x1F + orr r12, r12, #0x11 @ FIQ mode + msr cpsr_c, r12 + mov sp, r3 + + bic r12, r12, #0xDF @ clear I and F bits as well as mode bits (0xC0 | 0x1F) + orr r12, r12, #0x13 @ supervisor mode + msr cpsr_c, r12 +#else + cps #0x17 @ Abort Mode + mov sp, r0 + cps #0x1B @ Undefined Mode + mov sp, r1 + cps #0x12 @ IRQ Mode + mov sp, r2 + cps #0x11 @ FIQ Mode + mov sp, r3 + cps #0x13 @ Supervisor Mode +#endif + bx lr + +#ifdef TARGET_ARMV5 + +#warning TODO: rewrite this for armv5 too + +.macro ABORT_HANDLER name, handle, ps + .global \name + .extern \handle +\name: + push {r0-r7,lr} @ push GPRs and the LR. r8-r12 are saved by the C code + mrs r0, spsr @ prepare CPSR for saving + push {r0} + mov r0, sp @ prepare register pointer + mrs r1, cpsr + bic r1, r1, #0x1F + orr r1, r1, #0x13 @ switch to supervisor mode for this next call + msr cpsr_c, r1 + bl \handle @ handle the fault here + mrs r1, cpsr + bic r1, r1, #0x1F + orr r1, r1, #\ps @ back to the specific mode + msr cpsr_c, r1 + pop {r0} + msr spsr, r0 @ restore old CPSR + pop {r0-r7,lr} @ restore GPRs and the LR + subs pc, lr, #4 @ and finally return +.endm + +#else + +.macro ABORT_HANDLER name, handle, ps + .global \name + .extern \handle +\name: + push {r0-r7,lr} @ push GPRs and the LR. r8-r12 are saved by the C code + mrs r0, spsr @ prepare CPSR for saving + push {r0} + mov r0, sp @ prepare register pointer + cps #0x13 @ switch to supervisor mode for this next call + bl \handle @ handle the fault here + cps #\ps @ back to the specific mode + pop {r0} + msr spsr, r0 @ restore old CPSR + pop {r0-r7,lr} @ restore GPRs and the LR + subs pc, lr, #4 @ and finally return +.endm + +#endif + +@ABORT_HANDLER KiPrefetchAbortHandler, KiHandleInstructionFault, 0x17 +@ABORT_HANDLER KiDataAbortHandler, KiHandleDataFault, 0x17 +@ABORT_HANDLER KiUndefinedInstructionHandler, KeOnUndefinedInstruction, 0x17 +@ABORT_HANDLER KiIrqHandler, HalOnInterruptRequest, 0x12 +@ABORT_HANDLER KiFiqHandler, HalOnFastInterruptRequest, 0x11 + + .global KiIrqHandler +KiIrqHandler: + sub lr, lr, #4 + push {r0-r12, lr} + mrs r0, spsr + push {r0} + push {r0} @ dummy to push lr_svc + mov r0, sp + cps #0x13 + str lr, [r0] + bl HalOnInterruptRequest + ldr lr, [r0] + cps #0x12 + pop {r0} + pop {r0} + msr spsr, r0 + pop {r0-r12, lr} + movs pc, lr + + .global KiFiqHandler +KiFiqHandler: + sub lr, lr, #4 + push {r0-r12, lr} + mrs r0, spsr + push {r0} + push {r0} @ dummy to push lr_svc + mov r0, sp + cps #0x13 + str lr, [r0] + bl HalOnFastInterruptRequest + ldr lr, [r0] + cps #0x11 + pop {r0} + pop {r0} + msr spsr, r0 + pop {r0-r12, lr} + movs pc, lr + + .global KiPrefetchAbortHandler +KiPrefetchAbortHandler: + sub lr, lr, #4 + push {r0-r12, lr} + mrs r0, spsr + push {r0} + push {r0} @ dummy to push lr_svc + mov r0, sp + cps #0x13 + str lr, [r0] + bl KiHandleInstructionFault + ldr lr, [r0] + cps #0x17 + pop {r0} + pop {r0} + msr spsr, r0 + pop {r0-r12, lr} + movs pc, lr + + .global KiDataAbortHandler +KiDataAbortHandler: + sub lr, lr, #8 + push {r0-r12, lr} + mrs r0, spsr + push {r0} + push {r0} @ dummy to push lr_svc + mov r0, sp + cps #0x13 + str lr, [r0] + bl KiHandleDataFault + ldr lr, [r0] + cps #0x17 + pop {r0} + pop {r0} + msr spsr, r0 + pop {r0-r12, lr} + movs pc, lr + + .global KiUndefinedInstructionHandler +KiUndefinedInstructionHandler: + push {r0-r12, lr} + mrs r0, spsr + push {r0} + push {r0} @ dummy to push lr_svc + mov r0, sp + cps #0x13 + str lr, [r0] + bl KiHandleUndefinedInstructionFault + ldr lr, [r0] + cps #0x1B + pop {r0} + pop {r0} + msr spsr, r0 + pop {r0-r12, lr} + movs pc, lr + + .global KiSoftwareInterruptHandler + .extern KiSystemServiceHandler +KiSoftwareInterruptHandler: + push {r0-r12, lr} + mrs r0, spsr + push {r0} + cps #0x1F @ enter system mode temporarily + mov r1, sp @ to back up user SP + cpsie if, #0x13 + push {r1} @ this is the Lr_Svc slot in KREGISTERS, but reuse it + mov r0, sp + bl KiSystemServiceHandler + pop {r1} + cpsid if, #0x1F @ enter system mode temporarily + mov sp, r1 @ to restore user SP + cps #0x13 + pop {r0} + msr spsr, r0 + pop {r0-r12, lr} + movs pc, lr diff --git a/boron/source/ke/arm/traplist.S b/boron/source/ke/arm/traplist.S new file mode 100644 index 00000000..e69de29b diff --git a/boron/source/ke/arm/traps.c b/boron/source/ke/arm/traps.c new file mode 100644 index 00000000..9190a989 --- /dev/null +++ b/boron/source/ke/arm/traps.c @@ -0,0 +1,59 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + ke/armv6/traps.c + +Abstract: + This header file implements support for the IDT (Interrupt + Dispatch Table). + +Author: + iProgramInCpp - 28 December 2025 +***/ +#include +#include +#include +#include +#include + +bool KiInitHandlingInstructionFault; + +bool KiHandlingInstructionFault() +{ + if (!KeGetCurrentThread()) + return KiInitHandlingInstructionFault; + + return KeGetCurrentThread()->HandlingInstructionFault; +} + +static void KiSetHandlingInstructionFault(bool If) +{ + if (!KeGetCurrentThread()) { + KiInitHandlingInstructionFault = If; + return; + } + + KeGetCurrentThread()->HandlingInstructionFault = If; +} + +PKREGISTERS KiHandleInstructionFault(PKREGISTERS Registers) +{ + KiSetHandlingInstructionFault(true); + KeOnPageFault(Registers); + return Registers; +} + +PKREGISTERS KiHandleDataFault(PKREGISTERS Registers) +{ + KiSetHandlingInstructionFault(false); + KeOnPageFault(Registers); + return Registers; +} + +PKREGISTERS KiHandleUndefinedInstructionFault(PKREGISTERS Registers) +{ + KeOnUndefinedInstruction(Registers); + return Registers; +} diff --git a/boron/source/ke/crash.c b/boron/source/ke/crash.c index 8e76cf2c..bc9f92cb 100644 --- a/boron/source/ke/crash.c +++ b/boron/source/ke/crash.c @@ -53,7 +53,13 @@ void KeCrashConclusion(const char* Message) KiDebugPrintLock.Locked = 0; #endif - snprintf(CrashBuffer, sizeof CrashBuffer, "\n\x1B[91m*** STOP (CPU %u): \x1B[0m %s\n", KeGetCurrentPRCB()->LapicId, Message); + snprintf( + CrashBuffer, + sizeof CrashBuffer, + "\n\x1B[91m*** STOP (CPU %u): \x1B[0m %s\n", + (unsigned)KeGetCurrentPRCB()->LapicId, + Message + ); HalDisplayString(CrashBuffer); diff --git a/boron/source/ke/dispatch.c b/boron/source/ke/dispatch.c index 6af1db26..f0858965 100644 --- a/boron/source/ke/dispatch.c +++ b/boron/source/ke/dispatch.c @@ -551,3 +551,12 @@ void KiDispatchSoftwareInterrupts(KIPL NewIpl) KeRestoreInterrupts(Restore); } + +void KeDispatchPendingSoftInterrupts() +{ + PKPRCB Prcb = KeGetCurrentPRCB(); + KIPL Ipl = Prcb->Ipl; + + if (Prcb->PendingSoftInterrupts >> Ipl) + KiDispatchSoftwareInterrupts(Ipl); +} diff --git a/boron/source/ke/except.c b/boron/source/ke/except.c index 5be0f277..8105b3c5 100644 --- a/boron/source/ke/except.c +++ b/boron/source/ke/except.c @@ -32,6 +32,22 @@ Module name: UNUSED uint64_t FaultMode = TrapFrame->ErrorCode; \ UNUSED int Vector = TrapFrame->IntNumber +#elif defined TARGET_ARM + +// TODO: an actual vector number +#define KI_EXCEPTION_HANDLER_INIT() \ + UNUSED uint32_t FaultPC = TrapFrame->Lr; \ + UNUSED uint32_t FaultAddress, FaultMode; \ + UNUSED uint32_t Vector = 0; \ + if (KiHandlingInstructionFault()) { \ + FaultAddress = KiReadIfar(); \ + FaultMode = KiReadIfsr() & 0xFFFF; \ + FaultMode |= MM_FAULT_INSNFETCH; \ + } else { \ + FaultAddress = KiReadDfar(); \ + FaultMode = KiReadDfsr() & 0xFFFF; \ + } \ + #else #error Go implement KI_EXCEPTION_HANDLER_INIT! @@ -61,7 +77,13 @@ void KeOnDoubleFault(PKREGISTERS TrapFrame) void KeOnProtectionFault(PKREGISTERS TrapFrame) { KI_EXCEPTION_HANDLER_INIT(); - KeCrash("General Protection Fault at %p on CPU %u", FaultPC, KeGetCurrentPRCB()->LapicId); + KeCrash("General protection fault at %p on CPU %u", FaultPC, KeGetCurrentPRCB()->LapicId); +} + +void KeOnUndefinedInstruction(PKREGISTERS TrapFrame) +{ + KI_EXCEPTION_HANDLER_INIT(); + KeCrash("Undefined instruction at %p on CPU %u", FaultPC, KeGetCurrentPRCB()->LapicId); } extern void MmProbeAddressSubEarlyReturn(); @@ -71,8 +93,8 @@ void KeOnPageFault(PKREGISTERS TrapFrame) KI_EXCEPTION_HANDLER_INIT(); #ifdef DEBUG2 - DbgPrint("handling fault ip=%p, faultaddr=%p, faultmode=%p", FaultPC, FaultAddress, FaultMode); #endif + DbgPrint("handling fault ip=%p, faultaddr=%p, faultmode=%p", FaultPC, FaultAddress, FaultMode); BSTATUS FaultReason = STATUS_SUCCESS; PKTHREAD Thread = KeGetCurrentThread(); @@ -87,7 +109,7 @@ void KeOnPageFault(PKREGISTERS TrapFrame) // instead, immediately return the specific status. FaultReason = STATUS_IPL_TOO_HIGH; } - else + else if (Thread) { // If there are any other immediately observable bad accesses, handle them here. // For example, perhaps we might force the first page (0-0xFFF) to always be @@ -122,23 +144,19 @@ void KeOnPageFault(PKREGISTERS TrapFrame) // Instead of crashing, just modify the trap frame to point to the return // instruction of MmProbeAddressSubEarlyReturn, and RAX to return STATUS_FAULT. #ifdef TARGET_AMD64 - TrapFrame->rip = (uint64_t) MmProbeAddressSubEarlyReturn; TrapFrame->rax = (uint64_t) STATUS_FAULT; - return; - #elif defined TARGET_I386 - TrapFrame->Eip = (uint32_t) MmProbeAddressSubEarlyReturn; TrapFrame->Eax = (uint32_t) STATUS_FAULT; - return; - + #elif defined TARGET_ARM + TrapFrame->Lr = (uint32_t) MmProbeAddressSubEarlyReturn; + TrapFrame->R0 = (uint32_t) STATUS_FAULT; + return; #else - #error Hey! - #endif } } diff --git a/boron/source/ke/i386/debug.c b/boron/source/ke/i386/debug.c index a2a7943b..518bce6b 100644 --- a/boron/source/ke/i386/debug.c +++ b/boron/source/ke/i386/debug.c @@ -3,7 +3,7 @@ Copyright (C) 2025 iProgramInCpp Module name: - ke/i386/debug.c + ke/i386/debug.c Abstract: This module implements architecture specific debugging @@ -117,6 +117,12 @@ void DbgPrintDouble(const char* String) HalDisplayString(String); } +void DbgDumpPageTables() +{ + // TODO + DbgPrint("You really need this? Add it. It's supposed to dump the current page table."); +} + void DbgPrintStackTrace(uintptr_t Ebp) { if (Ebp == 0) diff --git a/boron/source/ke/i386/probe.c b/boron/source/ke/i386/probe.c index 7d5b7d47..a1629bb0 100644 --- a/boron/source/ke/i386/probe.c +++ b/boron/source/ke/i386/probe.c @@ -84,7 +84,7 @@ BSTATUS MmProbeAddress(void* Address, size_t Length, bool ProbeWrite, KPROCESSOR return STATUS_SUCCESS; } -// Defined in arch/amd64/misc.asm +// Defined in arch/i386/misc.asm int MmSafeCopySub(void* Address, const void* Source, size_t Length); BSTATUS MmSafeCopy(void* Address, const void* Source, size_t Length, KPROCESSOR_MODE AccessMode, bool VerifyDest) diff --git a/boron/source/ke/i386/traps.c b/boron/source/ke/i386/traps.c index 2f66725a..d57db8c7 100644 --- a/boron/source/ke/i386/traps.c +++ b/boron/source/ke/i386/traps.c @@ -215,6 +215,12 @@ PKREGISTERS KiHandleProtectionFault(PKREGISTERS Regs) return Regs; } +PKREGISTERS KiHandleUndefinedInstructionFault(PKREGISTERS Regs) +{ + KeOnUndefinedInstruction(Regs); + return Regs; +} + PKREGISTERS KiHandlePageFault(PKREGISTERS Regs) { KeOnPageFault(Regs); diff --git a/boron/source/ke/ki.h b/boron/source/ke/ki.h index 3585c1a4..b67957ac 100644 --- a/boron/source/ke/ki.h +++ b/boron/source/ke/ki.h @@ -113,4 +113,24 @@ bool KiCancelTimer(PKTIMER Timer); void KiSwitchArchSpecificContext(PKTHREAD NewThread, PKTHREAD OldThread); +#ifdef TARGET_ARM + +void KiSaveInterruptStacks( + uintptr_t* AbortStack, + uintptr_t* UndefinedStack, + uintptr_t* IrqStack, + uintptr_t* FiqStack +); + +void KiRestoreInterruptStacks( + uintptr_t AbortStack, + uintptr_t UndefinedStack, + uintptr_t IrqStack, + uintptr_t FiqStack +); + +bool KiHandlingInstructionFault(); + +#endif + #endif//BORON_KE_KI_H diff --git a/boron/source/ke/limreq.c b/boron/source/ke/limreq.c deleted file mode 100644 index c6f81f90..00000000 --- a/boron/source/ke/limreq.c +++ /dev/null @@ -1,53 +0,0 @@ -/*** - The Boron Operating System - Copyright (C) 2023 iProgramInCpp - -Module name: - ke/limreq.c - -Abstract: - This module contains the list of Limine bootloader requests. - -Author: - iProgramInCpp - 23 September 2023 -***/ -#include -#include <_limine.h> - -#if 0 -#include - -// NOTE: Requesting the kernel file for two reasons: -// - 1. It's a possibility that I'll be phasing out the existing symbol table system and -// - 2. It seems like Limine unconditionally occupies bootloader reclaimable memory with the kernel file. - -// Note: This MUST match the order of the KLGR enum. -static volatile void* const KepLimineRequestTable[] = -{ - NULL, - &KeLimineHhdmRequest, - &KeLimineFramebufferRequest, - &KeLimineMemMapRequest, - &KeLimineSmpRequest, - &KeLimineRsdpRequest, - &KeLimineModuleRequest, - &KeLimineKernelFileRequest, -}; - -volatile void* KeLimineGetRequest(int RequestId) -{ - if (RequestId <= KLGR_NONE || RequestId >= KLGR_COUNT) - return NULL; - - return KepLimineRequestTable[RequestId]; -} - -const char* KeGetBootCommandLine() -{ - const char* cmdLine = KeLimineKernelFileRequest.response->kernel_file->cmdline; - if (!cmdLine) - cmdLine = ""; - - return cmdLine; -} -#endif diff --git a/boron/source/ke/lock.c b/boron/source/ke/lock.c index 82bdeb54..e40d2eec 100644 --- a/boron/source/ke/lock.c +++ b/boron/source/ke/lock.c @@ -15,6 +15,7 @@ Module name: #include #include +#include // Note! The reason we raise IPL when locking a spinlock is that we don't want // the scheduler to interrupt the critical section, thus wasting precious cycles @@ -47,14 +48,18 @@ void KeAcquireSpinLock(PKSPIN_LOCK SpinLock, PKIPL OldIpl) if (KeGetProcessorCount() == 1 && SpinLock->Locked) { #ifdef SPINLOCK_TRACK_PC + #ifdef IS_64_BIT KeCrash("KeAcquireSpinLock: spinlock already locked by %p", SpinLock->Pc | 0xFFFF000000000000); + #else + KeCrash("KeAcquireSpinLock: spinlock already locked by %p", SpinLock->Pc); + #endif #else KeCrash("KeAcquireSpinLock: spinlock already locked"); #endif } #endif - + while (true) { if (!AtTestAndSetMO(SpinLock->Locked, ATOMIC_MEMORD_ACQUIRE)) @@ -76,7 +81,9 @@ void KeAcquireSpinLock(PKSPIN_LOCK SpinLock, PKIPL OldIpl) // Use regular reads instead of atomic reads to minimize bus contention while (SpinLock->Locked) + { KeSpinningHint(); + } } } diff --git a/boron/source/ke/sched.c b/boron/source/ke/sched.c index cdc00efe..a8ed935f 100644 --- a/boron/source/ke/sched.c +++ b/boron/source/ke/sched.c @@ -677,6 +677,10 @@ void KiSwitchToNextThread() { KiAssertOwnDispatcherLock(); +#ifdef TARGET_ARM + bool Restore = KeDisableInterrupts(); +#endif + PKSCHEDULER Scheduler = KiGetCurrentScheduler(); // Load other properties about the thread. @@ -737,10 +741,32 @@ void KiSwitchToNextThread() // Set the relevant MSRs. KeSetMSR(MSR_GS_BASE_KERNEL, (uintptr_t) Thread->Process->PebPointer); KeSetMSR(MSR_FS_BASE, (uintptr_t) Thread->TebPointer); + +#elif defined TARGET_ARM + + if (OldThread) { + KiSaveInterruptStacks( + &OldThread->AbtStack, + &OldThread->UndStack, + &OldThread->IrqStack, + &OldThread->FiqStack + ); + } + + KiRestoreInterruptStacks( + Thread->AbtStack, + Thread->UndStack, + Thread->IrqStack, + Thread->FiqStack + ); + #endif if (OldThread == Thread) { + #ifdef TARGET_ARM + KeRestoreInterrupts(Restore); + #endif // The old thread is the same as the new thread, there's no need to // do anything anymore. return; @@ -759,6 +785,9 @@ void KiSwitchToNextThread() else KiSwitchThreadStackForever(Thread->StackPointer); +#ifdef TARGET_ARM + KeRestoreInterrupts(Restore); +#endif // NOTE: Beyond this point, you CANNOT use "Thread" anymore to refer to the // thread that was just switched to. You have to use "OldThread" - that was // the thread that was switched from when it saved its state in diff --git a/boron/source/ke/smp.c b/boron/source/ke/smp.c index dce0f319..df48d875 100644 --- a/boron/source/ke/smp.c +++ b/boron/source/ke/smp.c @@ -22,7 +22,7 @@ Module name: #include "ki.h" KPRCB** KeProcessorList; -int KeProcessorCount = 0; +int KeProcessorCount = 1; uint32_t KeBootstrapLapicId = 0; KPRCB** KiGetProcessorList() { return KeProcessorList; } @@ -70,6 +70,7 @@ void KiCPUBootstrap(PLOADER_AP LoaderAp) // Update the IPL when initing. Currently we start at the highest IPL KeOnUpdateIPL(KeGetIPL(), 0); + ENABLE_INTERRUPTS(); KeInitCPU(); diff --git a/boron/source/ke/thread.c b/boron/source/ke/thread.c index 7531b2f0..c081c68a 100644 --- a/boron/source/ke/thread.c +++ b/boron/source/ke/thread.c @@ -29,8 +29,15 @@ void KiInitializeThread(PKTHREAD Thread, void* KernelStack, size_t KernelStackSi Thread->StartRoutine = StartRoutine; Thread->StartContext = StartContext; +#ifdef TARGET_ARM + ASSERT(KernelStackSize > KERNEL_INTERRUPT_STACK_SIZE); + + Thread->InterruptStack = (void*)((uintptr_t) KernelStack + KernelStackSize - KERNEL_INTERRUPT_STACK_SIZE); + KernelStackSize -= KERNEL_INTERRUPT_STACK_SIZE; +#endif + Thread->Stack.Top = KernelStack; - Thread->Stack.Size = KernelStackSize; //MmGetSizeFromPoolAddress(KernelStack) * PAGE_SIZE; + Thread->Stack.Size = KernelStackSize; Thread->Mode = MODE_KERNEL; diff --git a/boron/source/ldr/dll.c b/boron/source/ldr/dll.c index 36d9d619..69d8997d 100644 --- a/boron/source/ldr/dll.c +++ b/boron/source/ldr/dll.c @@ -86,6 +86,7 @@ static void LdriMapInProgramHeader(PLOADER_MODULE File, PELF_PROGRAM_HEADER Phdr if (!MmIsEqualPte(Pte1, *Pte)) KeInvalidatePage((void*) VirtAddr); + VirtAddr += PAGE_SIZE; continue; } diff --git a/boron/source/ldr/loader.c b/boron/source/ldr/loader.c index d4b27663..f484cac8 100644 --- a/boron/source/ldr/loader.c +++ b/boron/source/ldr/loader.c @@ -24,6 +24,11 @@ static const char* LdrpHalPath = "halx86.sys"; static uintptr_t LdrpCurrentBase = 0xD2000000; static const char* LdrpHalPath = "hali386.sys"; // sorry bucko, halx86 is already taken +#elif defined TARGET_ARM + +static uintptr_t LdrpCurrentBase = 0xD2000000; +static const char* LdrpHalPath = "halipod1,1.sys"; // TODO: make this configurable. temporary + #else #error Define your loader base and HAL path here. @@ -101,7 +106,7 @@ void LdrInit() return; } - LdriLoadFile(HalFile); + LdriLoadDll(HalFile); } INIT diff --git a/boron/source/mm/amd64/pt.c b/boron/source/mm/amd64/pt.c index 434e849b..a47ae359 100644 --- a/boron/source/mm/amd64/pt.c +++ b/boron/source/mm/amd64/pt.c @@ -118,6 +118,12 @@ HPAGEMAP MiCreatePageMapping() return (HPAGEMAP) NewPageMappingResult; } +// Frees a page mapping. +void MiFreePageMapping(HPAGEMAP PageMap) +{ + return MmFreePhysicalPage(MmPhysPageToPFN(PageMap)); +} + /*** Function description: Gets the PTE (Page Table Entry) pointer for the specified address. diff --git a/boron/source/mm/amd64/pte.c b/boron/source/mm/amd64/pte.c index 5c3071d1..078bf911 100644 --- a/boron/source/mm/amd64/pte.c +++ b/boron/source/mm/amd64/pte.c @@ -191,3 +191,8 @@ bool MmIsFromPmmPte(MMPTE Pte) { return (MmIsPresentPte(Pte) || MmWasPresentPte(Pte)) && (MmHardwarePte(Pte) & MM_AMD64_PTE_ISFROMPMM); } + +void MmFlushTlbUpdates() +{ + // On amd64, the TLB is coherent against the data cache, so no need for anything +} diff --git a/boron/source/mm/arm/idmap.c b/boron/source/mm/arm/idmap.c new file mode 100644 index 00000000..f8b716a2 --- /dev/null +++ b/boron/source/mm/arm/idmap.c @@ -0,0 +1,90 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + mm/armv6/idmap.c + +Abstract: + This module implements identity mapping management for + the armv6 platform. + +Author: + iProgramInCpp - 30 December 2025 +***/ +#include "../mi.h" +#include + +#define P2V(n) ((void*)(MI_IDENTMAP_START + (n)) +#define V2P(p) ((uintptr_t)(p) - MI_IDENTMAP_START) + +// TODO: keep these mnemonics in one place +#define L1PTE(Address) (((uintptr_t)(Address) & ~0x3FF) | MM_ARM_PTEL1_COARSE_PAGE_TABLE) +#define L2PTE(Pfn) MmHardwarePte(MmBuildPte(Pfn, MM_PROT_READ | MM_PROT_WRITE)) +/* #define L2PTE(Pfn) (MM_PTE_NEWPFN(Pfn) | MM_ARM_PTEL2_TYPE_SMALLPAGE | MM_PTE_READWRITE) */ + +#define L1PTE_FLAGS_SEC 0b001010000001110 // Level 1 PTE flags for Section + +extern uint32_t KiRootPageTable[]; +extern uint32_t KiRootPageTableJibbie[]; +extern uint32_t KiRootPageTableDebbie[]; +extern uint32_t KiExceptionHandlerTable[]; +extern uint32_t KiPoolHeadersPageTables[]; + +void MiInitializeBaseIdentityMapping() +{ + for (size_t i = 0, j = MI_IDENTMAP_START >> 20; + i < MI_IDENTMAP_SIZE; + i += 1024 * 1024, j++) + { + uintptr_t Address = MI_IDENTMAP_START_PHYS + i; + KiRootPageTable[j] = Address | L1PTE_FLAGS_SEC; + } + + // TODO: is this right? + for (uintptr_t i = 0; i < MI_POOL_HEADERS_SIZE; i += 1024 * 1024) + { + uintptr_t Address = MI_POOL_HEADERS_START + i; + KiRootPageTable[Address >> 20] = ((uintptr_t) &KiPoolHeadersPageTables[i / PAGE_SIZE]) | MM_ARM_PTEL1_COARSE_PAGE_TABLE; + } + + // setup a linear page table view of the root page table. + MMPFN RootPageTablePfn = (uintptr_t)KiRootPageTable >> 12; + + for (int i = 0; i < 4; i++) { + KiRootPageTableJibbie[i] = L2PTE(RootPageTablePfn + i); + } + + uintptr_t JibbieAddress = (uintptr_t) KiRootPageTableJibbie; + uintptr_t DebbieAddress = (uintptr_t) KiRootPageTableDebbie; + + KiRootPageTableJibbie[4] = L2PTE(MmPhysPageToPFN(JibbieAddress)); + KiRootPageTableJibbie[5] = L2PTE(MmPhysPageToPFN(DebbieAddress)); + KiRootPageTableJibbie[1008] = L2PTE(MmPhysPageToPFN((uintptr_t) KiExceptionHandlerTable)); + + for (int i = 512; i < 1024; i++) { + if (KiRootPageTable[i * 4]) { + MMPFN Pfn = MM_ARM_PTE_PFN(KiRootPageTable[i * 4]); + KiRootPageTableDebbie[i] = L2PTE(Pfn); + } + else { + KiRootPageTableDebbie[i] = 0; + } + } + + KeFlushTLB(); + for (int i = 0; i < 4; i++) { + KiRootPageTable[4088 + i] = L1PTE(DebbieAddress + i * 1024); + KiRootPageTable[4092 + i] = L1PTE(JibbieAddress + i * 1024); + } + + // Disable Lower Half + uint32_t* RootPageTableUpperHalf = (uint32_t*)((uintptr_t)KiRootPageTable | 0xC0000000); + for (size_t i = 0; i < 2048; i++) { + RootPageTableUpperHalf[i] = 0; + } + + MmFlushTlbUpdates(); + KeSweepIcache(); + KeSweepDcache(); +} diff --git a/boron/source/mm/arm/pt.c b/boron/source/mm/arm/pt.c new file mode 100644 index 00000000..c06cda36 --- /dev/null +++ b/boron/source/mm/arm/pt.c @@ -0,0 +1,357 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + mm/arm/pt.c + +Abstract: + This module implements page table management for + the arm platform. + +Author: + iProgramInCpp - 25 December 2025 +***/ + +#include +#include +#include +#include +#include "../mi.h" + +static MMPTE MmBuildL1Pte(uintptr_t Address) +{ + MMPTE Pte; + MmHardwarePte(Pte) = (Address & ~0x3FF) | MM_ARM_PTEL1_COARSE_PAGE_TABLE; + return Pte; +} + +extern char KiExceptionHandlerTable[]; // NOTE: This is a *PHYSICAL* address! + +HPAGEMAP MiCreatePageMapping() +{ + MMPFN NewPageMapping = MmAllocatePhysicalContiguousRegion(4, 0x3FFF); + if (NewPageMapping == PFN_INVALID) + { + DbgPrint("Error, can't create a new page mapping, because we can't create the first level."); + return 0; + } + + // This page is referenced in the L1 of the new page mapping. + // It's done this way so that we can actually implement a self-referencing + // page mapping (a.k.a. fractal page mapping), without hardware support for + // it. + MMPFN Jibbie = MmAllocatePhysicalPage(); + + // Inside of Jibbie we place four PTEs: one which indexes the root page map + // at no offset, one at +4K, one at +8K, and one at +12K. Then we also place + // a reference to Jibbie inside of itself, and also to Debbie. + + // That's the L1 mapped, now we need to map the L2s. There are 4096 PTEs, + // where each of these PTEs matches up to a 1MB section, and each L2 page + // table is 1KB in size. So really, Boron will treat it as 1024 PTEs that + // point to 4KB L2 page tables. (these logical PTEs are each equivalent to + // FOUR different hardware L1 PTEs.) + // + // In order to map all 4 MB of L2 page tables, we need another page frame. + MMPFN Debbie = MmAllocatePhysicalPage(); + + if (Jibbie == PFN_INVALID || Debbie == PFN_INVALID) + { + if (Jibbie != PFN_INVALID) + MmFreePhysicalPage(Jibbie); + + if (Debbie != PFN_INVALID) + MmFreePhysicalPage(Debbie); + + MmFreePhysicalContiguousRegion(NewPageMapping, 4); + DbgPrint("Error, can't create a new page mapping, because we can't allocate either Jibbie or Debbie."); + return 0; + } + + // Lock the kernel space's lock to not get any surprises. + MmLockKernelSpaceShared(); + MmBeginUsingHHDM(); + + PMMPTE JibbiePtr = MmGetHHDMOffsetAddr(MmPFNToPhysPage(Jibbie)); + memset(JibbiePtr, 0, PAGE_SIZE); + + for (int i = 0; i < 4; i++) + JibbiePtr[i] = MmBuildPte(NewPageMapping + i, MM_PROT_READ | MM_PROT_WRITE); + + JibbiePtr[4] = MmBuildPte(Jibbie, MM_MISC_IS_FROM_PMM | MM_PROT_READ | MM_PROT_WRITE); + JibbiePtr[5] = MmBuildPte(Debbie, MM_MISC_IS_FROM_PMM | MM_PROT_READ | MM_PROT_WRITE); + + // The 1008th entry of Jibbie maps the exception handler pointers + // (at 0xFFFF0000), so map it too. Note that KiExceptionHandlerTable + // is a physical address. + JibbiePtr[1008] = MmBuildPte(MmPhysPageToPFN((uintptr_t) KiExceptionHandlerTable), MM_PROT_READ | MM_PROT_WRITE | MM_PROT_EXEC); + + PMMPTE RootPtr = MmGetHHDMOffsetAddr(MmPFNToPhysPage(NewPageMapping)); + PMMPTE OldRootPtr = (PMMPTE) MI_PML1_LOCATION; + memset(RootPtr, 0, PAGE_SIZE * 4); + + // Copy the higher half from the old root to the new root. + for (int i = 2048; i < 4096; i++) { + RootPtr[i] = OldRootPtr[i]; + } + + // And also, to Debbie. + PMMPTE DebbiePtr = MmGetHHDMOffsetAddr(MmPFNToPhysPage(Debbie)); + memset(DebbiePtr, 0, PAGE_SIZE); + + MMPTE ZeroPte = MmBuildZeroPte(); + for (int i = 512; i < 1024; i++) { + if (!MmIsEqualPte(OldRootPtr[i * 4], ZeroPte)) { + MMPFN Pfn = MmGetPfnPte(OldRootPtr[i * 4]); + DebbiePtr[i] = MmBuildPte(Pfn, MM_PROT_READ | MM_PROT_WRITE); + } + } + + // Replace the last few entries with pointers to Jibbie and Debbie. + uintptr_t JibbieAddress = Jibbie * PAGE_SIZE; + uintptr_t DebbieAddress = Debbie * PAGE_SIZE; + for (int i = 0; i < 4; i++) { + RootPtr[4088 + i] = MmBuildL1Pte(DebbieAddress + i * 1024); + RootPtr[4092 + i] = MmBuildL1Pte(JibbieAddress + i * 1024); + } + + MmEndUsingHHDM(); + MmFlushTlbUpdates(); + MmUnlockKernelSpace(); + + DbgPrint("ROOT: %p", NewPageMapping*PAGE_SIZE); + DbgPrint("Jibbie: %p", Jibbie*PAGE_SIZE); + DbgPrint("Debbie: %p", Debbie*PAGE_SIZE); + + uintptr_t NewPageMappingResult = MmPFNToPhysPage (NewPageMapping); + return (HPAGEMAP) NewPageMappingResult; +} + +void MiFreePageMapping(HPAGEMAP PageMap) +{ + MMPFN Jibbie, Debbie, Root; + + Root = MmPhysPageToPFN(PageMap); + + MmBeginUsingHHDM(); + PMMPTE RootPtr = MmGetHHDMOffsetAddr(PageMap); + + // this works because MM_PTE_PFN fetches bits [31..12], and the root entries + // have the coarse page table address from bits [31..10] + Debbie = MmGetPfnPte(RootPtr[4088]); + Jibbie = MmGetPfnPte(RootPtr[4092]); + + MmEndUsingHHDM(); + + MmFreePhysicalPage(Jibbie); + MmFreePhysicalPage(Debbie); + MmFreePhysicalContiguousRegion(Root, 4); + MmFlushTlbUpdates(); +} + +bool MmCheckPteLocationAllocator( + uintptr_t Address, + bool GenerateMissingLevels, + MM_PAGE_ALLOCATOR_METHOD PageAllocate +) +{ + ASSERT(Address < MI_PML1_LOCATION && "MmCheckPteLocation for regions inside the L1 and L2 maps is unimplemented."); + + MMADDRESS_CONVERT Convert; + Convert.Long = Address; + + uintptr_t IsFromPmmFlag = 0; + if (PageAllocate == &MmAllocatePhysicalPage) + IsFromPmmFlag = MM_MISC_IS_FROM_PMM; + + // check if the corresponding L1 page table is present through Jibbie. + PMMPTE PtePtr = (PMMPTE) MI_PML1_LOCATION; + if (!MmIsPresentPte(PtePtr[Convert.Level1Index])) + { + if (!GenerateMissingLevels) + return false; + + int Index = Convert.Level1Index & ~3; + MMPFN Pfn = PageAllocate(); + if (Pfn == PFN_INVALID) { + DbgPrint("MmCheckPteLocation: Out of memory!"); + return false; + } + + for (int i = 0; i < 4; i++) { + PtePtr[Index + i] = MmBuildL1Pte(Pfn * PAGE_SIZE + i * 1024); + } + + // Debbie must also be updated. + PtePtr = (PMMPTE) MI_PML2_MIRROR_LOCATION; + PtePtr[Convert.Level1Index >> 2] = MmBuildPte(Pfn, IsFromPmmFlag | MM_PROT_READ | MM_PROT_WRITE); + MmFlushTlbUpdates(); + } + + // Page table exists. + return true; +} + +bool MmCheckPteLocation(uintptr_t Address, bool GenerateMissingLevels) +{ + return MmCheckPteLocationAllocator(Address, GenerateMissingLevels, MmAllocatePhysicalPage); +} + +PMMPTE MmGetPteLocationCheck(uintptr_t Address, bool GenerateMissingLevels) +{ + if (!MmCheckPteLocation(Address, GenerateMissingLevels)) + return NULL; + + return MmGetPteLocation(Address); +} + +PMMPTE MmGetPteLocation(uintptr_t Address) +{ + ASSERT(Address < MI_PML1_LOCATION && "MmGetPteLocation for regions inside the L1 and L2 maps is unimplemented."); + + PMMPTE PtePtr = (PMMPTE)MI_PTE_LOC(Address); + + // HACK: Instead of just invalidating everything in the function + // MiFreeUnusedMappingLevelsInCurrentMap like I am supposed to, + // I will invalidate the TLB here. + // + // I know this is bad, but come on, when are we *ever* going to + // *not* go through this function? + KeInvalidatePage(PtePtr); + MmFlushTlbUpdates(); + + return PtePtr; +} + +static bool MmpMapSingleAnonPageAtPte(PMMPTE Pte, uintptr_t Permissions, bool NonPaged) +{ + if (!Pte) + return false; + + if (MM_DBG_NO_DEMAND_PAGING || NonPaged) + { + MMPFN pfn = MmAllocatePhysicalPage(); + if (pfn == PFN_INVALID) + { + return false; + } + + if (!Pte) + { + return false; + } + + *Pte = MmBuildPte(pfn, MM_MISC_IS_FROM_PMM | Permissions); + MmFlushTlbUpdates(); + return true; + } + + (void) Permissions; + *Pte = MmBuildAbsentPte(MM_PAGE_COMMITTED); + MmFlushTlbUpdates(); + return true; +} + +bool MiMapAnonPage(uintptr_t Address, uintptr_t Permissions, bool NonPaged) +{ + PMMPTE Pte = MmGetPteLocationCheck(Address, true); + return MmpMapSingleAnonPageAtPte(Pte, Permissions, NonPaged); +} + +bool MiMapPhysicalPage(uintptr_t PhysicalPage, uintptr_t Address, uintptr_t Permissions) +{ + PMMPTE Pte = MmGetPteLocationCheck(Address, true); + if (!Pte) + return false; + + *Pte = MmBuildPte(MmPhysPageToPFN(PhysicalPage), Permissions); + MmFlushTlbUpdates(); + return true; +} + +void MiUnmapPages(uintptr_t Address, size_t LengthPages) +{ + MMPTE ZeroPte = MmBuildZeroPte(); + + for (size_t i = 0; i < LengthPages; i++) + { + PMMPTE pPTE = MmGetPteLocationCheck(Address + i * PAGE_SIZE, false); + if (!pPTE) + continue; + + MMPTE Pte = *pPTE; + *pPTE = ZeroPte; + MmFlushTlbUpdates(); + + if (MmIsPresentPte(Pte)) + MmFreePhysicalPage(MmGetPfnPte(Pte)); + } + + MmFlushTlbUpdates(); + MmIssueTLBShootDown(Address, LengthPages); +} + +uintptr_t MiGetTopOfPoolManagedArea() +{ + return MI_GLOBAL_AREA_START << 22; +} + +uintptr_t MiGetTopOfSecondPoolManagedArea() +{ + return MI_GLOBAL_AREA_START_2ND << 22; +} + +bool MiMapAnonPages(uintptr_t Address, size_t SizePages, uintptr_t Permissions, bool NonPaged) +{ + // As an optimization, we'll wait until the PML1 index rolls over to zero before reloading the PTE pointer. + uint64_t CurrentPtL2Index = PT_L2_IDX(Address); + size_t DonePages = 0; + + PMMPTE PtePtr = MmGetPteLocationCheck(Address, true); + + for (size_t i = 0; i < SizePages; i++) + { + // If one of these fails, then we should roll back. + if (!MmpMapSingleAnonPageAtPte(PtePtr, Permissions, NonPaged)) + goto ROLLBACK; + + // Increase the address size, get the next PTE pointer, update the current PML1, and + // increment the number of mapped pages (since this one was successfully mapped). + Address += PAGE_SIZE; + PtePtr++; + CurrentPtL2Index++; + DonePages++; + + // Despite L2 page tables being 1KB in size, we still scroll through 1024 entries, + // because this implementation of the page tables always allocates L2 page tables + // in 4KB regions. + if (CurrentPtL2Index % (PAGE_SIZE / sizeof(MMPTE)) == 0) + { + // We have rolled over. + PtePtr = MmGetPteLocationCheck(Address, true); + } + } + + // All allocations have succeeded! Let the caller know and don't undo our work. :) + return true; + +ROLLBACK: + // Unmap all the pages that we have mapped. + MiUnmapPages(Address, DonePages); + MmFlushTlbUpdates(); + return false; +} + +uintptr_t MmGetPteBitsFromProtection(int Protection) +{ + uintptr_t Bits = 0; + if (Protection & PAGE_READ) + Bits |= MM_PROT_READ; + if (Protection & PAGE_WRITE) + Bits |= MM_PROT_WRITE; + if (Protection & PAGE_EXECUTE) + Bits |= MM_PROT_EXEC; + + return Bits; +} diff --git a/boron/source/mm/arm/pte.c b/boron/source/mm/arm/pte.c new file mode 100644 index 00000000..e83b880b --- /dev/null +++ b/boron/source/mm/arm/pte.c @@ -0,0 +1,325 @@ +/*** + The Boron Operating System + Copyright (C) 2026 iProgramInCpp + +Module name: + mm/arm/pte.c + +Abstract: + This module implements page table entry management + functions for the ARM platform. + +Author: + iProgramInCpp - 14 March 2026 +***/ +#include +#include + +bool MmIsPresentPte(MMPTE Pte) +{ + return (MmHardwarePte(Pte) & MM_ARM_PTEL1_TYPE) != 0; +} + +bool MmIsPoolHeaderPte(MMPTE Pte) +{ + return !MmIsPresentPte(Pte) && (MmHardwarePte(Pte) & MM_ARM_DPTE_ISPOOLHDR); +} + +bool MmWasPresentPte(MMPTE Pte) +{ + return !MmIsPresentPte(Pte) && (MmHardwarePte(Pte) & MM_ARM_DPTE_WASPRESENT); +} + +bool MmIsCommittedPte(MMPTE Pte) +{ + return !MmIsPresentPte(Pte) && (MmHardwarePte(Pte) & MM_ARM_DPTE_COMMITTED); +} + +bool MmIsDecommittedPte(MMPTE Pte) +{ + return !MmIsPresentPte(Pte) && (MmHardwarePte(Pte) & MM_ARM_DPTE_DECOMMITTED); +} + +MMPTE MmBuildZeroPte() +{ + MMPTE Pte; + MmHardwarePte(Pte) = 0; + return Pte; +} + +MMPTE MmSetPfnPte(MMPTE Pte, MMPFN NewPfn) +{ + MmHardwarePte(Pte) &= ~MM_ARM_PTE_ADDRESSMASK; + MmHardwarePte(Pte) |= MM_ARM_PTE_NEWPFN(NewPfn); + return Pte; +} + +MMPTE MmSetPageBitsPte(MMPTE Pte, uintptr_t PageBits) +{ +/* + MmHardwarePte(Pte) &= ~(MM_I386_PTE_READWRITE + | MM_I386_PTE_USERACCESS + | MM_I386_PTE_CDISABLE + | MM_I386_PTE_ACCESSED + | MM_I386_PTE_DIRTY + | MM_I386_PTE_ISFROMPMM); + + if (PageBits & MM_PROT_WRITE) MmHardwarePte(Pte) |= MM_I386_PTE_READWRITE; + if (PageBits & MM_PROT_USER) MmHardwarePte(Pte) |= MM_I386_PTE_USERACCESS; + if (PageBits & MM_MISC_DISABLE_CACHE) MmHardwarePte(Pte) |= MM_I386_PTE_CDISABLE; + if (PageBits & MM_MISC_ACCESSED) MmHardwarePte(Pte) |= MM_I386_PTE_ACCESSED; // do we even need this? + if (PageBits & MM_MISC_DIRTY) MmHardwarePte(Pte) |= MM_I386_PTE_DIRTY; // do we even need this? + if (PageBits & MM_MISC_IS_FROM_PMM) MmHardwarePte(Pte) |= MM_I386_PTE_ISFROMPMM; + return Pte; +*/ + +#ifdef TARGET_ARMV6 + bool IsPresent = MmIsPresentPte(Pte); + + MmHardwarePte(Pte) &= ~(MM_ARM_AP_MASK // remove permissions + | MM_ARM_PTEL2_TEX_MASK // remove caching information + | MM_ARM_PTEL2_CB_MASK + | MM_ARM_PTEL2_TYPE_SMALLPAGENX // actually removes ALL presence info + ); + + uintptr_t WU = PageBits & (MM_PROT_WRITE | MM_PROT_USER); + switch (WU) + { + case MM_PROT_WRITE | MM_PROT_USER: + MmHardwarePte(Pte) |= MM_ARM_PTEL2_AP_USERREADWRITE; + break; + case MM_PROT_WRITE: + MmHardwarePte(Pte) |= MM_ARM_PTEL2_AP_SUPERREADWRITE; + break; + case MM_PROT_USER: + MmHardwarePte(Pte) |= MM_ARM_PTEL2_AP_BOTHREADONLY; + break; + case 0: + MmHardwarePte(Pte) |= MM_ARM_PTEL2_AP_SUPERREADONLY; + break; + default: + ASSERT(!"Unreachable"); + break; + } + + if (IsPresent) + { + if (PageBits & MM_PROT_EXEC) + MmHardwarePte(Pte) |= MM_ARM_PTEL2_TYPE_SMALLPAGE; + else + MmHardwarePte(Pte) |= MM_ARM_PTEL2_TYPE_SMALLPAGENX; + } + + if (~PageBits & MM_MISC_DISABLE_CACHE) + { + MmHardwarePte(Pte) |= MM_ARM_PTEL2_TEXCB_NORMALMEM; + } +#else + bool IsPresent = MmIsPresentPte(Pte); + + MmHardwarePte(Pte) &= ~(MM_ARM_AP_MASK // remove permissions + | MM_ARM_PTEL2_B + | MM_ARM_PTEL2_C + | MM_ARM_PTEL2_TYPE_EXSMALLPAGE // actually removes ALL presence info + ); + + uintptr_t WU = PageBits & (MM_PROT_WRITE | MM_PROT_USER); + switch (WU) + { + case MM_PROT_WRITE | MM_PROT_USER: + MmHardwarePte(Pte) |= MM_ARM_PTEL2_AP_ALL_USERREADWRITE; + break; + case MM_PROT_WRITE: + MmHardwarePte(Pte) |= MM_ARM_PTEL2_AP_ALL_SUPERREADWRITE; + break; + // bummer. Superuser cannot opt out of writes! + // reminds me of the 80386 which also doesn't have the concept + // of pages read only for kernel mode in addition to user mode. + case MM_PROT_USER: + MmHardwarePte(Pte) |= MM_ARM_PTEL2_AP_ALL_USERREADONLY; + break; + case 0: + MmHardwarePte(Pte) |= MM_ARM_PTEL2_AP_ALL_SUPERREADWRITE; + break; + default: + ASSERT(!"Unreachable"); + break; + } + + if (IsPresent) + { + MmHardwarePte(Pte) |= MM_ARM_PTEL2_TYPE_SMALLPAGE; + } + + if (~PageBits & MM_MISC_DISABLE_CACHE) + { + MmHardwarePte(Pte) |= MM_ARM_PTEL2_C | MM_ARM_PTEL2_B; + } +#endif + + // N.B. Ignoring MM_MISC_IS_FROM_PMM because arm has no such concept as "software-use PTE bits". + + return Pte; +} + +MMPTE MmBuildPte(MMPFN Pfn, uintptr_t PageBits) +{ + MMPTE Pte = MmBuildZeroPte(); + MmHardwarePte(Pte) |= MM_ARM_PTEL2_TYPE_SMALLPAGE; + + Pte = MmSetPfnPte(Pte, Pfn); + Pte = MmSetPageBitsPte(Pte, PageBits); + + return Pte; +} + +MMPTE MmBuildWasPresentPte(MMPTE OldPte) +{ + // not needed on ARM + (void) OldPte; + return MmBuildZeroPte(); +} + +MMPTE MmBuildAbsentPte(uintptr_t PageBits) +{ + MMPTE Pte = MmBuildZeroPte(); + + if (PageBits & MM_PAGE_DECOMMITTED) + MmHardwarePte(Pte) |= MM_ARM_DPTE_DECOMMITTED; + + if (PageBits & MM_PAGE_COMMITTED) + MmHardwarePte(Pte) |= MM_ARM_DPTE_COMMITTED; + + return Pte; +} + +// the structure of the pool header PTE if this is set is as follows: +// +// Address[30:3] 0 1 0 0 +// +// - bits 0 and 1 are cleared because ARM uses the first 2 bits as the PTE's "type" +// - bit 2 is set because that's MM_DPTE_ISPOOLHDR +// - bit 3 is cleared because that's MM_DPTE_COMMITTED and it shouldn't conflict +typedef union +{ + MMPTE_HW Pte; + + struct + { + uintptr_t Present : 2; // MUST be zero + uintptr_t IsPoolHdr : 1; // MUST be ONE + uintptr_t Committed : 1; // MUST be zero + uintptr_t B3to30 : 28; + } + PACKED; +} +MMPTE_POOLHEADER; + +static_assert(sizeof(MMPTE_POOLHEADER) == sizeof(uint32_t)); +static_assert(MM_ARM_DPTE_ISPOOLHDR == (1 << 2)); +static_assert(MM_ARM_DPTE_COMMITTED == (1 << 3)); + +MMPTE MmBuildPoolHeaderPte(uintptr_t Handle) +{ + ASSERT(!(Handle & 0x7)); + MMPTE_POOLHEADER PteHeader; + PteHeader.Pte = 0; + + PteHeader.B3to30 = Handle >> 3; + PteHeader.IsPoolHdr = true; + + MMPTE ActualPte; + MmHardwarePte(ActualPte) = PteHeader.Pte; + return ActualPte; +} + +uintptr_t MmGetPoolHeaderAddressPte(MMPTE Pte) +{ + MMPTE_POOLHEADER PteHeader; + PteHeader.Pte = MmHardwarePte(Pte); + + ASSERT(!PteHeader.Present); + ASSERT(!PteHeader.Committed); + ASSERT(PteHeader.IsPoolHdr); + + return (PteHeader.B3to30 << 3) | 0x80000000; +} + +MMPFN MmGetPfnPte(MMPTE Pte) +{ + return MM_ARM_PTE_PFN(MmHardwarePte(Pte)); +} + +uintptr_t MmGetPageBitsPte(MMPTE Pte) +{ + uintptr_t PageBits = MM_PROT_READ; + +#ifdef TARGET_ARMV5 + PageBits |= MM_PROT_EXEC; + + if ((MmHardwarePte(Pte) & (MM_ARM_PTEL2_C | MM_ARM_PTEL2_B)) == 0) + PageBits |= MM_MISC_DISABLE_CACHE; +#else + if ((MmHardwarePte(Pte) & (MM_ARM_PTEL2_TEX_MASK | MM_ARM_PTEL2_CB_MASK)) == 0) + PageBits |= MM_MISC_DISABLE_CACHE; +#endif + + switch (MmHardwarePte(Pte) & MM_ARM_AP_MASK) + { + case 0: + PageBits &= ~MM_PROT_READ; + break; + case MM_ARM_PTEL2_AP_SUPERREADWRITE: + PageBits |= MM_PROT_WRITE; + break; + case MM_ARM_PTEL2_AP_USERREADONLY: + PageBits |= MM_PROT_USER; + break; + case MM_ARM_PTEL2_AP_USERREADWRITE: + PageBits |= MM_PROT_USER | MM_PROT_WRITE; + break; + #ifndef TARGET_ARMV5 + case MM_ARM_PTEL2_AP_SUPERREADONLY: + break; + case MM_ARM_PTEL2_AP_BOTHREADONLY: + PageBits |= MM_PROT_USER; + break; + #endif + default: + KeCrash("Unknown protection bits in PTE %p", MmHardwarePte(Pte)); + break; + } + + // ARM has no software-defined bits... so just tell whoever's calling that + // this page came from the PMM, and be done with it. + PageBits |= MM_MISC_IS_FROM_PMM; + + return PageBits; +} + +bool MmIsEqualPte(MMPTE Pte1, MMPTE Pte2) +{ + return MmHardwarePte(Pte1) == MmHardwarePte(Pte2); +} + +bool MmIsUnsupportedHigherLevelPte(MMPTE Pte) +{ + if (!MmIsPresentPte(Pte)) + return false; + + // other unsupported properties here...? + + return false; +} + +bool MmIsFromPmmPte(MMPTE Pte) +{ + // Same as MmGetPageBitsPte()... + (void) Pte; + return true; +} + +void MmFlushTlbUpdates() +{ + KeFlushTLB(); +} diff --git a/boron/source/mm/arm/ptfree.c b/boron/source/mm/arm/ptfree.c new file mode 100644 index 00000000..c10808b4 --- /dev/null +++ b/boron/source/mm/arm/ptfree.c @@ -0,0 +1,70 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + mm/armv6/ptfree.c + +Abstract: + This module implements the function that frees unused + page table mapping levels. + +Author: + iProgramInCpp - 30 December 2025 +***/ +#include "../mi.h" + +static bool MmpIsPteListCompletelyEmpty(PMMPTE Pte) +{ + bool AllZeroes = true; + + MMPTE ZeroPte = MmBuildZeroPte(); + for (size_t PteIndex = 0; PteIndex < 1024; PteIndex++) + { + if (!MmIsEqualPte(Pte[PteIndex], ZeroPte)) + { + AllZeroes = false; + break; + } + } + + return AllZeroes; +} + +// NOTE: StartVa and SizePages are only roughly followed. +// +// NOTE: The address space lock of the process *must* be held. +void MiFreeUnusedMappingLevelsInCurrentMap(uintptr_t StartVa, size_t SizePages) +{ + if (StartVa >= MM_KERNEL_SPACE_BASE) + return; + + PMMPTE Pml1 = (PMMPTE) MI_PML1_LOCATION; + PMMPTE Pml2 = (PMMPTE) MI_PML2_LOCATION; + PMMPTE Pte = &Pml1[(StartVa >> 20) & ~3]; + MMPTE ZeroPte = MmBuildZeroPte(); + + for (size_t i = 0; i < SizePages; i += 1024, Pte += 4) + { + if (MmIsEqualPte(*Pte, ZeroPte)) + continue; + + PMMPTE SubPte = &Pml2[(StartVa >> 22) << 10]; + if (MmpIsPteListCompletelyEmpty(SubPte)) + { + MMPFN Pfn = MmGetPfnPte(*Pte); + MmFreePhysicalPage(Pfn); + + for (int m = 0; m < 4; m++) { + Pte[m] = ZeroPte; + } + + // also clear the pml2 mirror: + PMMPTE Pml2Mirror = (PMMPTE) MI_PML2_MIRROR_LOCATION; + Pml2Mirror[(StartVa >> 22)] = ZeroPte; + MmFlushTlbUpdates(); + } + } + + KeFlushTLB(); +} diff --git a/boron/source/mm/commit.c b/boron/source/mm/commit.c index 19ab77f6..e6a5d21e 100644 --- a/boron/source/mm/commit.c +++ b/boron/source/mm/commit.c @@ -151,6 +151,7 @@ BSTATUS MmCommitVirtualMemory(uintptr_t StartVa, size_t SizePages, int Protectio } // Okay, everything's committed. Success! + MmFlushTlbUpdates(); MmUnlockSpace(Ipl, StartVa); return STATUS_SUCCESS; } diff --git a/boron/source/mm/fault.c b/boron/source/mm/fault.c index 286a7b51..d9dd05f5 100644 --- a/boron/source/mm/fault.c +++ b/boron/source/mm/fault.c @@ -53,6 +53,7 @@ static BSTATUS MmpHandleFaultCommittedPage(PMMPTE PtePtr, uintptr_t PageBits) } *PtePtr = MmBuildPte(Pfn, PageBits | MM_MISC_IS_FROM_PMM); + MmFlushTlbUpdates(); return STATUS_SUCCESS; } @@ -72,6 +73,7 @@ static BSTATUS MmpAssignPfnToAddress(uintptr_t Va, MMPFN Pfn, int Protection) return STATUS_INSUFFICIENT_MEMORY; *PtePtr = MmBuildPte(Pfn, PageBits | MmGetPteBitsFromProtection(Protection)); + MmFlushTlbUpdates(); return STATUS_SUCCESS; } @@ -179,13 +181,13 @@ BSTATUS MiNormalFault(PEPROCESS Process, uintptr_t Va, PMMPTE PtePtr, KIPL Space uintptr_t PoolStart = MiGetTopOfPoolManagedArea(); uintptr_t PoolEnd = PoolStart + (1ULL << MI_POOL_LOG2_SIZE); - #ifdef TARGET_I386 + #ifdef MI_USE_TWO_POOLS uintptr_t Pool2Start = MiGetTopOfSecondPoolManagedArea(); uintptr_t Pool2End = PoolStart + (1ULL << MI_POOL_LOG2_SIZE_2ND); #endif if ((PoolStart <= Va && Va < PoolEnd) - #ifdef TARGET_I386 + #ifdef MI_USE_TWO_POOLS || (Pool2Start <= Va && Va < Pool2End) #endif ) @@ -313,13 +315,13 @@ BSTATUS MiWriteFault(UNUSED PEPROCESS Process, uintptr_t Va, PMMPTE PtePtr) uintptr_t PoolStart = MiGetTopOfPoolManagedArea(); uintptr_t PoolEnd = PoolStart + (1ULL << MI_POOL_LOG2_SIZE); - #ifdef TARGET_I386 + #ifdef MI_USE_TWO_POOLS uintptr_t Pool2Start = MiGetTopOfSecondPoolManagedArea(); uintptr_t Pool2End = PoolStart + (1ULL << MI_POOL_LOG2_SIZE_2ND); #endif if ((PoolStart <= Va && Va < PoolEnd) - #ifdef TARGET_I386 + #ifdef MI_USE_TWO_POOLS || (Pool2Start <= Va && Va < Pool2End) #endif ) @@ -393,6 +395,7 @@ BSTATUS MiWriteFault(UNUSED PEPROCESS Process, uintptr_t Va, PMMPTE PtePtr) *PtePtr = MmSetPageBitsPte(*PtePtr, MmGetPageBitsPte(*PtePtr) | MM_PROT_READ | MM_PROT_WRITE | MM_MISC_IS_FROM_PMM); } + MmFlushTlbUpdates(); PFDbgPrint("MiWriteFault: VA %p upgraded to write successfully!", Va); MmUnlockVadList(VadList); return STATUS_SUCCESS; @@ -439,6 +442,15 @@ BSTATUS MmPageFault(UNUSED uintptr_t FaultPC, uintptr_t FaultAddress, uintptr_t // declare failure instantly. if ((~MmGetPageBitsPte(*PtePtr) & MM_PROT_USER) && IsUserModeCode) { + PFDbgPrint("AV: Page is valid, but this is a user mode access for a kernel region."); + Status = STATUS_ACCESS_VIOLATION; + MmUnlockSpace(OldIpl, FaultAddress); + goto EarlyExit; + } + + if ((~MmGetPageBitsPte(*PtePtr) & MM_PROT_EXEC) && (FaultMode & MM_FAULT_INSNFETCH)) + { + PFDbgPrint("AV: Page is valid, but this is an attempt to execute code that isn't marked executable."); Status = STATUS_ACCESS_VIOLATION; MmUnlockSpace(OldIpl, FaultAddress); goto EarlyExit; diff --git a/boron/source/mm/i386/pt.c b/boron/source/mm/i386/pt.c index 497eed74..4c76bce7 100644 --- a/boron/source/mm/i386/pt.c +++ b/boron/source/mm/i386/pt.c @@ -34,7 +34,11 @@ PMMPTE MmGetPteLocation(uintptr_t Address) return PtePtr; } -bool MmCheckPteLocation(uintptr_t Address, bool GenerateMissingLevels) +bool MmCheckPteLocationAllocator( + uintptr_t Address, + bool GenerateMissingLevels, + MM_PAGE_ALLOCATOR_METHOD PageAllocate +) { PMMPTE Pte; uintptr_t SupervisorBit; @@ -53,7 +57,7 @@ bool MmCheckPteLocation(uintptr_t Address, bool GenerateMissingLevels) if (!GenerateMissingLevels) return false; - MMPFN PtAllocated = MmAllocatePhysicalPage(); + MMPFN PtAllocated = PageAllocate(); if (PtAllocated == PFN_INVALID) return false; @@ -64,6 +68,11 @@ bool MmCheckPteLocation(uintptr_t Address, bool GenerateMissingLevels) return true; } +bool MmCheckPteLocation(uintptr_t Address, bool GenerateMissingLevels) +{ + return MmCheckPteLocationAllocator(Address, GenerateMissingLevels, MmAllocatePhysicalPage); +} + PMMPTE MmGetPteLocationCheck(uintptr_t Address, bool GenerateMissingLevels) { if (!MmCheckPteLocation(Address, GenerateMissingLevels)) @@ -112,6 +121,12 @@ HPAGEMAP MiCreatePageMapping() return (HPAGEMAP) NewPageMappingResult; } +// Frees a page mapping. +void MiFreePageMapping(HPAGEMAP PageMap) +{ + return MmFreePhysicalPage(MmPhysPageToPFN(PageMap)); +} + static void MmpFreeVacantPageTables(uintptr_t Address) { if (!MmCheckPteLocation(Address, false)) diff --git a/boron/source/mm/i386/pte.c b/boron/source/mm/i386/pte.c index 72f6a89d..ac1af620 100644 --- a/boron/source/mm/i386/pte.c +++ b/boron/source/mm/i386/pte.c @@ -172,3 +172,8 @@ bool MmIsFromPmmPte(MMPTE Pte) { return (MmIsPresentPte(Pte) || MmWasPresentPte(Pte)) && (MmHardwarePte(Pte) & MM_I386_PTE_ISFROMPMM); } + +void MmFlushTlbUpdates() +{ + // On i386, the TLB is coherent against the data cache, so no need for anything +} diff --git a/boron/source/mm/mi.h b/boron/source/mm/mi.h index 607456e8..8d1a3d95 100644 --- a/boron/source/mm/mi.h +++ b/boron/source/mm/mi.h @@ -20,7 +20,6 @@ Module name: #include #include #include -#include <_limine.h> #define PAGE_ALIGNED(x) (((x) & (PAGE_SIZE - 1)) == 0) @@ -182,7 +181,7 @@ HUGE_MEMORY_BLOCK, *PHUGE_MEMORY_BLOCK; // Thus, our pool will be 512 GB in size. #define MI_POOL_LOG2_SIZE (39) -#elif defined TARGET_I386 +#elif defined TARGET_I386 || defined TARGET_ARM // There will actually be two arenas of pool space: // 0x80000000 - 0xC0000000 and 0xD0000000 - 0xF0000000 @@ -190,6 +189,8 @@ HUGE_MEMORY_BLOCK, *PHUGE_MEMORY_BLOCK; #define MI_POOL_LOG2_SIZE_2ND (28) +#define MI_USE_TWO_POOLS + #else #error "Define the pool size for your platform!" @@ -286,7 +287,7 @@ void MiPrepareGlobalAreaForPool(HPAGEMAP PageMap); // Get the top of the area managed by the pool allocator. uintptr_t MiGetTopOfPoolManagedArea(); -#ifdef TARGET_I386 +#ifdef MI_USE_TWO_POOLS // Get the top of the second area managed by the pool allocator. uintptr_t MiGetTopOfSecondPoolManagedArea(); #endif @@ -312,6 +313,10 @@ PMMPTE MmGetPteLocation(uintptr_t Address); // been generated if GenerateMissingLevels is true). bool MmCheckPteLocation(uintptr_t Address, bool GenerateMissingLevels); +// TODO: Implement this for other platforms too! +typedef MMPFN(*MM_PAGE_ALLOCATOR_METHOD)(void); +bool MmCheckPteLocationAllocator(uintptr_t Address, bool GenerateMissingLevels, MM_PAGE_ALLOCATOR_METHOD PageAllocate); + // Reserves a range of virtual memory and returns a VAD. // // Note: This leaves the VAD list locked, if the function succeeds, so you must call MmUnlockVadList! @@ -402,7 +407,15 @@ BSTATUS MiAssignEntrySection(PMMSECTION Section, uint64_t SectionOffset, MMPFN P // ===== Hardware Specific ===== #if defined TARGET_I386 || defined TARGET_AMD64 + #define MI_PTE_LOC(Address) (MI_PML1_LOCATION + (((Address) & MI_PML_ADDRMASK) >> 12) * sizeof(MMPTE)) + +#elif defined TARGET_ARM + +// same as above, but L1 is the top level and L2 is the bottom level +#define MI_PTE_LOC(Address) (MI_PML2_LOCATION + (((Address) & MI_PML_ADDRMASK) >> 12) * sizeof(MMPTE)) + #endif + #endif//NS64_MI_H diff --git a/boron/source/mm/pmm.c b/boron/source/mm/pmm.c index 72be0249..636dbc45 100644 --- a/boron/source/mm/pmm.c +++ b/boron/source/mm/pmm.c @@ -136,7 +136,9 @@ static void MiUpdateHHDMWindowBase(uintptr_t PhysAddr) Ptes[Convert.Level2Index * PtesPerLevel + Convert.Level1Index] = MmBuildPte(MmPhysPageToPFN(PhysAddr + i), MM_PROT_READ | MM_PROT_WRITE); + KeInvalidatePage((void*)Address); + MmFlushTlbUpdates(); } MiUnlockPfdb(Ipl); @@ -192,8 +194,8 @@ uintptr_t MiAllocatePageFromMemMap() if (Entry->Type != LOADER_MEM_FREE) continue; - // Note! Usable entries in limine are guaranteed to be aligned to - // page size, and not overlap any other entries. So we are good + // Note: Usable entries are guaranteed to be aligned to page size, + // and not overlap any other entries. // if it's got no pages, also skip it.. if (Entry->Size == 0) @@ -243,6 +245,16 @@ uintptr_t MiAllocateMemoryFromMemMap(size_t SizeInPages) KeCrashBeforeSMPInit("Error, out of memory in the memmap allocate function"); } +INIT +MMPFN MiAllocatePfnFromMemMap() +{ + uintptr_t Page = MiAllocatePageFromMemMap(); + if (!Page) + return PFN_INVALID; + + return MmPhysPageToPFN(Page); +} + typedef struct { MMPTE entries[512]; @@ -255,6 +267,8 @@ INIT static bool MiMapNewPageAtAddressIfNeeded(uintptr_t pageTable, uintptr_t address) { #ifdef TARGET_AMD64 + // TODO: remove this arch-specific implementation. + // Maps a new page at an address, if needed. PAGE_MAP_LEVEL *pPML[4]; pPML[3] = (PPAGE_MAP_LEVEL) MmGetHHDMOffsetAddr(pageTable); @@ -289,54 +303,35 @@ static bool MiMapNewPageAtAddressIfNeeded(uintptr_t pageTable, uintptr_t address Flags |= MM_MISC_GLOBAL; pPML[i]->entries[index] = MmBuildPte(MmPhysPageToPFN(Addr), Flags); + MmFlushTlbUpdates(); } } return true; -#elif defined TARGET_I386 - (void)pageTable; // unused - - MMADDRESS_CONVERT Convert; - Convert.Long = address; - - PMMPTE Level1, Level2; - - Level2 = (PMMPTE)MI_PML2_LOCATION; - Level1 = (PMMPTE)(MI_PML1_LOCATION + 4096 * Convert.Level2Index); +#else + (void) pageTable; + + if (!MmCheckPteLocationAllocator(address, true, MiAllocatePfnFromMemMap)) + return false; - if (!MmIsPresentPte(Level2[Convert.Level2Index])) - { - uintptr_t Addr = MiAllocatePageFromMemMap(); - - if (!Addr) - { - // TODO: Allow rollback - return false; - } - - Level2[Convert.Level2Index] = MmBuildPte(MmPhysPageToPFN(Addr), MM_PROT_READ | MM_PROT_WRITE); + MMPTE ZeroPte = MmBuildZeroPte(); + PMMPTE Pte = MmGetPteLocation(address); + if (!MmIsEqualPte(*Pte, ZeroPte)) { + return true; } - if (!MmIsPresentPte(Level1[Convert.Level1Index])) - { - uintptr_t Addr = MiAllocatePageFromMemMap(); - - if (!Addr) - { - // TODO: Allow rollback - return false; - } - - MmBeginUsingHHDM(); - memset(MmGetHHDMOffsetAddr(Addr), 0, PAGE_SIZE); - MmEndUsingHHDM(); - - Level1[Convert.Level1Index] = MmBuildPte(MmPhysPageToPFN(Addr), MM_PROT_READ | MM_PROT_WRITE); + MMPFN Pfn = MiAllocatePfnFromMemMap(); + if (Pfn == PFN_INVALID) { + return false; } + MmBeginUsingHHDM(); + memset(MmGetHHDMOffsetAddr(MmPFNToPhysPage(Pfn)), 0, PAGE_SIZE); + MmEndUsingHHDM(); + + *Pte = MmBuildPte(Pfn, MM_PROT_READ | MM_PROT_WRITE); + MmFlushTlbUpdates(); return true; -#else - #error "Implement this for your platform!" #endif } @@ -481,7 +476,7 @@ void MiInitPMM() for (uint64_t j = 0; j < Entry->Size; j += PAGE_SIZE) { - bool isUsed = Entry->Type != LIMINE_MEMMAP_USABLE; + bool isUsed = Entry->Type != LOADER_MEM_FREE; int currPFN = MmPhysPageToPFN(Entry->Base + j); @@ -582,6 +577,7 @@ void MmRegisterMMIOAsMemory(uintptr_t Base, uintptr_t Length) KeCrash("MmRegisterMMIOAsMemory: could not ensure PTE location %p exists", currPage); PMMPTE Pte = MmGetPteLocation(currPage); + MmFlushTlbUpdates(); if (!MmIsPresentPte(*Pte)) { // allocate it @@ -590,6 +586,7 @@ void MmRegisterMMIOAsMemory(uintptr_t Base, uintptr_t Length) *Pte = MmBuildPte(PfnAlloc, MM_PROT_READ | MM_PROT_WRITE | MM_MISC_IS_FROM_PMM); KeInvalidatePage((void*) currPage); + MmFlushTlbUpdates(); } lastAllocatedPage = currPage; @@ -1141,3 +1138,103 @@ int MiGetReferenceCountPfn(MMPFN Pfn) return Pfdbe->RefCount; } + +FORCE_INLINE +bool MmpIsFree(MMPFN Pfn) +{ + return MmGetPageFrameFromPFN(Pfn)->Type == PF_TYPE_FREE; +} + +static void MmpRemovePfnFromItsList(MMPFN Pfn) +{ + PMMPFDBE Pfdbe = MmGetPageFrameFromPFN(Pfn); + + ASSERT(Pfdbe->Type == PF_TYPE_FREE); + + if (Pfdbe->NextFrame != PFN_INVALID && Pfdbe->PrevFrame != PFN_INVALID) { + MmpUnlinkPfn(Pfdbe); + return; + } + + PMMPFN First = NULL, Last = NULL; + if (Pfdbe->NextFrame == PFN_INVALID) { + if (MiLastFreePFN == Pfn) + First = &MiFirstFreePFN, Last = &MiLastFreePFN; + else if (MiLastZeroPFN == Pfn) + First = &MiFirstZeroPFN, Last = &MiLastZeroPFN; + else if (MiLastStandbyPFN == Pfn) + First = &MiFirstStandbyPFN, Last = &MiLastStandbyPFN; + } + else if (Pfdbe->PrevFrame == PFN_INVALID) { + if (MiFirstFreePFN == Pfn) + First = &MiFirstFreePFN, Last = &MiLastFreePFN; + else if (MiFirstZeroPFN == Pfn) + First = &MiFirstZeroPFN, Last = &MiLastZeroPFN; + else if (MiFirstStandbyPFN == Pfn) + First = &MiFirstStandbyPFN, Last = &MiLastStandbyPFN; + } + else { + ASSERT(!"Neither NextFrame nor PrevFrame are NULL but this is impossible"); + } + + ASSERT(First && Last && "This PFN is in a free list... right?!"); + MmpRemovePfnFromList(First, Last, Pfn); +} + +static bool MmpTryAllocateContiguousRegion(MMPFN Pfn, int PageCount, uintptr_t Alignment) +{ + // first, make sure this region is truly 16 Kbyte aligned. + if (MmPFNToPhysPage(Pfn) & Alignment) + return false; + + // N.B. Pfn is already free + ASSERT(MmpIsFree(Pfn)); + + for (int i = 1; i < PageCount; i++) { + if (!MmpIsFree(Pfn + i)) + return false; + } + + // okay, now actually allocate it. + for (int i = 0; i < PageCount; i++) { + MmpRemovePfnFromItsList(Pfn + i); + MmpInitializePfn(MmGetPageFrameFromPFN(Pfn + i)); + } + + return true; +} + +MMPFN MmAllocatePhysicalContiguousRegion(int PageCount, uintptr_t Alignment) +{ + MMPFN Pfn; + KIPL OldIpl; + KeAcquireSpinLock(&MmPfnLock, &OldIpl); + + for (Pfn = MiFirstFreePFN; Pfn != MiLastFreePFN; Pfn = MmGetPageFrameFromPFN(Pfn)->NextFrame) { + if (MmpTryAllocateContiguousRegion(Pfn, PageCount, Alignment)) { + goto Return; + } + } + + for (Pfn = MiFirstZeroPFN; Pfn != MiLastZeroPFN; Pfn = MmGetPageFrameFromPFN(Pfn)->NextFrame) { + if (MmpTryAllocateContiguousRegion(Pfn, PageCount, Alignment)) { + goto Return; + } + } + + for (Pfn = MiFirstStandbyPFN; Pfn != MiLastStandbyPFN; Pfn = MmGetPageFrameFromPFN(Pfn)->NextFrame) { + if (MmpTryAllocateContiguousRegion(Pfn, PageCount, Alignment)) { + goto Return; + } + } + +Return: + KeReleaseSpinLock(&MmPfnLock, OldIpl); + return Pfn; +} + +void MmFreePhysicalContiguousRegion(MMPFN PfnStart, int PageCount) +{ + for (int i = 0; i < PageCount; i++) + MmFreePhysicalPage(PfnStart + i); +} diff --git a/boron/source/mm/pool.c b/boron/source/mm/pool.c index d0cc08d5..5607ff62 100644 --- a/boron/source/mm/pool.c +++ b/boron/source/mm/pool.c @@ -142,3 +142,9 @@ void* MmMapIoSpace(uintptr_t PhysicalAddress, size_t Size, uintptr_t Permissions MmFreePoolBig(Space); return NULL; } + +void* MmAllocateKernelStack() +{ + return MmAllocatePoolBig(POOL_FLAG_NON_PAGED, KERNEL_STACK_SIZE / PAGE_SIZE, POOL_TAG("ThSt")); +} + diff --git a/boron/source/mm/poolhdr.c b/boron/source/mm/poolhdr.c index a357ef08..8b1aec51 100644 --- a/boron/source/mm/poolhdr.c +++ b/boron/source/mm/poolhdr.c @@ -156,6 +156,7 @@ PMIPOOL_ENTRY_SLAB MiAllocatePoolHeaderSlab() PMMPTE Pte = (PMMPTE) MI_PTE_LOC((uintptr_t) Address); *Pte = MmBuildPte(Pfn, MM_PROT_READ | MM_PROT_WRITE | MM_MISC_IS_FROM_PMM); KeInvalidatePage(Pte); + MmFlushTlbUpdates(); KeReleaseSpinLock(&MiPoolHeaderMapLock, Ipl); return Address; diff --git a/boron/source/mm/poolsup.c b/boron/source/mm/poolsup.c index 7846d992..6cb6b446 100644 --- a/boron/source/mm/poolsup.c +++ b/boron/source/mm/poolsup.c @@ -26,7 +26,7 @@ Module name: // // - Bit 0 is cleared because MM_PTE_PRESENT conflicts // -// - Bit 11 is set because that's MM_PTE_ISPOOLHDR +// - Bit 11 is set because that's MM_DPTE_ISPOOLHDR typedef union { @@ -47,7 +47,7 @@ MMPTE_POOLHEADER; static_assert(sizeof(MMPTE_POOLHEADER) == sizeof(uint32_t)); static_assert(MM_DPTE_COMMITTED == (1 << 8)); -static_assert(MM_PTE_ISPOOLHDR == (1 << 11)); +static_assert(MM_DPTE_ISPOOLHDR == (1 << 11)); MMPTE MiCalculatePoolHeaderPte(uintptr_t Handle) { @@ -79,11 +79,64 @@ uintptr_t MiReconstructPoolHandleFromPte(MMPTE Pte) PteHeader.B12to31 << 12; } +#elif defined TARGET_ARM + +// the structure of the pool header PTE if this is set is as follows: +// +// Address[30:3] 0 1 0 0 +// +// - bits 0 and 1 are cleared because ARM uses the first 2 bits as the PTE's "type" +// - bit 2 is set because that's MM_DPTE_ISPOOLHDR +// - bit 3 is cleared because that's MM_DPTE_COMMITTED and it shouldn't conflict +typedef union +{ + MMPTE Pte; + + struct + { + uintptr_t Present : 2; // MUST be zero + uintptr_t IsPoolHdr : 1; // MUST be ONE + uintptr_t Committed : 1; // MUST be zero + uintptr_t B3to30 : 28; + } + PACKED; +} +MMPTE_POOLHEADER; + +static_assert(sizeof(MMPTE_POOLHEADER) == sizeof(uint32_t)); +static_assert(MM_DPTE_ISPOOLHDR == (1 << 2)); +static_assert(MM_DPTE_COMMITTED == (1 << 3)); + +MMPTE MiCalculatePoolHeaderPte(uintptr_t Handle) +{ + ASSERT(!(Handle & 0x7)); + MMPTE_POOLHEADER PteHeader; + PteHeader.Pte = 0; + + PteHeader.B3to30 = Handle >> 3; + PteHeader.IsPoolHdr = true; + + return PteHeader.Pte; +} + +FORCE_INLINE +uintptr_t MiReconstructPoolHandleFromPte(MMPTE Pte) +{ + MMPTE_POOLHEADER PteHeader; + PteHeader.Pte = Pte; + + ASSERT(!PteHeader.Present); + ASSERT(!PteHeader.Committed); + ASSERT(PteHeader.IsPoolHdr); + + return (PteHeader.B3to30 << 3) | 0x80000000; +} + #else -#define MiCalculatePoolHeaderPte(Handle) (((uintptr_t)(Handle) - MM_KERNEL_SPACE_BASE) | MM_PTE_ISPOOLHDR) +#define MiCalculatePoolHeaderPte(Handle) (((uintptr_t)(Handle) - MM_KERNEL_SPACE_BASE) | MM_DPTE_ISPOOLHDR) -#define MiReconstructPoolHandleFromPte(Pte) ((MIPOOL_SPACE_HANDLE)(((Pte) & ~MM_PTE_ISPOOLHDR) + MM_KERNEL_SPACE_BASE)) +#define MiReconstructPoolHandleFromPte(Pte) ((MIPOOL_SPACE_HANDLE)(((Pte) & ~MM_DPTE_ISPOOLHDR) + MM_KERNEL_SPACE_BASE)) #endif #endif @@ -106,7 +159,7 @@ static LIST_ENTRY MmpPoolList; #define MI_EMPTY_TAG MI_TAG(" ") -#ifdef IS_32_BIT +#ifdef TARGET_I386 void MiInitializeRootPageTable(int Idx) { @@ -114,11 +167,40 @@ void MiInitializeRootPageTable(int Idx) MMPFN Pfn = MmAllocatePhysicalPage(); if (Pfn == PFN_INVALID) - KeCrashBeforeSMPInit("MiCalculatePoolHeaderPte ERROR: Out of memory!"); + KeCrashBeforeSMPInit("MiInitializeRootPageTable ERROR: Out of memory!"); *Pte = MmBuildPte(Pfn, MM_PROT_READ | MM_PROT_WRITE | MM_MISC_IS_FROM_PMM); + MmFlushTlbUpdates(); } +#elif defined TARGET_ARM + +#define L1PTE_FLAGS_CPT 0b01 + +void MiInitializeRootPageTable(int Idx) +{ + PMMPTE Pte = (PMMPTE) MI_PML1_LOCATION; + MMPFN Pfn = MmAllocatePhysicalPage(); + + if (Pfn == PFN_INVALID) + KeCrashBeforeSMPInit("MiInitializeRootPageTable ERROR: Out of memory!"); + + for (int i = 0; i < 4; i++) { + // HACK for now. + MMPTE hPte; + hPte.PteHardware = ((Pfn << 12) + i * 1024) | L1PTE_FLAGS_CPT; + Pte[Idx * 4 + i] = hPte; + } + + // Also update Debbie + Pte = (PMMPTE) MI_PML2_MIRROR_LOCATION; + Pte[Idx] = MmBuildPte(Pfn, MM_MISC_IS_FROM_PMM | MM_PROT_READ | MM_PROT_WRITE); +} + +#endif + +#ifdef IS_32_BIT + void MiInitializePoolPageTables() { int Size1 = 1 << (MI_POOL_LOG2_SIZE - 22); @@ -129,6 +211,8 @@ void MiInitializePoolPageTables() for (int i = MI_GLOBAL_AREA_START_2ND; i < MI_GLOBAL_AREA_START_2ND + Size2; i++) MiInitializeRootPageTable(i); + + MmFlushTlbUpdates(); } #endif @@ -149,7 +233,7 @@ void MiInitPool() Entry->Address = MiGetTopOfPoolManagedArea(); InsertTailList(&MmpPoolList, &Entry->ListEntry); -#ifdef TARGET_I386 +#ifdef MI_USE_TWO_POOLS // TODO: Will other 32-bit platforms look similar? Entry = MiCreatePoolEntry(); @@ -326,6 +410,7 @@ void MiFreePoolSpace(MIPOOL_SPACE_HANDLE Handle) ASSERT(MmIsEqualPte(*PtePtr, MmBuildPoolHeaderPte(Handle))); *PtePtr = MmBuildZeroPte(); + MmFlushTlbUpdates(); MmUnlockKernelSpace(); // Now actually free that handle. diff --git a/boron/source/mm/reclaim.c b/boron/source/mm/reclaim.c index 64fd9f67..288216cc 100644 --- a/boron/source/mm/reclaim.c +++ b/boron/source/mm/reclaim.c @@ -35,6 +35,7 @@ void MiReclaimInitText() // the memory region will never be read from again. However, in debug // mode, a TLB shootdown will be performed anyway. *PtePtr = ZeroPte; + MmFlushTlbUpdates(); MMPFN Pfn = MmGetPfnPte(Pte); MmFreePhysicalPage(Pfn); diff --git a/boron/source/mm/teardown.c b/boron/source/mm/teardown.c index ef7063e7..2aea194f 100644 --- a/boron/source/mm/teardown.c +++ b/boron/source/mm/teardown.c @@ -81,7 +81,7 @@ void MmTearDownProcess(PEPROCESS Process) #endif // DEBUG if (Process->Pcb.PageMap != 0) - MmFreePhysicalPage(MmPhysPageToPFN(Process->Pcb.PageMap)); + MiFreePageMapping(Process->Pcb.PageMap); PsSetAttachedProcess(ProcessRestore); } diff --git a/boron/source/mm/vad.c b/boron/source/mm/vad.c index 5e3c693d..497f018e 100644 --- a/boron/source/mm/vad.c +++ b/boron/source/mm/vad.c @@ -470,6 +470,7 @@ void MiCleanUpVad(PMMVAD Vad) ASSERT(!MmIsPresentPte(*Pte)); *Pte = ZeroPte; + MmFlushTlbUpdates(); Pte++; CurrentVa += PAGE_SIZE; } diff --git a/boron/source/ps/fork.c b/boron/source/ps/fork.c index ec69506f..dc7d289b 100644 --- a/boron/source/ps/fork.c +++ b/boron/source/ps/fork.c @@ -42,7 +42,7 @@ void PspUserThreadStartFork(void* ContextV) MmFreePool(Context); KeGetCurrentThread()->Mode = MODE_USER; -#ifdef TARGET_I386 +#if defined TARGET_I386 || defined TARGET_ARM KeDescendIntoUserMode(ReturnPC, ReturnSP, STATUS_IS_CHILD_PROCESS); #else KeDescendIntoUserMode(ReturnPC, ReturnSP, NULL, STATUS_IS_CHILD_PROCESS); diff --git a/boron/source/ps/initproc.c b/boron/source/ps/initproc.c index 195c6ff3..e8a030c7 100644 --- a/boron/source/ps/initproc.c +++ b/boron/source/ps/initproc.c @@ -93,8 +93,9 @@ void PsStartInitialProcess(UNUSED void* ContextUnused) false ); - if (FAILED(Status)) - KeCrash("%s: Failed to create initial process: %s (%d)", RtlGetStatusString(Status), Status); + if (FAILED(Status)) { + KeCrash("%s: Failed to create initial process: %s (%d)", __func__, RtlGetStatusString(Status), Status); + } PEPROCESS Process = NULL; Status = ExReferenceObjectByHandle(ProcessHandle, PsProcessObjectType, (void**) &Process); @@ -393,6 +394,7 @@ void PsStartInitialProcess(UNUSED void* ContextUnused) INIT bool PsInitSystemPart2() { + LogMsg("PsInitSystemPart2"); PETHREAD Thread = NULL; BSTATUS Status; diff --git a/boron/source/ps/userthrd.c b/boron/source/ps/userthrd.c index cca5981c..b24768f7 100644 --- a/boron/source/ps/userthrd.c +++ b/boron/source/ps/userthrd.c @@ -69,6 +69,8 @@ void PspUserThreadStart(void* ContextV) KeGetCurrentThread()->Mode = MODE_USER; #ifdef TARGET_I386 KeDescendIntoUserMode(Context.InstructionPointer, StackBottom, 0); +#elif defined TARGET_ARM + KeDescendIntoUserMode(Context.InstructionPointer, StackBottom, (uintptr_t) Context.UserContext); #else KeDescendIntoUserMode(Context.InstructionPointer, StackBottom, Context.UserContext, 0); #endif diff --git a/boron/source/rtl/armv5sup.c b/boron/source/rtl/armv5sup.c new file mode 100644 index 00000000..22c8dbd6 --- /dev/null +++ b/boron/source/rtl/armv5sup.c @@ -0,0 +1,133 @@ +/*** + The Boron Operating System + Copyright (C) 2026 iProgramInCpp + +Module name: + rtl/armv5sup.c + +Abstract: + This module implements several atomic methods for armv5. + +Author: + iProgramInCpp - 31 January 2026 +***/ +#include + +#ifdef TARGET_ARMV5 + +#ifdef KERNEL + +#include + +void __sync_synchronize() +{ + unsigned int zero = 0; + // Data Memory Barrier + ASM("mcr p15, 0, %0, c7, c10, 5" : : "r" (zero) : "memory"); +} + +#define LockAtomics() KeDisableInterrupts() +#define UnlockAtomics(x) KeRestoreInterrupts(x) + +#else + +// TODO: implement user-mode atomics. Multithreaded user mode applications +// will likely be unstable until then. +void __sync_synchronize() +{ +} + +#define LockAtomics() true +#define UnlockAtomics(x) ((void)x) + +#endif + +unsigned long long __atomic_load_8(const volatile void* PointerV, UNUSED int MemoryOrder) +{ + bool Restore = LockAtomics(); + const volatile unsigned long long* Pointer = PointerV; + unsigned long long Result = *Pointer; + UnlockAtomics(Restore); + return Result; +} + +unsigned long long __atomic_fetch_add_8(volatile void* PointerV, unsigned long long Value, UNUSED int MemoryOrder) +{ + bool Restore = LockAtomics(); + volatile unsigned long long* Pointer = PointerV; + unsigned long long Result = *Pointer; + *Pointer += Value; + UnlockAtomics(Restore); + return Result; +} + +unsigned int __atomic_fetch_add_4(volatile void* PointerV, unsigned int Value, UNUSED int MemoryOrder) +{ + bool Restore = LockAtomics(); + volatile unsigned int* Pointer = PointerV; + unsigned int Result = *Pointer; + *Pointer += Value; + UnlockAtomics(Restore); + return Result; +} + +unsigned short __atomic_fetch_add_2(volatile void* PointerV, unsigned short Value, UNUSED int MemoryOrder) +{ + bool Restore = LockAtomics(); + volatile unsigned short* Pointer = PointerV; + unsigned short Result = *Pointer; + *Pointer += Value; + UnlockAtomics(Restore); + return Result; +} + +unsigned int __atomic_fetch_or_4(volatile void* PointerV, unsigned int Value, UNUSED int MemoryOrder) +{ + bool Restore = LockAtomics(); + volatile unsigned int* Pointer = PointerV; + unsigned int Result = *Pointer; + *Pointer |= Value; + UnlockAtomics(Restore); + return Result; +} + +unsigned int __atomic_fetch_and_4(volatile void* PointerV, unsigned int Value, UNUSED int MemoryOrder) +{ + bool Restore = LockAtomics(); + volatile unsigned int* Pointer = PointerV; + unsigned int Result = *Pointer; + *Pointer &= Value; + UnlockAtomics(Restore); + return Result; +} + +bool __atomic_compare_exchange_4( + volatile void* DestV, + void* ExpectedV, + unsigned int Desired, + UNUSED bool Weak, + UNUSED int SuccessMemoryOrder, + UNUSED int FailureMemoryOrder +) +{ + bool Restore = LockAtomics(); + bool Result = false; + volatile unsigned int* Dest = DestV; + volatile unsigned int* Expected = ExpectedV; + + if (*Dest == *Expected) + { + *Dest = Desired; + Result = true; + } + else + { + *Expected = *Dest; + Result = false; + } + + UnlockAtomics(Restore); + return Result; +} + +#endif diff --git a/boron/source/rtl/elf.c b/boron/source/rtl/elf.c index 64891ffa..406c3b44 100644 --- a/boron/source/rtl/elf.c +++ b/boron/source/rtl/elf.c @@ -84,6 +84,142 @@ static bool RtlpComputeRelocation( *Length = sizeof(uint32_t); break; // TODO +#elif defined TARGET_ARM + case R_ARM_NONE: + *Value = 0; + *Length = 0; + break; + case R_ARM_GLOB_DAT: + case R_ARM_JUMP_SLOT: + *Value = Symbol; + *Length = sizeof(uint32_t); + break; + case R_ARM_RELATIVE: + *Value = Base + Addend; + *Length = sizeof(uint32_t); + break; + case R_ARM_ABS32: + *Value = Symbol + Addend; + *Length = sizeof(uint32_t); + break; + case R_ARM_REL32: + *Value = Symbol + Addend - Place; + *Length = sizeof(uint32_t); + break; + case R_ARM_SBREL32: + *Value = Symbol + Addend - Base; + *Length = sizeof(uint32_t); + break; + case R_ARM_ABS16: + *Value = (Symbol + Addend) & 0xFFFF; + *Length = sizeof(uint16_t); + break; + case R_ARM_ABS8: + *Value = (Symbol + Addend) & 0xFF; + *Length = sizeof(uint8_t); + break; + case R_ARM_PC24: + case R_ARM_XPC25: + { + uint32_t Instruction = *(uint32_t*) Place; + + uint32_t Address = Symbol + Addend - Place; + int32_t Disp = (int32_t)(Address) >> 2; + int32_t Thm = (Address & 2) != 0; + Disp &= 0x00FFFFFF; + + if (Type == R_ARM_PC24) { + Instruction &= 0xFF000000; + Instruction |= Disp; + } + else { + Instruction &= 0xFE000000; + Instruction |= Disp; + Instruction |= Thm << 24; + } + + *Value = Instruction; + *Length = sizeof(uint32_t); + break; + } + case R_ARM_PC13: + { + uint32_t Instruction = *(uint32_t*)Place; + int32_t Disp = (int32_t)(Symbol + Addend - Place); + + uint32_t Direction = (Disp >= 0); + uint32_t Imm12 = (uint32_t)(Direction ? Disp : -Disp) & 0xFFF; + + Instruction &= ~((1 << 23) | 0xFFF); + Instruction |= (Direction << 23) | Imm12; + + *Value = Instruction; + *Length = sizeof(uint32_t); + break; + } + case R_ARM_ABS12: + { + uint32_t Instruction = *(uint32_t*)Place; + uint32_t Imm12 = (Symbol + Addend) & 0xFFF; + + Instruction &= ~0xFFF; + Instruction |= Imm12; + + *Value = Instruction; + *Length = sizeof(uint32_t); + break; + } + case R_ARM_SWI24: + { + uint32_t Instruction = *(uint32_t*)Place; + uint32_t Imm24 = (Symbol + Addend) & 0x00FFFFFF; + + Instruction &= 0xFF000000; + Instruction |= Imm24; + + *Value = Instruction; + *Length = sizeof(uint32_t); + break; + } + case R_ARM_THM_ABS5: + { + uint16_t instr = *(uint16_t*)Place; + uint16_t imm5 = ((Symbol + Addend) >> 2) & 0x1F; + + instr &= ~(0x1F << 6); + instr |= (imm5 << 6); + + *Value = instr; + *Length = sizeof(uint16_t); + break; + } + case R_ARM_THM_PC8: + { + uint16_t instr = *(uint16_t*)Place; + uint32_t disp = Symbol + Addend - (Place & ~3); + + uint8_t imm8 = (disp >> 2) & 0xFF; + + instr &= ~0xFF; + instr |= imm8; + + *Value = instr; + *Length = sizeof(uint16_t); + break; + } + case R_ARM_THM_SWI8: + { + uint16_t instr = *(uint16_t*)Place; + uint8_t imm8 = (Symbol + Addend) & 0xFF; + + instr &= 0xFF00; + instr |= imm8; + + *Value = instr; + *Length = sizeof(uint16_t); + break; + } + // TODO: R_ARM_THM_PC22, R_ARM_THM_XPC22, R_ARM_AMP_VCALL9 #else #error Hey! Add ELF relocation types here #endif @@ -447,6 +583,8 @@ BSTATUS RtlCheckValidity(PELF_HEADER Header) const int Arch = ELF_ARCH_AMD64; #elif defined TARGET_I386 const int Arch = ELF_ARCH_386; +#elif defined TARGET_ARM + const int Arch = ELF_ARCH_ARM; #endif if (Header->Machine != Arch) diff --git a/boron/source/rtl/print.c b/boron/source/rtl/print.c index d47d65a7..de3f9ab6 100644 --- a/boron/source/rtl/print.c +++ b/boron/source/rtl/print.c @@ -15,6 +15,10 @@ Module name: #define STB_SPRINTF_IMPLEMENTATION // implement the stb_sprintf right here +#ifdef TARGET_ARM +#define STB_SPRINTF_NOUNALIGNED +#endif + #include #include diff --git a/common/include/atom.h b/common/include/atom.h index 121fd149..5c104513 100644 --- a/common/include/atom.h +++ b/common/include/atom.h @@ -65,6 +65,10 @@ Module name: // Note for AtClear: Don't use for anything other than bool and char. +#if defined TARGET_AMD64 || defined TARGET_I386 #define SpinHint() __asm__("pause") +#else +#define SpinHint() __asm__("nop") +#endif #endif//BORON_KE_ATOMICS_H diff --git a/common/include/elf.h b/common/include/elf.h index 0da67348..5c7fa207 100644 --- a/common/include/elf.h +++ b/common/include/elf.h @@ -161,6 +161,28 @@ enum R_386_GOTOFF, // S + A - GOT R_386_GOTPC, // GOT + A - P R_386_32PLT, // L + A +#elif defined TARGET_ARM + R_ARM_NONE, // none + R_ARM_PC24, // S - P + A (ARM B/BL) + R_ARM_ABS32, // S + A + R_ARM_REL32, // S - P + A + R_ARM_PC13, // S - P + A (ARM LDR r, [pc, ...]) + R_ARM_ABS16, // S + A (16-bit half-word) + R_ARM_ABS12, // S + A (ARM LDR/STR) + R_ARM_THM_ABS5, // S + A (Thumb LDR/STR) + R_ARM_ABS8, // S + A (8-bit Byte) + R_ARM_SBREL32, // S - B + A (32-bit Word) + R_ARM_THM_PC22, // S - P + A (Thumb BL pair) + R_ARM_THM_PC8, // S - P + A (Thumb LDR r, [pc, ...]) + R_ARM_AMP_VCALL9,// S - B + A (AMP VCALL) + R_ARM_SWI24, // S + A (ARM SWI) + R_ARM_THM_SWI8, // S + A (Thumb SWI) + R_ARM_XPC25, // S - P + A (ARM BLX) + R_ARM_THM_XPC22, // S - P + A (Thumb BLX pair) + + R_ARM_GLOB_DAT = 21, // S + R_ARM_JUMP_SLOT, // S + R_ARM_RELATIVE, // B + A #else #error Hey! Add ELF relocation types here #endif diff --git a/common/include/mms.h b/common/include/mms.h index 20331dc3..747e5058 100644 --- a/common/include/mms.h +++ b/common/include/mms.h @@ -95,7 +95,7 @@ typedef struct VIRTUAL_MEMORY_INFORMATION, *PVIRTUAL_MEMORY_INFORMATION; // Page Size definition -#if defined TARGET_AMD64 || defined TARGET_I386 +#if defined TARGET_AMD64 || defined TARGET_I386 || defined TARGET_ARM #define PAGE_SIZE (0x1000) #else #error Define page size here! diff --git a/common/include/rtl/check64.h b/common/include/rtl/check64.h index e3a2543e..e93b0c1b 100644 --- a/common/include/rtl/check64.h +++ b/common/include/rtl/check64.h @@ -18,7 +18,7 @@ Module name: #define IS_64_BIT 1 -#elif defined TARGET_I386 +#elif defined TARGET_I386 || defined TARGET_ARM #define IS_32_BIT 1 diff --git a/drivers/CommonMakefile b/drivers/CommonMakefile index 3694f694..7d51776d 100644 --- a/drivers/CommonMakefile +++ b/drivers/CommonMakefile @@ -15,7 +15,6 @@ INC_DIR = include DDK_DIR = ../../common/include KE_DIR = ../../boron/include SCRIPTS_DIR = scripts -LINKER_FILE = linker.ld DRIVER_ENTRY ?= DriverEntry diff --git a/drivers/halipod1,1/Makefile b/drivers/halipod1,1/Makefile new file mode 100644 index 00000000..19b5a4a3 --- /dev/null +++ b/drivers/halipod1,1/Makefile @@ -0,0 +1,10 @@ +# The Boron Operating System +# Common makefile for all driver targets + +DRIVER_NAME = halipod1,1 +# DRIVER_ENTRY = DriverEntry +# DEBUG = yes +# DEBUG2 = no +USER_DEFINES = -DIS_HAL -DFLANTERM_FB_DISABLE_CANVAS -DFLANTERM_FB_DISABLE_BUMP_ALLOC + +include ../CommonMakefile diff --git a/drivers/halipod1,1/source/clcd.c b/drivers/halipod1,1/source/clcd.c new file mode 100644 index 00000000..317817a8 --- /dev/null +++ b/drivers/halipod1,1/source/clcd.c @@ -0,0 +1,6 @@ +#include "hali.h" + +void HalInitCLCD() +{ + DbgPrint("%s NYI", __func__); +} diff --git a/drivers/halipod1,1/source/clock.c b/drivers/halipod1,1/source/clock.c new file mode 100644 index 00000000..dd17b685 --- /dev/null +++ b/drivers/halipod1,1/source/clock.c @@ -0,0 +1,78 @@ +/*** + The Boron Operating System + Copyright (C) 2026 iProgramInCpp + +Module name: + ha/clock.c + +Abstract: + This module contains the platform's clock driver. + +Author: + iProgramInCpp - 16 March 2026 +***/ +#include +#include "hali.h" + +#define CLOCK0_MEM_BASE 0x38100000 +#define CLOCK1_MEM_BASE 0x3C500000 + +#define REG(base, rgof) (* (volatile uint32_t*) ((uintptr_t)base + rgof)) + +#define CLOCK0_CONFIG REG(HalClock0Base, 0x0) +#define CLOCK0_ADJ1 REG(HalClock0Base, 0x8) +#define CLOCK0_ADJ2 REG(HalClock0Base, 0x404) + +// NOTE: for now, only supporting the base of what we need +#define CLOCK1_CL2_GATES REG(HalClock1Base, 0x48) +#define CLOCK1_CL3_GATES REG(HalClock1Base, 0x4C) +#define CLOCK1_CL3_SEPARATOR 0x20 + +void* HalClock0Base; +void* HalClock1Base; + +void HalClockSetGateEnabled(uint32_t Gate, bool Enabled) +{ + if (Gate < CLOCK1_CL3_SEPARATOR) + { + if (Enabled) + CLOCK1_CL2_GATES &= ~(1 << (Gate & 0x1F)); + else + CLOCK1_CL2_GATES |= 1 << (Gate & 0x1F); + } + else + { + if (Enabled) + CLOCK1_CL3_GATES &= ~(1 << (Gate & 0x1F)); + else + CLOCK1_CL3_GATES |= 1 << (Gate & 0x1F); + } +} + +void HalInitClock() +{ + DbgPrint("%s...", __func__); + + HalClock0Base = MmMapIoSpace( + CLOCK0_MEM_BASE, + PAGE_SIZE, + MM_PROT_READ | MM_PROT_WRITE | MM_MISC_DISABLE_CACHE, + POOL_TAG("Clk0") + ); + + HalClock1Base = MmMapIoSpace( + CLOCK1_MEM_BASE, + PAGE_SIZE, + MM_PROT_READ | MM_PROT_WRITE | MM_MISC_DISABLE_CACHE, + POOL_TAG("Clk1") + ); + + // DO NOT DO THIS! This seems to stop every clock on the system and nothing works anymore + //CLOCK1_CL2_GATES = 0xFFFFFFFF; + //CLOCK1_CL3_GATES = 0xFFFFFFFF; +} + +bool HalUseOneShotTimer() +{ + return false; +} diff --git a/drivers/halipod1,1/source/clock.h b/drivers/halipod1,1/source/clock.h new file mode 100644 index 00000000..f2001cfb --- /dev/null +++ b/drivers/halipod1,1/source/clock.h @@ -0,0 +1,5 @@ +#pragma once + +#define CLOCK_GATE_TIMER 0x25 + +void HalClockSetGateEnabled(uint32_t Gate, bool Enabled); diff --git a/drivers/halipod1,1/source/crash.c b/drivers/halipod1,1/source/crash.c new file mode 100644 index 00000000..487a32b4 --- /dev/null +++ b/drivers/halipod1,1/source/crash.c @@ -0,0 +1,28 @@ +/*** + The Boron Operating System + Copyright (C) 2026 iProgramInCpp + +Module name: + ha/crash.c + +Abstract: + This module contains the iPod1,1 platform's specific crash routine. + +Author: + iProgramInCpp - 15 March 2026 +***/ +#include +#include "hali.h" + +HAL_API +void HalProcessorCrashed() +{ + KeStopCurrentCPU(); +} + +NO_RETURN +void HalCrashSystem(const char* Message) +{ + DISABLE_INTERRUPTS(); + KeCrashConclusion(Message); +} diff --git a/drivers/halipod1,1/source/flanterm b/drivers/halipod1,1/source/flanterm new file mode 120000 index 00000000..6beeb88c --- /dev/null +++ b/drivers/halipod1,1/source/flanterm @@ -0,0 +1 @@ +../../../external/flanterm/ \ No newline at end of file diff --git a/drivers/halipod1,1/source/flanterm_alt_fb.c b/drivers/halipod1,1/source/flanterm_alt_fb.c new file mode 120000 index 00000000..0dd63727 --- /dev/null +++ b/drivers/halipod1,1/source/flanterm_alt_fb.c @@ -0,0 +1 @@ +../../../external/flanterm_alt_fb.c \ No newline at end of file diff --git a/drivers/halipod1,1/source/flanterm_alt_fb.h b/drivers/halipod1,1/source/flanterm_alt_fb.h new file mode 120000 index 00000000..06b1592a --- /dev/null +++ b/drivers/halipod1,1/source/flanterm_alt_fb.h @@ -0,0 +1 @@ +../../../external/flanterm_alt_fb.h \ No newline at end of file diff --git a/drivers/halipod1,1/source/font.h b/drivers/halipod1,1/source/font.h new file mode 120000 index 00000000..bf455947 --- /dev/null +++ b/drivers/halipod1,1/source/font.h @@ -0,0 +1 @@ +../../../external/tamsyn6x12.h \ No newline at end of file diff --git a/drivers/halipod1,1/source/hali.h b/drivers/halipod1,1/source/hali.h new file mode 100644 index 00000000..7ee6805e --- /dev/null +++ b/drivers/halipod1,1/source/hali.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +#define HAL_API // specify calling convention here if needed + +void HalInitClock(); diff --git a/drivers/halipod1,1/source/init.c b/drivers/halipod1,1/source/init.c new file mode 100644 index 00000000..08e00945 --- /dev/null +++ b/drivers/halipod1,1/source/init.c @@ -0,0 +1,95 @@ +/*** + The Boron Operating System + Copyright (C) 2026 iProgramInCpp + +Module name: + ha/init.c + +Abstract: + This module contains the two initialization functions + (UP-init and MP-init) for the iPod touch 1G HAL. + +Author: + iProgramInCpp - 14 March 2026 +***/ +#include +#include +#include "hali.h" + +void HalEndOfInterrupt(int InterruptNumber); +void HalRequestIpi(uint32_t LapicId, uint32_t Flags, int Vector); +void HalInitSystemUP(); +void HalInitSystemMP(); +void HalDisplayString(const char* Message); +void HalCrashSystem(const char* Message) NO_RETURN; +bool HalUseOneShotIntTimer(); +void HalProcessorCrashed() NO_RETURN; +uint64_t HalGetIntTimerFrequency(); +uint64_t HalGetTickCount(); +uint64_t HalGetTickFrequency(); +uint64_t HalGetIntTimerDeltaTicks(); +int HalGetMaximumInterruptCount(); +void HalOnUpdateIpl(KIPL NewIpl, KIPL OldIpl); +void HalVicRegisterInterrupt(int InterruptNumber, KIPL Ipl); +void HalVicDeregisterInterrupt(int InterruptNumber, KIPL Ipl); +PKREGISTERS HalOnInterruptRequest(PKREGISTERS Registers); +PKREGISTERS HalOnFastInterruptRequest(PKREGISTERS Registers); +bool HalUseOneShotTimer(); +void HalRequestInterruptInTicks(uint64_t ticks); +uint64_t HalGetInterruptDeltaTime(); +void HalInitCLCD(); +void HalInitTerminal(); +void HalInitPL192(); +void HalInitClock(); +void HalInitTimer(); + +// Initialize the HAL on the BSP, for all processors. +HAL_API void HalInitSystemUP() +{ + HalInitCLCD(); + HalInitTerminal(); + HalInitPL192(); +} + +// Initialize the HAL separately for each processor. +// This function is run on ALL processors. +HAL_API void HalInitSystemMP() +{ + HalInitClock(); + HalInitTimer(); +} + +static const HAL_VFTABLE HalpVfTable = +{ + .EndOfInterrupt = HalEndOfInterrupt, + .RequestInterruptInTicks = HalRequestInterruptInTicks, + .RequestIpi = HalRequestIpi, + .InitSystemUP = HalInitSystemUP, + .InitSystemMP = HalInitSystemMP, + .DisplayString = HalDisplayString, + .CrashSystem = HalCrashSystem, + .ProcessorCrashed = HalProcessorCrashed, + .UseOneShotIntTimer = HalUseOneShotIntTimer, + .GetIntTimerFrequency = HalGetIntTimerFrequency, + .GetTickCount = HalGetTickCount, + .GetTickFrequency = HalGetTickFrequency, + .GetIntTimerDeltaTicks = HalGetIntTimerDeltaTicks, + .GetMaximumInterruptCount = HalGetMaximumInterruptCount, + .OnUpdateIpl = HalOnUpdateIpl, + .VicRegisterInterrupt = HalVicRegisterInterrupt, + .VicDeregisterInterrupt = HalVicDeregisterInterrupt, + .OnInterruptRequest = HalOnInterruptRequest, + .OnFastInterruptRequest = HalOnFastInterruptRequest, + .Flags = HAL_VFTABLE_LOADED, +}; + +BSTATUS DriverEntry(UNUSED PDRIVER_OBJECT Object) +{ + // Note! The HAL's driver object is kind of useless, it doesn't do anything actually. + + // Hook the HAL's functions. + HalSetVftable(&HalpVfTable); + + // And return, that's all we need. + return STATUS_SUCCESS; +} diff --git a/drivers/halipod1,1/source/pl192.c b/drivers/halipod1,1/source/pl192.c new file mode 100644 index 00000000..361635ef --- /dev/null +++ b/drivers/halipod1,1/source/pl192.c @@ -0,0 +1,247 @@ +/*** + The Boron Operating System + Copyright (C) 2026 iProgramInCpp + +Module name: + ha/pl192.c + +Abstract: + This module contains the implementation of the PL192 + VIC (vectored interrupt controller) driver. + +Author: + iProgramInCpp - 16 March 2026 +***/ +#include "hali.h" +#include +#include + +// N.B. Each PL192 has support for 32 interrupts, so 64 total +#define MAX_INTERRUPTS 64 + +#define VIC0_BASE_PHYS (0x38E00000) +#define VIC1_BASE_PHYS (0x38E01000) + +#define VICREG(n, rgof) (* (volatile uint32_t*) ((uintptr_t)HalVic ## n ## Base + rgof)) + +#define VICIRQSTATUS(n) VICREG(n, 0x000) +#define VICFIQSTATUS(n) VICREG(n, 0x004) +#define VICRAWINTR(n) VICREG(n, 0x008) +#define VICINTSELECT(n) VICREG(n, 0x00C) +#define VICINTENABLE(n) VICREG(n, 0x010) +#define VICINTENCLEAR(n) VICREG(n, 0x014) +#define VICSOFTINT(n) VICREG(n, 0x018) +#define VICSOFTINTCLEAR(n) VICREG(n, 0x01C) +#define VICPROTECTION(n) VICREG(n, 0x020) +#define VICSWPRIORITYMASK(n) VICREG(n, 0x024) +#define VICPRIORITYDAISY(n) VICREG(n, 0x028) +#define VICVECTADDR(n) (&VICREG(n, 0x100)) // index as VICVECTADDR(VIC#)[INT#]. INT# in [0,31] +#define VICVECTPRIORITY(n) (&VICREG(n, 0x200)) // index as VICVECTPRIORITY(VIC#)[INT#]. INT# in [0,31] +#define VICADDRESS(n) VICREG(n, 0xF00) // weird place but OK + +static void* HalVic0Base; +static void* HalVic1Base; + +static KIPL HalInterruptIpls[MAX_INTERRUPTS]; + +void HalInitPL192() +{ + // step 1: map the VICs + HalVic0Base = MmMapIoSpace( + VIC0_BASE_PHYS, + PAGE_SIZE, + MM_PROT_READ | MM_PROT_WRITE | MM_MISC_DISABLE_CACHE, + POOL_TAG("Vic0") + ); + + HalVic1Base = MmMapIoSpace( + VIC1_BASE_PHYS, + PAGE_SIZE, + MM_PROT_READ | MM_PROT_WRITE | MM_MISC_DISABLE_CACHE, + POOL_TAG("Vic1") + ); + + if (!HalVic0Base || !HalVic1Base) + KeCrash("Could not map pl192 VICs"); + + // N.B. OpeniBoot appears to write to something called an edgeic? + // I'll configure it later, if it turns out that my kernel will not work + // on real hardware. + + // mask all interrupts + VICINTENCLEAR(0) = 0xFFFFFFFF; + VICINTENCLEAR(1) = 0xFFFFFFFF; + + // 0 means use IRQs, 1 means use FIQs + VICINTSELECT(0) = 0; + VICINTSELECT(1) = 0; + + // Unmask all 16 interrupt levels + VICSWPRIORITYMASK(0) = 0xFFFF; + VICSWPRIORITYMASK(1) = 0xFFFF; + + // Set interrupt vector addresses to the interrupt number. + for (int i = 0; i < 0x20; i++) + { + VICVECTADDR(0)[i] = i; + VICVECTADDR(1)[i] = 0x20 + i; + + VICVECTPRIORITY(0)[i] = 0xF; + VICVECTPRIORITY(1)[i] = 0xF; + } + + // Clear the IPL list. + for (int i = 0; i < MAX_INTERRUPTS; i++) + HalInterruptIpls[i] = IPL_UNDEFINED; + + // Acknowledge all possible interrupts. + for (int i = 0; i < 0x20; i++) + { + VICADDRESS(0) = 0; + VICADDRESS(1) = 0; + } +} + +int HalGetMaximumInterruptCount() +{ + return MAX_INTERRUPTS; +} + +void HalVicRegisterInterrupt(int InterruptNumber, KIPL Ipl) +{ + HalInterruptIpls[InterruptNumber] = Ipl; + + // Also let the hardware know. + // + // NOTE: Ipl is 4-bit, and it's also 4-bit in arch/arm/ipl.h, so we're good. + // However, we do have to invert the priorities, since 0xF is highest and 0 is lowest. + int Priority = 0xF - Ipl; + if (InterruptNumber >= 32) + { + VICVECTPRIORITY(1)[InterruptNumber & 0x1F] = Priority; + VICINTENABLE(1) = 1U << (InterruptNumber & 0x1F); + } + else + { + VICVECTPRIORITY(0)[InterruptNumber & 0x1F] = Priority; + VICINTENABLE(0) = 1U << (InterruptNumber & 0x1F); + } +} + +void HalVicDeregisterInterrupt(int InterruptNumber, UNUSED KIPL Ipl) +{ + HalInterruptIpls[InterruptNumber] = IPL_UNDEFINED; + + if (InterruptNumber >= 32) + VICINTENCLEAR(1) = 1U << (InterruptNumber & 0x1F); + else + VICINTENCLEAR(0) = 1U << (InterruptNumber & 0x1F); +} + +void HalOnUpdateIpl(KIPL NewIpl, UNUSED KIPL OldIpl) +{ + if (!HalVic0Base || !HalVic1Base) + return; + + // 0 is lowest, 1 is highest, meaning that the mask goes 0b111...000 + int Mask = 0xFFFF; + if (NewIpl != IPL_NORMAL) + Mask = (0xFFFF >> (NewIpl + 1)) & 0xFFFF; + + VICSWPRIORITYMASK(0) = Mask; + VICSWPRIORITYMASK(1) = Mask; +} + +PKREGISTERS HalOnFastInterruptRequest(PKREGISTERS Registers) +{ + DbgPrint("%s NYI", __func__); + return Registers; +} + +void HalRequestIpi(uint32_t LapicId, uint32_t Flags, int Vector) +{ + (void) LapicId; + (void) Flags; + (void) Vector; + DbgPrint("%s NYI", __func__); +} + +void HalEndOfInterrupt(int InterruptNumber) +{ + if (InterruptNumber >= 32) { + VICADDRESS(1) = 0; + } + else { + VICADDRESS(0) = 0; + } +} + +KIPL HalEnterHardwareInterrupt(int InterruptNumber) +{ + PKPRCB Prcb = KeGetCurrentPRCB(); + ASSERT(Prcb); + + PKIPL IplPtr = &Prcb->Ipl; + KIPL OldIpl = (int) *IplPtr; + KIPL NewIpl = HalInterruptIpls[InterruptNumber]; + + if (NewIpl != IPL_UNDEFINED) + { + *IplPtr = NewIpl; + + if (OldIpl > NewIpl) + // uh oh! + KeCrash("HalEnterHardwareInterrupt: Old IPL of %d was higher than current IPL of %d.", OldIpl, NewIpl); + + HalOnUpdateIpl(NewIpl, OldIpl); + } + + // now that we've set up the hardware interrupt stuff, enable interrupts. + ENABLE_INTERRUPTS(); + return OldIpl; +} + +void HalExitHardwareInterrupt(int InterruptNumber, KIPL OldIpl) +{ + DISABLE_INTERRUPTS(); + + PKPRCB Prcb = KeGetCurrentPRCB(); + ASSERT(Prcb); + + KIPL PrevIpl = Prcb->Ipl; + Prcb->Ipl = OldIpl; + HalOnUpdateIpl(OldIpl, PrevIpl); + + (void) InterruptNumber; + + // Note: safe to call here because KeDispatchPendingSoftInterrupts + // preserves interrupt disable state across a call to it + KeDispatchPendingSoftInterrupts(); +} + +PKREGISTERS HalOnInterruptRequest(PKREGISTERS Registers) +{ + // Interrupts are disabled at this point. Also, the VIC should handle IPLs for us. + int InterruptNumber = -1; + + // TODO: this might be bad if a higher priority interrupt from VIC1 is taken. + // Thankfully the timer is on IRQ 7, so it should be okay. + if (VICIRQSTATUS(0) != 0) + { + InterruptNumber = (int) VICADDRESS(0); + } + else if (VICIRQSTATUS(1) != 0) + { + InterruptNumber = 32 + (int) VICADDRESS(1); + } + else + { + DbgPrintString("HalOnInterruptRequest: spurious interrupt?\n"); + return Registers; + } + + KIPL OldIpl = HalEnterHardwareInterrupt(InterruptNumber); + KeDispatchInterruptRequest(InterruptNumber); + HalExitHardwareInterrupt(InterruptNumber, OldIpl); + return Registers; +} diff --git a/drivers/halipod1,1/source/term.c b/drivers/halipod1,1/source/term.c new file mode 100644 index 00000000..e9e90950 --- /dev/null +++ b/drivers/halipod1,1/source/term.c @@ -0,0 +1,144 @@ +/*** + The Boron Operating System + Copyright (C) 2026 iProgramInCpp + +Module name: + ha/term.c + +Abstract: + This module implements the terminal functions for the iPod1,1 + platform. + +Author: + iProgramInCpp - 15 March 2026 +***/ +#include +#include +#include "hali.h" +#include "flanterm/src/flanterm.h" +#include "flanterm_alt_fb.h" + +static uint8_t HalpBuiltInFont[] = { +#include "font.h" +}; + +// NOTE: Initialization done on the BSP. So no need to sync anything +uint8_t* HalpTerminalMemory; +size_t HalpTerminalMemoryHead; +size_t HalpTerminalMemorySize; + +static struct flanterm_context* HalpTerminalContext; + +bool HalIsTerminalInitted() +{ + return HalpTerminalContext != NULL; +} + +static void* HalpTerminalMemAlloc(size_t sz) +{ + if (HalpTerminalMemoryHead + sz > HalpTerminalMemorySize) + { + DbgPrint("Error, running out of memory in the terminal heap"); + return NULL; + } + + uint8_t* pCurMem = &HalpTerminalMemory[HalpTerminalMemoryHead]; + HalpTerminalMemoryHead += sz; + return pCurMem; +} + +static void HalpTerminalFree(UNUSED void* pMem, UNUSED size_t sz) +{ +} + +bool HalpIsSerialAvailable; + +HAL_API void HalDisplayString(const char* Message); + +void HalInitTerminal() +{ + if (KeLoaderParameterBlock.FramebufferCount == 0) + KeCrashBeforeSMPInit("HAL: No framebuffers found"); + + PLOADER_FRAMEBUFFER Framebuffer = &KeLoaderParameterBlock.Framebuffers[0]; + uint32_t defaultBG = 0x0000007f; + uint32_t defaultFG = 0x00ffffff; + + // on a 1280x800 screen, the term will have a rez of 160x50 (8000 chars). + // 52 bytes per character. + + //const int charWidth = 8, charHeight = 16; + const int charWidth = 6, charHeight = 12; + int termBufWidth = Framebuffer->Width / charWidth; + int termBufHeight = Framebuffer->Height / charHeight; + + const int usagePerChar = 52; // I calculated it + const int fontBoolMemUsage = charWidth * charHeight * 256; // there are 256 chars + const int fontDataMemUsage = charWidth * charHeight * 256 / 8; + const int contextSize = 256; // seems like sizeof(struct flanterm_context) is no longer exposed, so my best guess + + int totalMemUsage = contextSize + fontDataMemUsage + fontBoolMemUsage + termBufWidth * termBufHeight * usagePerChar; + size_t sizePages = (totalMemUsage + PAGE_SIZE - 1) / PAGE_SIZE; + + void* FramebufferMemory = MmMapIoSpace( + (uintptr_t)Framebuffer->Address, + Framebuffer->Pitch * Framebuffer->Height, + MM_PROT_READ | MM_PROT_WRITE | MM_MISC_DISABLE_CACHE, + POOL_TAG("HAFB") + ); + + DbgPrint("Screen resolution: %d by %d. Will use %d Bytes. Framebuffer mapped at %p.", Framebuffer->Width, Framebuffer->Height, sizePages * PAGE_SIZE, FramebufferMemory); + + HalpTerminalMemory = MmAllocatePoolBig(POOL_FLAG_NON_PAGED, sizePages, POOL_TAG("Term")); + HalpTerminalMemoryHead = 0; + HalpTerminalMemorySize = sizePages * PAGE_SIZE; + + if (!HalpTerminalMemory) + KeCrashBeforeSMPInit("Error, no memory for the terminal."); + + HalpTerminalContext = flanterm_fb_init_alt( + &HalpTerminalMemAlloc, + &HalpTerminalFree, + FramebufferMemory, + Framebuffer->Width, + Framebuffer->Height, + Framebuffer->Pitch, + Framebuffer->BitDepth, + Framebuffer->RedMaskSize, Framebuffer->RedMaskShift, // red mask size and shift + Framebuffer->GreenMaskSize, Framebuffer->GreenMaskShift, // green mask size and shift + Framebuffer->BlueMaskSize, Framebuffer->BlueMaskShift, // blue mask size and shift + NULL, // ansi colors + NULL, // ansi bright colors + &defaultBG, // default background + &defaultFG, // default foreground + NULL, // default background bright + NULL, // default fontground bright + HalpBuiltInFont, // font pointer + charWidth, // font width + charHeight, // font height + 0, // character spacing X + 0 // character spacing Y + ); + + if (!HalpTerminalContext) + { + KeCrashBeforeSMPInit("Error, no terminal context"); + } +} + +HAL_API void HalDisplayString(const char* Message) +{ + if (!HalpTerminalContext) + { + HalPrintStringDebug(Message); + return; + } + + size_t Length = strlen(Message); + + static KSPIN_LOCK SpinLock; + KIPL Ipl; + KeAcquireSpinLock(&SpinLock, &Ipl); + flanterm_write(HalpTerminalContext, Message, Length); + KeReleaseSpinLock(&SpinLock, Ipl); +} diff --git a/drivers/halipod1,1/source/timer.c b/drivers/halipod1,1/source/timer.c new file mode 100644 index 00000000..d6c4024b --- /dev/null +++ b/drivers/halipod1,1/source/timer.c @@ -0,0 +1,287 @@ +/*** + The Boron Operating System + Copyright (C) 2026 iProgramInCpp + +Module name: + ha/timer.c + +Abstract: + This module contains the platform's timer driver. + +Author: + iProgramInCpp - 16 March 2026 +***/ +#include +#include +#include "hali.h" +#include "clock.h" + +// Thanks to https://github.com/iDroid-Project/openiBoot.git, and also +// https://github.com/devos50/qemu-ios/tree/ipod_touch_1g for painstakingly +// documenting all the iPod-touch-unique hardware. + +#define TIMER1_MEM_BASE (0x3E200000) + +#define TIMER_COUNT 7 +#define TIMER_IRQ 0x7 + +#define TIMER_STATE_START 1 +#define TIMER_STATE_STOP 0 +#define TIMER_STATE_MANUAL_UPDATE 2 + +// not sure why they're assigned so weirdly +#define TIMER_DIVIDE1 4 +#define TIMER_DIVIDE2 0 +#define TIMER_DIVIDE4 1 +#define TIMER_DIVIDE16 2 +#define TIMER_DIVIDE64 3 + +// We will only use a few of these timers. +// +// Timer 1 - Piezo Timer (iPod touch 1G exclusive) +// Timer 4 - Event Timer +// Timer 5 - Vibrator Timer (iPhone 3G exclusive) + +// Interestingly the vibrator motor is controlled by timer 5 on the 3G, but +// by AT commands issued to the radio board on the 2G. That's strange.. +#define TIMER_EVENT 4 +#define TIMER_PIEZO 1 +#define TIMER_VIBRATE 5 + +#define TIMER_0_OFFSET 0x00 +#define TIMER_1_OFFSET 0x20 +#define TIMER_2_OFFSET 0x40 +#define TIMER_3_OFFSET 0x60 +// 0x80 skipped? +#define TIMER_4_OFFSET 0xA0 +#define TIMER_5_OFFSET 0xC0 +#define TIMER_6_OFFSET 0xE0 + +static const int HalTimerOffsets[] = { + TIMER_0_OFFSET, + TIMER_1_OFFSET, + TIMER_2_OFFSET, + TIMER_3_OFFSET, + TIMER_4_OFFSET, + TIMER_5_OFFSET, + TIMER_6_OFFSET, +}; + +#define TIMERREG(rgof) (* (volatile uint32_t*) ((uintptr_t)HalTimerBase + rgof)) +#define TIMERREGN(n, rgof) (* (volatile uint32_t*) ((uintptr_t)HalTimerBase + HalTimerOffsets[n] + rgof)) + +#define TIMER_CONFIG(n) TIMERREGN(n, 0x00) +#define TIMER_STATE(n) TIMERREGN(n, 0x04) +#define TIMER_COUNT_BUFFER(n) TIMERREGN(n, 0x08) +#define TIMER_COUNT_BUFFER2(n) TIMERREGN(n, 0x0C) +#define TIMER_PRESCALER(n) TIMERREGN(n, 0x10) +#define TIMER_UNKNOWN3(n) TIMERREGN(n, 0x14) + +#define TIMER_TICKSHIGH TIMERREG(0x80) +#define TIMER_TICKSLOW TIMERREG(0x84) +#define TIMER_UNKREG0 TIMERREG(0x88) +#define TIMER_UNKREG1 TIMERREG(0x8C) +#define TIMER_UNKREG2 TIMERREG(0x90) +#define TIMER_UNKREG3 TIMERREG(0x94) +#define TIMER_UNKREG4 TIMERREG(0x98) +#define TIMER_IRQSTAT TIMERREG(0x10000) // Woah!! +#define TIMER_IRQLATCH TIMERREG(0xF8) // 0x118 on the iPodTouch2G + +// TIMER_CONFIG +#define TIMER_PRODUCE_INTERRUPTS 0x7000 // INT0_EN | INT1_EN | OVF_EN +#define TIMER_DIVIDER(Divider) ((Divider) << 8) +#define TIMER_OPTION6 (1 << 6) + +#define TIMER_SPECIAL_BIT_1 0x01000000 +#define TIMER_SPECIAL_BIT_2 0x02000000 + +#define TIMER_TICKS_PER_SEC 12000000 +#define IRQS_PER_SEC 240 + +static KINTERRUPT HalTimerInterrupt; +static KSPIN_LOCK HalTimerInterruptLock; + +static const uint32_t HalEventTimerConfig = + TIMER_PRODUCE_INTERRUPTS | TIMER_DIVIDER(TIMER_DIVIDE2) | TIMER_OPTION6; + +static void* HalTimerBase; + +static void HalTimerInitRtc() +{ + TIMER_UNKREG0 = 0xA; + TIMER_UNKREG2 = 0xFFFFFFFF; + TIMER_UNKREG1 = 0xFFFFFFFF; + TIMER_UNKREG4 = 0xFFFFFFFF; + TIMER_UNKREG3 = 0xFFFFFFFF; + TIMER_UNKREG0 = 0x18010; +} + +static void HalTimerStop(int TimerIndex) +{ + TIMER_STATE(TimerIndex) = TIMER_STATE_STOP; +} + +static void HalTimerConfig( + int TimerIndex, + uint32_t Config, + uint32_t CountBuffer, + uint32_t CountBuffer2, + uint32_t Prescaler, + bool Start +) +{ + bool Disable = KeDisableInterrupts(); + + // Stop the timer because we're editing it + TIMER_STATE(TimerIndex) = TIMER_STATE_STOP; + + TIMER_CONFIG(TimerIndex) = Config; + TIMER_COUNT_BUFFER(TimerIndex) = CountBuffer; + TIMER_COUNT_BUFFER2(TimerIndex) = CountBuffer2; + TIMER_PRESCALER(TimerIndex) = Prescaler; + + TIMER_STATE(TimerIndex) = TIMER_STATE_MANUAL_UPDATE; + + if (Start) { + // Go!! + TIMER_STATE(TimerIndex) = TIMER_STATE_START; + } + + KeRestoreInterrupts(Disable); +} + +static void HalTimerSetUnkReg0ForInit() +{ + TIMER_UNKREG0 |= 0xA; +} + +void HalTimerHandler(UNUSED PKINTERRUPT Interrupt, UNUSED void* Context) +{ + ASSERT(Interrupt == &HalTimerInterrupt); + + uint32_t Stat = TIMER_IRQSTAT; + + // signal to the timer controller that we're handling it + uint32_t Discard = TIMER_IRQLATCH; + Discard--; + + if (Stat & TIMER_SPECIAL_BIT_1) + TIMER_UNKREG0 |= TIMER_SPECIAL_BIT_1; + + if (Stat & TIMER_SPECIAL_BIT_1) + TIMER_UNKREG0 |= TIMER_SPECIAL_BIT_2; + + // uhhhhh + // n.b. emulator always returns 0xFFFFFFFF + uint32_t Timer4Flags = Stat >> (8 * (TIMER_COUNT - TIMER_EVENT - 1)); + if (Timer4Flags & (1 << 0)) + { + KeTimerTick(); + } + + TIMER_IRQLATCH = Stat; +} + +void HalInitTimer() +{ + HalTimerBase = MmMapIoSpace( + TIMER1_MEM_BASE, + PAGE_SIZE * 0x11, // yeah, we also need access to IRQSTAT + MM_PROT_READ | MM_PROT_WRITE | MM_MISC_DISABLE_CACHE, + POOL_TAG("Tmr1") + ); + ASSERT(HalTimerBase); + + HalClockSetGateEnabled(CLOCK_GATE_TIMER, true); + + for (int i = 0; i < TIMER_COUNT; i++) { + HalTimerStop(i); + } + + HalTimerSetUnkReg0ForInit(); + + HalTimerInitRtc(); + + bool Restore = KeDisableInterrupts(); + + KeInitializeInterrupt( + &HalTimerInterrupt, + &HalTimerHandler, + NULL, + &HalTimerInterruptLock, + TIMER_IRQ, + IPL_CLOCK, + false + ); + + KeConnectInterrupt(&HalTimerInterrupt); + + // Enable event timer + HalTimerConfig( + TIMER_EVENT, + HalEventTimerConfig, + TIMER_TICKS_PER_SEC / IRQS_PER_SEC, + 0, // CountBuffer2 + 0, // Prescaler + true + ); + + KeRestoreInterrupts(Restore); +} + +uint64_t HalGetTickCount() +{ + uint32_t TicksHigh, TicksLow, TicksHigh2; + + bool Restore = KeDisableInterrupts(); + if (!HalTimerBase) + { + KeRestoreInterrupts(Restore); + return 0; + } + + do + { + TicksHigh = TIMER_TICKSHIGH; + TicksLow = TIMER_TICKSLOW; + TicksHigh2 = TIMER_TICKSHIGH; + } + while (TicksHigh != TicksHigh2); + + KeRestoreInterrupts(Restore); + return ((uint64_t)TicksHigh << 32ULL) | TicksLow; +} + +uint64_t HalGetTickFrequency() +{ + return TIMER_TICKS_PER_SEC; +} + +uint64_t HalGetIntTimerFrequency() +{ + DbgPrint("%s NYI", __func__); + return 1000000; +} + +uint64_t HalGetIntTimerDeltaTicks() +{ + DbgPrint("%s NYI", __func__); + return 1000; +} + +uint64_t HalGetInterruptDeltaTime() +{ + DbgPrint("%s NYI", __func__); + return 1000; +} + +bool HalUseOneShotIntTimer() +{ + return false; +} + +void HalRequestInterruptInTicks(uint64_t ticks) +{ + (void) ticks; + DbgPrint("%s intentionally left blank"); +} diff --git a/drivers/ipodgpio/Makefile b/drivers/ipodgpio/Makefile new file mode 100644 index 00000000..c97f9e4b --- /dev/null +++ b/drivers/ipodgpio/Makefile @@ -0,0 +1,9 @@ +# The Boron Operating System +# Common makefile for all driver targets + +DRIVER_NAME = ipodgpio +# DRIVER_ENTRY = DriverEntry +# DEBUG = yes +# DEBUG2 = no + +include ../CommonMakefile diff --git a/drivers/ipodgpio/source/main.c b/drivers/ipodgpio/source/main.c new file mode 100644 index 00000000..777de67f --- /dev/null +++ b/drivers/ipodgpio/source/main.c @@ -0,0 +1,25 @@ +/*** + The Boron Operating System + Copyright (C) 2026 iProgramInCpp + +Module name: + main.c + +Abstract: + This module implements the main function for the + iPod touch GPIO device driver. + +Author: + iProgramInCpp - 16 March 2026 +***/ +#include +#include + +PDRIVER_OBJECT gDriverObject; + +BSTATUS DriverEntry(PDRIVER_OBJECT DriverObject) +{ + gDriverObject = DriverObject; + DbgPrint("Hello from iPod GPIO driver!"); + return STATUS_SUCCESS; +} diff --git a/drivers/test/source/fs1tst.c b/drivers/test/source/fs1tst.c index ae71e0ab..59ffa6ee 100644 --- a/drivers/test/source/fs1tst.c +++ b/drivers/test/source/fs1tst.c @@ -40,7 +40,7 @@ void DirectoryList(const char* Path) Status = ObReferenceObjectByHandle(Handle, NULL, &ObjectV); PFILE_OBJECT File = (PFILE_OBJECT) ObjectV; - IoResetDirectoryReadHead(File); + //IoResetDirectoryReadHead(File); IO_STATUS_BLOCK Iosb; IO_DIRECTORY_ENTRY DirEnt; diff --git a/drivers/test/source/fworktst.c b/drivers/test/source/fworktst.c index d6d61245..2f1e8b5a 100644 --- a/drivers/test/source/fworktst.c +++ b/drivers/test/source/fworktst.c @@ -18,6 +18,10 @@ Module name: #include #include "utils.h" +#if defined TARGET_I386 || defined TARGET_AMD64 +#define USE_TSC +#endif + // 2023... It was a year of giant changes for me. I'm sorry that I couldn't // get a fully stable demo (it seems to be reasonably stable on 4 cores but // cracks on 32 cores...), but yeah, it is what it is, I'll fix it in 2024. @@ -26,7 +30,8 @@ Module name: // ####### GRAPHICS BACKEND ####### -#define BACKGROUND_COLOR 0x09090F +//#define BACKGROUND_COLOR 0x09090F +#define BACKGROUND_COLOR 0 uint8_t* PixBuff; int PixWidth, PixHeight, PixPitch, PixBPP; @@ -139,6 +144,8 @@ void Init() // ####### UTILITY LIBRARY ####### +#ifdef USE_TSC + uint64_t ReadTsc() { uintptr_t low, high; @@ -156,6 +163,8 @@ unsigned RandTscBased() return ((uint32_t)Tsc ^ (uint32_t)(Tsc >> 32)); } +#endif + int g_randGen = 0x9521af17; int Rand() { @@ -283,13 +292,14 @@ NO_RETURN void T_Explodeable(UNUSED void* Parameter) memset(&Data, 0, sizeof Data); int OffsetX = PixWidth * 400 / 1024; + int OffsetY = PixHeight * 400 / 768; // This is a fire, so it doesn't have a base. Data.m_x = PixWidth / 2; Data.m_y = PixHeight - 1; Data.m_actX = INT_TO_FP(Data.m_x); Data.m_actY = INT_TO_FP(Data.m_y); - Data.m_velY = -INT_TO_FP(400 + Rand() % 400); + Data.m_velY = -INT_TO_FP(OffsetY + Rand() % OffsetY); Data.m_velX = OffsetX * RandFPSign(); Data.m_color = GetRandomColor(); Data.m_explosionRange = Rand() % 100 + 100; @@ -360,7 +370,9 @@ NO_RETURN void CoreUsage(void* LookedAtThread); void PerformFireworksTest() { Init(); +#ifdef USE_TSC g_randGen ^= RandTscBased(); +#endif FillScreen(BACKGROUND_COLOR); //HalDisplayString("\x1B[40m\x1B[1;1HThe Boron Operating System - Fireworks test\nHappy New Year 2024!"); diff --git a/drivers/test/source/inttst.c b/drivers/test/source/inttst.c index 057f455d..e1a6b12c 100644 --- a/drivers/test/source/inttst.c +++ b/drivers/test/source/inttst.c @@ -44,7 +44,11 @@ void PerformIntTest() if (!KeConnectInterrupt(&Int1)) KeCrash("Test fail: cannot connect Int1 (first test)"); if (!KeConnectInterrupt(&Int2)) KeCrash("Test fail: cannot connect Int2 (first test)"); +#if defined TARGET_I386 || defined TARGET_AMD64 ASM("int $0x80":::"memory"); +#else + KeCrash("Unimplemented: PerformIntTest()"); +#endif if (!Int1Fired) KeCrash("Test fail: int1 not fired (first test)"); if (!Int2Fired) KeCrash("Test fail: int2 not fired (first test)"); @@ -61,7 +65,9 @@ void PerformIntTest() if (!KeConnectInterrupt(&Int1)) KeCrash("Test fail: cannot connect int1 (second test)"); if ( KeConnectInterrupt(&Int2)) KeCrash("Test fail: can connect int2 (second test)"); +#if defined TARGET_I386 || defined TARGET_AMD64 ASM("int $0x80":::"memory"); +#endif if (!Int1Fired) KeCrash("Test fail: int1 not fired (second test)"); if ( Int2Fired) KeCrash("Test fail: int2 fired (second test)"); diff --git a/drivers/test/source/main.c b/drivers/test/source/main.c index afaf27df..d5bbf939 100644 --- a/drivers/test/source/main.c +++ b/drivers/test/source/main.c @@ -85,14 +85,14 @@ NO_RETURN void DriverTestThread(UNUSED void* Parameter) //PerformProcessTest(); //PerformMutexTest(); //PerformBallTest(); - //PerformFireworksTest(); + PerformFireworksTest(); //PerformHandleTest(); //PerformApcTest(); //PerformRwlockTest(); //PerformObjectTest(); //PerformMdlTest(); //PerformIntTest(); - PerformKeyboardTest(); + //PerformKeyboardTest(); //PerformStorageTest(); //PerformExObTest(); //PerformCcbTest(); @@ -103,6 +103,7 @@ NO_RETURN void DriverTestThread(UNUSED void* Parameter) //PerformMm5Test(); //PerformFs1Test(); //PerformPipeTest(); + //PerformTwoThreadsTest(); LogMsg(ANSI_GREEN "*** All tests have concluded." ANSI_RESET); KeTerminateThread(0); diff --git a/drivers/test/source/mm1tst.c b/drivers/test/source/mm1tst.c index 48586deb..d0fd1ec2 100644 --- a/drivers/test/source/mm1tst.c +++ b/drivers/test/source/mm1tst.c @@ -20,6 +20,8 @@ Module name: // NOTE: This is not ideal. The test penetrates into internal functions. +#if defined TARGET_I386 || defined TARGET_AMD64 + uint8_t PortReadByte(uint16_t portNo) { uint8_t rv; @@ -32,8 +34,8 @@ void PortWriteByte(uint16_t portNo, uint8_t data) ASM("outb %0, %1"::"a"((uint8_t)data),"Nd"((uint16_t)portNo)); } -void Reboot() { - +void Reboot() +{ uint8_t good = 0x02; while (good & 0x02) good = PortReadByte(0x64); @@ -43,6 +45,15 @@ void Reboot() { while (true) KeWaitForNextInterrupt(); } +#else + +void Reboot() +{ + KeCrash("TODO: Reboot()"); +} + +#endif + void PerformDemandPageTest() { LogMsg(">> Demand page test"); diff --git a/drivers/test/source/smthrtst.c b/drivers/test/source/smthrtst.c new file mode 100644 index 00000000..ec2eccd8 --- /dev/null +++ b/drivers/test/source/smthrtst.c @@ -0,0 +1,64 @@ +/*** + The Boron Operating System + Copyright (C) 2023 iProgramInCpp + +Module name: + smthrtst.c + +Abstract: + This module implements the simple thread test. + + It creates two threads that together never idle. + +Author: + iProgramInCpp - 18 March 2026 +***/ +#include +#include +#include +#include "utils.h" + +NO_RETURN +void SttThread1(UNUSED void* Context) +{ + long long i = 1; + while (true) + { + uint64_t ctt = HalGetTickCount(); + uint64_t cttP1S = ctt + HalGetTickFrequency(); + + while (HalGetTickCount() < cttP1S) { + i++; + } + + LogMsg("Thread 1 came up with %lld.", i); + } +} + +NO_RETURN +void SttThread2(UNUSED void* Context) +{ + long long i = 1; + while (true) + { + uint64_t ctt = HalGetTickCount(); + uint64_t cttP1S = ctt + HalGetTickFrequency(); + + while (HalGetTickCount() < cttP1S) { + i--; + } + + LogMsg("Thread 2 came up with %lld.", i); + } +} + +void PerformTwoThreadsTest() +{ + PKTHREAD Thrd1 = CreateThread(SttThread1, NULL); + PKTHREAD Thrd2 = CreateThread(SttThread2, NULL); + + void* Objects[2] = { Thrd1, Thrd2 }; + KeWaitForMultipleObjects(2, Objects, WAIT_TYPE_ALL, false, TIMEOUT_INFINITE, NULL, MODE_KERNEL); + + DbgPrint("PerformTwoThreadsTest done"); +} \ No newline at end of file diff --git a/drivers/test/source/tests.h b/drivers/test/source/tests.h index d2f61d69..afe35e65 100644 --- a/drivers/test/source/tests.h +++ b/drivers/test/source/tests.h @@ -35,3 +35,4 @@ void PerformMm4Test(void); void PerformMm5Test(void); void PerformFs1Test(void); void PerformPipeTest(void); +void PerformTwoThreadsTest(void); diff --git a/external/tamsyn6x12.h b/external/tamsyn6x12.h new file mode 100644 index 00000000..ce0549d6 --- /dev/null +++ b/external/tamsyn6x12.h @@ -0,0 +1,257 @@ +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xa8, 0x54, 0xa8, 0x54, 0xa8, 0x54, 0xa8, 0x54, 0xa8, 0x54, 0xa8, 0x54, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x60, 0x90, 0x90, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x20, 0x20, 0x20, 0x20, 0x20, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x20, 0x20, 0x20, 0x20, 0x20, 0xfc, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x20, 0x20, 0x20, 0x20, 0x20, 0x3c, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0xe0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x30, 0x40, 0x40, 0xf0, 0x40, 0x40, 0xf8, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, +0x00, 0x50, 0x50, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x50, 0x50, 0xf8, 0x50, 0x50, 0xf8, 0x50, 0x50, 0x00, 0x00, 0x00, +0x10, 0x10, 0x38, 0x40, 0x20, 0x10, 0x08, 0x70, 0x20, 0x20, 0x00, 0x00, +0x00, 0x40, 0xa0, 0xa8, 0x50, 0x20, 0x50, 0xa8, 0x28, 0x10, 0x00, 0x00, +0x00, 0x40, 0xa0, 0xa0, 0x40, 0xa8, 0x90, 0x90, 0x68, 0x00, 0x00, 0x00, +0x00, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x10, 0x20, 0x20, 0x40, 0x40, 0x40, 0x40, 0x20, 0x20, 0x10, 0x00, +0x00, 0x40, 0x20, 0x20, 0x10, 0x10, 0x10, 0x10, 0x20, 0x20, 0x40, 0x00, +0x00, 0x00, 0x00, 0x20, 0xa8, 0x70, 0xa8, 0x20, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x20, 0x20, 0xf8, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x20, 0x40, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, +0x00, 0x00, 0x10, 0x10, 0x20, 0x20, 0x40, 0x40, 0x80, 0x80, 0x00, 0x00, +0x00, 0x00, 0x70, 0x88, 0x98, 0xa8, 0xc8, 0x88, 0x70, 0x00, 0x00, 0x00, +0x00, 0x00, 0x20, 0x60, 0xa0, 0x20, 0x20, 0x20, 0xf8, 0x00, 0x00, 0x00, +0x00, 0x00, 0x70, 0x88, 0x08, 0x10, 0x20, 0x40, 0xf8, 0x00, 0x00, 0x00, +0x00, 0x00, 0xf8, 0x08, 0x10, 0x30, 0x08, 0x88, 0x70, 0x00, 0x00, 0x00, +0x00, 0x00, 0x10, 0x30, 0x50, 0x90, 0xf8, 0x10, 0x10, 0x00, 0x00, 0x00, +0x00, 0x00, 0xf8, 0x80, 0xf0, 0x08, 0x08, 0x88, 0x70, 0x00, 0x00, 0x00, +0x00, 0x00, 0x30, 0x40, 0x80, 0xf0, 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, +0x00, 0x00, 0xf8, 0x08, 0x10, 0x10, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, +0x00, 0x00, 0x70, 0x88, 0x88, 0x70, 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, +0x00, 0x00, 0x70, 0x88, 0x88, 0x78, 0x08, 0x10, 0x60, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, 0x60, 0x60, 0x20, 0x40, 0x00, +0x00, 0x00, 0x08, 0x10, 0x20, 0x40, 0x20, 0x10, 0x08, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x80, 0x40, 0x20, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, +0x00, 0x70, 0x88, 0x08, 0x10, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, +0x00, 0x70, 0x88, 0x88, 0x98, 0xa8, 0xb8, 0x80, 0x80, 0x78, 0x00, 0x00, +0x00, 0x00, 0x20, 0x50, 0x88, 0x88, 0xf8, 0x88, 0x88, 0x00, 0x00, 0x00, +0x00, 0x00, 0xf0, 0x88, 0x88, 0xf0, 0x88, 0x88, 0xf0, 0x00, 0x00, 0x00, +0x00, 0x00, 0x38, 0x40, 0x80, 0x80, 0x80, 0x40, 0x38, 0x00, 0x00, 0x00, +0x00, 0x00, 0xf0, 0x88, 0x88, 0x88, 0x88, 0x90, 0xe0, 0x00, 0x00, 0x00, +0x00, 0x00, 0xf8, 0x80, 0x80, 0xf0, 0x80, 0x80, 0xf8, 0x00, 0x00, 0x00, +0x00, 0x00, 0xf8, 0x80, 0x80, 0xf0, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x38, 0x40, 0x80, 0x98, 0x88, 0x48, 0x38, 0x00, 0x00, 0x00, +0x00, 0x00, 0x88, 0x88, 0x88, 0xf8, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, +0x00, 0x00, 0x70, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00, 0x00, +0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, +0x00, 0x00, 0x88, 0x90, 0xa0, 0xc0, 0xa0, 0x90, 0x88, 0x00, 0x00, 0x00, +0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xf8, 0x00, 0x00, 0x00, +0x00, 0x00, 0x88, 0xd8, 0xa8, 0xa8, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, +0x00, 0x00, 0x88, 0xc8, 0xa8, 0x98, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, +0x00, 0x00, 0x70, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, +0x00, 0x00, 0xf0, 0x88, 0x88, 0xf0, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x70, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70, 0x10, 0x08, 0x00, +0x00, 0x00, 0xf0, 0x88, 0x88, 0xf0, 0xa0, 0x90, 0x88, 0x00, 0x00, 0x00, +0x00, 0x00, 0x78, 0x80, 0x80, 0x70, 0x08, 0x08, 0xf0, 0x00, 0x00, 0x00, +0x00, 0x00, 0xf8, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, +0x00, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, +0x00, 0x00, 0x88, 0x88, 0x88, 0x50, 0x50, 0x20, 0x20, 0x00, 0x00, 0x00, +0x00, 0x00, 0x88, 0x88, 0x88, 0xa8, 0xa8, 0xa8, 0xd8, 0x00, 0x00, 0x00, +0x00, 0x00, 0x88, 0x88, 0x50, 0x20, 0x50, 0x88, 0x88, 0x00, 0x00, 0x00, +0x00, 0x00, 0x88, 0x88, 0x50, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, +0x00, 0x00, 0xf8, 0x10, 0x20, 0x20, 0x40, 0x40, 0xf8, 0x00, 0x00, 0x00, +0x00, 0x70, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x70, 0x00, +0x00, 0x00, 0x80, 0x80, 0x40, 0x40, 0x20, 0x20, 0x10, 0x10, 0x00, 0x00, +0x00, 0x70, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x70, 0x00, +0x00, 0x00, 0x20, 0x50, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, +0x00, 0x40, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, 0x00, 0x00, +0x00, 0x00, 0x80, 0x80, 0xf0, 0x88, 0x88, 0x88, 0xf0, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x78, 0x80, 0x80, 0x80, 0x78, 0x00, 0x00, 0x00, +0x00, 0x00, 0x08, 0x08, 0x78, 0x88, 0x88, 0x88, 0x78, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x70, 0x88, 0xf8, 0x80, 0x78, 0x00, 0x00, 0x00, +0x00, 0x00, 0x38, 0x40, 0xf8, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x78, 0x88, 0x88, 0x88, 0x78, 0x08, 0x70, 0x00, +0x00, 0x00, 0x80, 0x80, 0xf0, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, +0x00, 0x00, 0x20, 0x00, 0x60, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00, 0x00, +0x00, 0x00, 0x10, 0x00, 0x30, 0x10, 0x10, 0x10, 0x10, 0x10, 0x60, 0x00, +0x00, 0x00, 0x80, 0x80, 0x90, 0xa0, 0xe0, 0x90, 0x88, 0x00, 0x00, 0x00, +0x00, 0x00, 0x60, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xd8, 0xa8, 0xa8, 0xa8, 0xa8, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xf0, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x70, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xf0, 0x88, 0x88, 0x88, 0xf0, 0x80, 0x80, 0x00, +0x00, 0x00, 0x00, 0x00, 0x78, 0x88, 0x88, 0x88, 0x78, 0x08, 0x08, 0x00, +0x00, 0x00, 0x00, 0x00, 0xb8, 0xc0, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x78, 0x80, 0x70, 0x08, 0xf0, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x40, 0xf8, 0x40, 0x40, 0x40, 0x38, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, 0x78, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x88, 0x50, 0x20, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0xa8, 0xa8, 0xd8, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x88, 0x50, 0x20, 0x50, 0x88, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, 0x78, 0x08, 0x70, 0x00, +0x00, 0x00, 0x00, 0x00, 0xf8, 0x10, 0x20, 0x40, 0xf8, 0x00, 0x00, 0x00, +0x18, 0x20, 0x20, 0x20, 0x20, 0xc0, 0x20, 0x20, 0x20, 0x20, 0x18, 0x00, +0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, +0xc0, 0x20, 0x20, 0x20, 0x20, 0x18, 0x20, 0x20, 0x20, 0x20, 0xc0, 0x00, +0x00, 0x00, 0x48, 0xa8, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, +0x00, 0x20, 0x20, 0x70, 0x80, 0x80, 0x80, 0x70, 0x20, 0x20, 0x00, 0x00, +0x00, 0x00, 0x30, 0x40, 0x40, 0xf0, 0x40, 0x40, 0xf8, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x88, 0x70, 0x50, 0x70, 0x88, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x88, 0x50, 0x20, 0xf8, 0x20, 0xf8, 0x20, 0x00, 0x00, 0x00, +0x00, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x78, 0x84, 0x94, 0xa4, 0x94, 0x84, 0x78, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x28, 0x50, 0xa0, 0x50, 0x28, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x60, 0x90, 0x90, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x10, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xc0, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xa0, 0x50, 0x28, 0x50, 0xa0, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x40, 0x80, 0x88, 0x70, 0x00, +0x40, 0x20, 0x00, 0x20, 0x20, 0x50, 0x70, 0x88, 0x88, 0x00, 0x00, 0x00, +0x10, 0x20, 0x00, 0x20, 0x20, 0x50, 0x70, 0x88, 0x88, 0x00, 0x00, 0x00, +0x20, 0x50, 0x00, 0x20, 0x20, 0x50, 0x70, 0x88, 0x88, 0x00, 0x00, 0x00, +0x68, 0xb0, 0x00, 0x20, 0x20, 0x50, 0x70, 0x88, 0x88, 0x00, 0x00, 0x00, +0x50, 0x00, 0x20, 0x20, 0x50, 0x50, 0x70, 0x88, 0x88, 0x00, 0x00, 0x00, +0x20, 0x50, 0x50, 0x20, 0x20, 0x50, 0x70, 0x88, 0x88, 0x00, 0x00, 0x00, +0x00, 0x00, 0x38, 0x30, 0x50, 0x58, 0x70, 0x90, 0x98, 0x00, 0x00, 0x00, +0x00, 0x00, 0x38, 0x40, 0x80, 0x80, 0x80, 0x40, 0x38, 0x10, 0x60, 0x00, +0x40, 0x20, 0x00, 0xf8, 0x80, 0xf0, 0x80, 0x80, 0xf8, 0x00, 0x00, 0x00, +0x10, 0x20, 0x00, 0xf8, 0x80, 0xf0, 0x80, 0x80, 0xf8, 0x00, 0x00, 0x00, +0x20, 0x50, 0x00, 0xf8, 0x80, 0xf0, 0x80, 0x80, 0xf8, 0x00, 0x00, 0x00, +0x50, 0x00, 0xf8, 0x80, 0x80, 0xf0, 0x80, 0x80, 0xf8, 0x00, 0x00, 0x00, +0x40, 0x20, 0x00, 0x70, 0x20, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00, 0x00, +0x10, 0x20, 0x00, 0x70, 0x20, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00, 0x00, +0x20, 0x50, 0x00, 0x70, 0x20, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00, 0x00, +0x50, 0x00, 0x70, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00, 0x00, +0x00, 0x00, 0xf0, 0x48, 0x48, 0xe8, 0x48, 0x50, 0xe0, 0x00, 0x00, 0x00, +0x68, 0xb0, 0x00, 0x88, 0xc8, 0xa8, 0x98, 0x88, 0x88, 0x00, 0x00, 0x00, +0x40, 0x20, 0x00, 0x70, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, +0x10, 0x20, 0x00, 0x70, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, +0x20, 0x50, 0x00, 0x70, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, +0x68, 0xb0, 0x00, 0x70, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, +0x50, 0x00, 0x70, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x48, 0x30, 0x30, 0x48, 0x00, 0x00, 0x00, 0x00, +0x00, 0x08, 0x70, 0x98, 0x98, 0xa8, 0xc8, 0xc8, 0x70, 0x80, 0x00, 0x00, +0x40, 0x20, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, +0x10, 0x20, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, +0x20, 0x50, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, +0x50, 0x00, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, +0x10, 0x20, 0x00, 0x88, 0x88, 0x50, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, +0x00, 0x00, 0x80, 0xf0, 0x88, 0x88, 0x88, 0xf0, 0x80, 0x00, 0x00, 0x00, +0x00, 0x00, 0x70, 0x88, 0x90, 0xb0, 0x88, 0x88, 0xb0, 0x00, 0x00, 0x00, +0x00, 0x40, 0x20, 0x00, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, 0x00, 0x00, +0x00, 0x10, 0x20, 0x00, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, 0x00, 0x00, +0x20, 0x50, 0x00, 0x00, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, 0x00, 0x00, +0x68, 0xb0, 0x00, 0x00, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, 0x00, 0x00, +0x00, 0x00, 0x50, 0x00, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, 0x00, 0x00, +0x30, 0x48, 0x30, 0x00, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xd8, 0x28, 0x78, 0xa0, 0xd8, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x70, 0x80, 0x80, 0x80, 0x70, 0x20, 0xc0, 0x00, +0x00, 0x40, 0x20, 0x00, 0x70, 0x88, 0xf8, 0x80, 0x78, 0x00, 0x00, 0x00, +0x00, 0x10, 0x20, 0x00, 0x70, 0x88, 0xf8, 0x80, 0x78, 0x00, 0x00, 0x00, +0x20, 0x50, 0x00, 0x00, 0x70, 0x88, 0xf8, 0x80, 0x78, 0x00, 0x00, 0x00, +0x00, 0x00, 0x50, 0x00, 0x70, 0x88, 0xf8, 0x80, 0x78, 0x00, 0x00, 0x00, +0x00, 0x40, 0x20, 0x00, 0x60, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00, 0x00, +0x00, 0x10, 0x20, 0x00, 0x60, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00, 0x00, +0x20, 0x50, 0x00, 0x00, 0x60, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00, 0x00, +0x00, 0x00, 0x50, 0x00, 0x60, 0x20, 0x20, 0x20, 0x70, 0x00, 0x00, 0x00, +0x20, 0x38, 0x70, 0x10, 0x78, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, +0x68, 0xb0, 0x00, 0x00, 0xf0, 0x88, 0x88, 0x88, 0x88, 0x00, 0x00, 0x00, +0x00, 0x40, 0x20, 0x00, 0x70, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, +0x00, 0x10, 0x20, 0x00, 0x70, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, +0x20, 0x50, 0x00, 0x00, 0x70, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, +0x68, 0xb0, 0x00, 0x00, 0x70, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, +0x00, 0x00, 0x50, 0x00, 0x70, 0x88, 0x88, 0x88, 0x70, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x20, 0x00, 0xf8, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x08, 0x70, 0x98, 0xa8, 0xc8, 0x70, 0x80, 0x00, 0x00, +0x00, 0x40, 0x20, 0x00, 0x88, 0x88, 0x88, 0x88, 0x78, 0x00, 0x00, 0x00, +0x00, 0x10, 0x20, 0x00, 0x88, 0x88, 0x88, 0x88, 0x78, 0x00, 0x00, 0x00, +0x20, 0x50, 0x00, 0x00, 0x88, 0x88, 0x88, 0x88, 0x78, 0x00, 0x00, 0x00, +0x00, 0x00, 0x50, 0x00, 0x88, 0x88, 0x88, 0x88, 0x78, 0x00, 0x00, 0x00, +0x00, 0x10, 0x20, 0x00, 0x88, 0x88, 0x88, 0x88, 0x78, 0x08, 0x70, 0x00, +0x00, 0x00, 0x80, 0x80, 0xf0, 0x88, 0x88, 0x88, 0xf0, 0x80, 0x80, 0x00, +0x00, 0x00, 0x50, 0x00, 0x88, 0x88, 0x88, 0x88, 0x78, 0x08, 0x70, 0x00, +0xff, 0x8f, 0x77, 0xf7, 0xef, 0xdf, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, \ No newline at end of file diff --git a/tools/Makefile.arm b/tools/Makefile.arm new file mode 100644 index 00000000..622dc08f --- /dev/null +++ b/tools/Makefile.arm @@ -0,0 +1,10 @@ +# arm Makefile + +#IMAGE_TARGET = $(BUILD_DIR)/../image.$(TARGETL).img +IMAGE_TARGET = $(BUILD_DIR)/../image.$(TARGETL).tar + +DRIVERS_LIST = \ + halipod1,1 \ + tmpfs \ + ipodgpio \ + test diff --git a/tools/build_image_arm.mk b/tools/build_image_arm.mk new file mode 100644 index 00000000..0518712c --- /dev/null +++ b/tools/build_image_arm.mk @@ -0,0 +1,10 @@ +# TEMPORARY + +$(IMAGE_TARGET): kernel drivers apps initrd + @echo "[MK]\tBuilding tar file" + @rm -rf $(ISO_DIR) + @mkdir -p $(ISO_DIR) + @cp $(KERNEL_ELF) $(ISO_DIR)/$(KERNEL_NAME) + @cp -r $(BUILD_DIR)/*.sys $(BUILD_DIR)/*.tar $(ISO_DIR) + @tar -cvf $(IMAGE_TARGET) -C $(ISO_DIR) . > /dev/null + @rm -rf $(ISO_DIR) diff --git a/tools/libgcc-arm.a b/tools/libgcc-arm.a new file mode 100644 index 00000000..26c5847f Binary files /dev/null and b/tools/libgcc-arm.a differ diff --git a/boron/libgcc-i686.a b/tools/libgcc-i686.a similarity index 100% rename from boron/libgcc-i686.a rename to tools/libgcc-i686.a diff --git a/tools/run-amd64.bat b/tools/run-amd64.bat index 004f423e..fc76f85e 100644 --- a/tools/run-amd64.bat +++ b/tools/run-amd64.bat @@ -16,7 +16,7 @@ if exist %nspath%\vdiske2.img ( qemu-system-x86_64.exe -no-reboot -no-shutdown -d int -M smm=off ^ -M q35 ^ -m 256M ^ --smp 1 ^ +-smp 6 ^ -boot d ^ -display sdl ^ -accel tcg ^ diff --git a/tools/run-arm.bat b/tools/run-arm.bat new file mode 100644 index 00000000..8384f76f --- /dev/null +++ b/tools/run-arm.bat @@ -0,0 +1,50 @@ +@rem Run script + +@echo off + +set backupPath=%path% +set NSPath=%CD% +cd /d c:\Program Files\qemu +set path=%path%;%NSPath% + +@rem NOTE: This will likely only run on iProgramInCpp's machine for now. +@rem Just use the i386 or amd64 builds... + +@rem You will need the following projects: +@rem https://github.com/iProgramMC/qemu-ios (for the iPod touch 1G emulator) +@rem https://github.com/iProgramMC/openiBoot (for the bootloader) +@rem https://github.com/iDroid-Project/openiBoot-toolchain (the ancient arm-elf toolchain OpeniBoot uses to compile) + +set ipodDataPath=W:\iphone-qemu\ipod-data +set ipodQemuPath=W:\iphone-qemu\qemu-ios\build + +set qemu=%ipodQemuPath%\qemu-system-arm.exe +set ibootPath=%ipodDataPath%\ipt_1g_openiboot.dec +set nandPath=%ipodDataPath%\nand.img +set oibElfPath=%NSPath%\build\image.arm.tar +set bootromPath=%ipodDataPath%\bootrom_s5l8900 +set norPath=%ipodDataPath%\nor_n45ap.bin + +set machineType=iPod-Touch,bootrom=%bootromPath%,iboot=%ibootPath%,nand=%nandPath%,oib-elf=%oibElfPath% + +%qemu% ^ + -M %machineType% ^ + -serial stdio ^ + -cpu max ^ + -m 1G ^ + -d unimp ^ + -pflash %norPath% ^ + -display sdl ^ + -monitor telnet:127.0.0.1:56789,server,nowait ^ + -no-reboot ^ + -no-shutdown ^ + -d int ^ + -D %nspath%\keep\armlog.txt ^ + -s + + @rem -d int ^ + @rem -D %nspath%\keep\armlog.txt ^ +rem go back +cd /d %NSPath% + +set path=%backupPath% diff --git a/tools/run-arm.sh b/tools/run-arm.sh new file mode 100644 index 00000000..a316e096 --- /dev/null +++ b/tools/run-arm.sh @@ -0,0 +1,3 @@ +# works only in wsl :) + +cmd.exe /k "tools\run-arm.bat && exit" diff --git a/tools/run-unix-arm.sh b/tools/run-unix-arm.sh new file mode 100644 index 00000000..a7f4a27d --- /dev/null +++ b/tools/run-unix-arm.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +# NOTE: this is outdated. DO NOT USE THIS! + +qemu-system-arm \ + -no-reboot \ + -no-shutdown \ + -d int \ + -M virt \ + -cpu arm1176 \ + -m 256M \ + -kernel ../build/arm/kernel.elf \ + -serial stdio diff --git a/tools/run_rule_arm.mk b/tools/run_rule_arm.mk new file mode 100644 index 00000000..45e5f5bc --- /dev/null +++ b/tools/run_rule_arm.mk @@ -0,0 +1,8 @@ + +run: image + @echo "Running..." + @./tools/run-unix-arm.sh + +runw: image + @echo "Invoking WSL to run the OS..." + @./tools/run-arm.sh diff --git a/tools/toolchain.arm.mk b/tools/toolchain.arm.mk new file mode 100644 index 00000000..52418539 --- /dev/null +++ b/tools/toolchain.arm.mk @@ -0,0 +1,32 @@ +# arm Compiler Toolchain +BCC ?= arm-none-eabi-gcc +BCXX ?= arm-none-eabi-g++ +BLD ?= arm-none-eabi-ld +BASM ?= unknown +# ^^ note: we will be using .S files + +SUBTARGET ?= v6 +$(eval $(call validate-option,SUBTARGET,v5 v6 v7)) + +ifeq ($(SUBTARGET),v5) + ARCH_CFLAGS = \ + -mcpu=arm926ej-s \ + -marm \ + -mfloat-abi=soft \ + -DTARGET_ARMV5 +else ifeq ($(SUBTARGET),v6) + ARCH_CFLAGS = \ + -mcpu=arm1176jzf-s \ + -marm \ + -mfloat-abi=soft \ + -DTARGET_ARMV6 +else + $(error ARMv7 not supported for now.) +endif + +ARCH_LDFLAGS = \ + -z max-page-size=0x1000 \ + -L$(DDK_DIR)/../../tools \ + -lgcc-arm + +LINK_ARCH = armelf diff --git a/tools/toolchain.i386.mk b/tools/toolchain.i386.mk index ec0ad9ed..ca0cc9ee 100644 --- a/tools/toolchain.i386.mk +++ b/tools/toolchain.i386.mk @@ -11,10 +11,10 @@ ARCH_CFLAGS = \ -mno-mmx \ -mno-sse \ -mno-sse2 - + ARCH_LDFLAGS = \ -z max-page-size=0x1000 \ - -L$(DDK_DIR)/../../boron \ + -L$(DDK_DIR)/../../tools \ -lgcc-i686 ARCH_ASFLAGS = \ diff --git a/user/CommonMakefile b/user/CommonMakefile index a72bfa87..5eaa0c69 100644 --- a/user/CommonMakefile +++ b/user/CommonMakefile @@ -3,6 +3,7 @@ # DEBUG flags DEBUG ?= yes DEBUG2 ?= no +DWARF_SYMBOLS ?= yes # User parms USER_DEFINES ?= @@ -54,18 +55,28 @@ define DEFAULT_VAR = endif endef -DEFINES = -DIS_DRIVER -OPT = -O2 - +DEFINES= ifeq ($(DEBUG), yes) DEFINES += -DDEBUG - OPT = -O3 endif ifeq ($(DEBUG2), yes) DEFINES += -DDEBUG2 endif +ifeq ($(SECURE), yes) + DEFINES += -DSECURE +endif + +OPT = -O2 + +ifeq ($(DWARF_SYMBOLS), yes) + DEFINES += -gdwarf-4 + OPT = -O0 +else + OPT = -O3 +endif + include ../../tools/toolchain.$(TARGETL).mk # User controllable CFLAGS. diff --git a/user/libboron/source/calls_amd64.S b/user/libboron/source/calls_amd64.S new file mode 100644 index 00000000..ded60935 --- /dev/null +++ b/user/libboron/source/calls_amd64.S @@ -0,0 +1,48 @@ +// The Boron Operating System +// Copyright (C) 2025 iProgramInCpp + +#ifdef TARGET_AMD64 + +.section .text +.att_syntax + +.macro CALL number, argcount, name + .globl \name + .type \name, @function +\name: + pushq %rbp + movq %rsp, %rbp + pushq %rbx + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 + + .if \argcount > 6 + movq 16(%rbp), %r12 + .endif + .if \argcount > 7 + movq 24(%rbp), %r13 + .endif + .if \argcount > 8 + movq 32(%rbp), %r14 + .endif + .if \argcount > 3 + movq %rcx, %r10 + .endif + + movq $\number, %rax + syscall + + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %rbx + popq %rbp + ret +.endm + +#include "calltbl.h" + +#endif diff --git a/user/libboron/source/calls_arm.S b/user/libboron/source/calls_arm.S new file mode 100644 index 00000000..8ee3f036 --- /dev/null +++ b/user/libboron/source/calls_arm.S @@ -0,0 +1,66 @@ +// The Boron Operating System +// Copyright (C) 2026 iProgramInCpp + +#ifdef TARGET_ARM + +.macro CALL number, argcount, name + .global \name + .type \name, %function +\name: + .if \argcount > 4 + push {fp, lr} + mov fp, sp + .endif + .if \argcount <= 4 + /* R0-R3 are already set up with parameters. No need to do anything. */ + /* In fact, the kernel helpfully backs up r4-r12 for us too! */ + svc #\number + bx lr + .elseif \argcount == 5 + push {r4} + ldr r4, [fp, #8] + svc #\number + pop {r4} + pop {fp, pc} + .elseif \argcount == 6 + push {r4-r5} + ldr r4, [fp, #8] + ldr r5, [fp, #12] + svc #\number + pop {r4-r5} + pop {fp, pc} + .elseif \argcount == 7 + push {r4-r6} + ldr r4, [fp, #8] + ldr r5, [fp, #12] + ldr r6, [fp, #16] + svc #\number + pop {r4-r6} + pop {fp, pc} + .elseif \argcount == 8 + push {r4-r7} + ldr r4, [fp, #8] + ldr r5, [fp, #12] + ldr r6, [fp, #16] + ldr r7, [fp, #20] + svc #\number + pop {r4-r7} + pop {fp, pc} + .elseif \argcount == 9 + push {r4-r8} + ldr r4, [fp, #8] + ldr r5, [fp, #12] + ldr r6, [fp, #16] + ldr r7, [fp, #20] + ldr r8, [fp, #24] + svc #\number + pop {r4-r8} + pop {fp, pc} + .else + .error Not supported! + .endif +.endm + +#include "calltbl.h" + +#endif diff --git a/user/libboron/source/calls.S b/user/libboron/source/calls_i386.S similarity index 59% rename from user/libboron/source/calls.S rename to user/libboron/source/calls_i386.S index 7e901c9f..497b15b7 100644 --- a/user/libboron/source/calls.S +++ b/user/libboron/source/calls_i386.S @@ -1,98 +1,59 @@ -// The Boron Operating System -// Copyright (C) 2025 iProgramInCpp - -.section .text -.att_syntax - -#ifdef TARGET_AMD64 - -.macro CALL number, argcount, name - .globl \name - .type \name, @function -\name: - pushq %rbp - movq %rsp, %rbp - pushq %rbx - pushq %r12 - pushq %r13 - pushq %r14 - pushq %r15 - - .if \argcount > 6 - movq 16(%rbp), %r12 - .endif - .if \argcount > 7 - movq 24(%rbp), %r13 - .endif - .if \argcount > 8 - movq 32(%rbp), %r14 - .endif - .if \argcount > 3 - movq %rcx, %r10 - .endif - - movq $\number, %rax - syscall - - popq %r15 - popq %r14 - popq %r13 - popq %r12 - popq %rbx - popq %rbp - ret -.endm - -#elif defined TARGET_I386 - -.macro CALL number, argcount, name - .globl \name - .type \name, @function -\name: - pushl %ebp - movl %esp, %ebp - pushl %ebx - pushl %esi - pushl %edi - - movl $\number, %eax - - .if \argcount > 0 - movl 8(%ebp), %ebx - .endif - .if \argcount > 1 - movl 12(%ebp), %ecx - .endif - .if \argcount > 2 - movl 16(%ebp), %edx - .endif - .if \argcount > 3 - movl 20(%ebp), %esi - .endif - .if \argcount > 4 - movl 24(%ebp), %edi - .endif - - .if \argcount == 6 - /* if we have *exactly* 6 arguments, then just pass the */ - /* last one into EBP */ - movl 28(%ebp), %ebp - .elseif \argcount > 6 - /* if we have *more* than 6 arguments, we need to pass a */ - /* pointer to the last arguments which we'll copy from */ - /* the stack */ - leal 28(%ebp), %ebp - .endif - - int $0x80 - - popl %edi - popl %esi - popl %ebx - popl %ebp - ret -.endmacro - -#endif - -#include "calltbl.h" +// The Boron Operating System +// Copyright (C) 2025 iProgramInCpp + +#ifdef TARGET_I386 + +.section .text +.att_syntax + +.macro CALL number, argcount, name + .globl \name + .type \name, @function +\name: + pushl %ebp + movl %esp, %ebp + pushl %ebx + pushl %esi + pushl %edi + + movl $\number, %eax + + .if \argcount > 0 + movl 8(%ebp), %ebx + .endif + .if \argcount > 1 + movl 12(%ebp), %ecx + .endif + .if \argcount > 2 + movl 16(%ebp), %edx + .endif + .if \argcount > 3 + movl 20(%ebp), %esi + .endif + .if \argcount > 4 + movl 24(%ebp), %edi + .endif + + .if \argcount == 6 + /* if we have *exactly* 6 arguments, then just pass the */ + /* last one into EBP */ + movl 28(%ebp), %ebp + .elseif \argcount > 6 + /* if we have *more* than 6 arguments, we need to pass a */ + /* pointer to the last arguments which we'll copy from */ + /* the stack */ + leal 28(%ebp), %ebp + .endif + + int $0x80 + + popl %edi + popl %esi + popl %ebx + popl %ebp + ret +.endmacro + +#include "calltbl.h" + +#endif diff --git a/user/libboron/source/entry.S b/user/libboron/source/entry.S index 3c70d6bb..6f1a5f9f 100644 --- a/user/libboron/source/entry.S +++ b/user/libboron/source/entry.S @@ -2,9 +2,9 @@ // Copyright (C) 2026 iProgramInCpp .section .text -.att_syntax #ifdef TARGET_AMD64 + .att_syntax /* void OSDLLJumpToEntry(uintptr_t StackBottom, void* EntryPoint, void* Context); */ .globl OSDLLJumpToEntry @@ -24,6 +24,7 @@ OSDLLEntry: call OSDLLRelocateSelf /* note: this function is NORETURN */ #elif defined TARGET_I386 + .att_syntax /* void OSDLLJumpToEntry(uintptr_t StackBottom, void* EntryPoint, void* Context); */ .globl OSDLLJumpToEntry @@ -44,6 +45,24 @@ OSDLLJumpToEntry: OSDLLEntry: jmp OSDLLRelocateSelf +#elif TARGET_ARM + + /* void OSDLLJumpToEntry(uintptr_t StackBottom, void* EntryPoint, void* Context); */ + .globl OSDLLJumpToEntry + .type OSDLLJumpToEntry, %function +OSDLLJumpToEntry: + mov sp, r0 + mov lr, r1 + mov r0, r2 + bx lr + + /* Entry point of libboron.so. */ + .globl OSDLLEntry + .extern OSDLLRelocateSelf + .type OSDLLEntry, %function +OSDLLEntry: + bl OSDLLRelocateSelf + #else #error Implement this TODO diff --git a/user/libboron/source/reloc.c b/user/libboron/source/reloc.c index f373dc0b..40b3be0f 100644 --- a/user/libboron/source/reloc.c +++ b/user/libboron/source/reloc.c @@ -2,7 +2,11 @@ #include #include +#if defined TARGET_I386 || defined TARGET_AMD64 #define BUG_UNREACHABLE() __asm__ volatile("ud2":::"memory"); +#else +#define BUG_UNREACHABLE() __builtin_unreachable() +#endif #ifdef TARGET_I386 @@ -99,6 +103,8 @@ void OSDLLRelocateSelf(PPEB Peb) if (RelType != R_X86_64_RELATIVE) #elif defined TARGET_I386 if (RelType != R_386_RELATIVE) + #elif defined TARGET_ARM + if (RelType != R_ARM_RELATIVE) #else #error TODO! #endif @@ -126,8 +132,8 @@ void OSDLLRelocateSelf(PPEB Peb) if (RelType != R_X86_64_RELATIVE) #elif defined TARGET_I386 if (RelType != R_386_RELATIVE) - #else - #error TODO! + #elif defined TARGET_ARM + if (RelType != R_ARM_RELATIVE) #endif // Libboron.so, in addition to being restricted in so many // different ways already, also cannot import things from