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..1594cc2c 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 = 1 +else ifeq ($(TARGET),I386) + GENERATE_SYMBOLS = 1 +else + GENERATE_SYMBOLS = 0 +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,6 +183,8 @@ 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),1) + $(KERNEL): $(SYMBOLS) @echo "[LD]\tBuilding $(KERNEL)" @$(BLD) $(OBJ) $(SYMBOLS) $(LDFLAGS) -o $@ @@ -193,6 +203,14 @@ $(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..70cdff16 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,34 +19,9 @@ 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); diff --git a/boron/include/arch/amd64.h b/boron/include/arch/amd64.h index 0653a0b9..5dd91679 100644 --- a/boron/include/arch/amd64.h +++ b/boron/include/arch/amd64.h @@ -71,18 +71,17 @@ MMADDRESS_CONVERT; #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 @@ -90,6 +89,10 @@ MMADDRESS_CONVERT; // 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 @@ -112,6 +115,7 @@ MMADDRESS_CONVERT; #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) // Page fault reasons @@ -294,4 +298,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..2b719906 --- /dev/null +++ b/boron/include/arch/arm.h @@ -0,0 +1,313 @@ +#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_PTEL1_TYPE (3U << 0) +#define MM_PTEL1_COARSE_PAGE_TABLE (1U << 0) // Type = b01, Coarse Page Table +#define MM_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_PTEL2_B (1 << 2) +#define MM_PTEL2_C (1 << 3) + +#define MM_PTEL2_AP_NOACCESS ((0U << 4)) // super N/A, user N/A +#define MM_PTEL2_AP_SUPERREADWRITE ((1U << 4)) // super R/W, user N/A +#define MM_PTEL2_AP_USERREADONLY ((2U << 4)) // super R/W, user R/O +#define MM_PTEL2_AP_USERREADWRITE ((3U << 4)) // super R/W, user R/W + +#define MM_PTEL2_AP_ALL(x) ((x) | ((x) << 2) | ((x) << 4) | ((x) << 6)) +#define MM_PTEL2_AP_ALL_NOACCESS (0) +#define MM_PTEL2_AP_ALL_SUPERREADWRITE MM_PTEL2_AP_ALL(MM_PTEL2_AP_SUPERREADWRITE) +#define MM_PTEL2_AP_ALL_USERREADONLY MM_PTEL2_AP_ALL(MM_PTEL2_AP_USERREADONLY) +#define MM_PTEL2_AP_ALL_USERREADWRITE MM_PTEL2_AP_ALL(MM_PTEL2_AP_USERREADWRITE) + +#define MM_PTEL2_TYPE_TRANSFAULT (0U << 0) +#define MM_PTEL2_TYPE_LARGEPAGE (1U << 0) +#define MM_PTEL2_TYPE_SMALLPAGE (2U << 0) +#define MM_PTEL2_TYPE_EXSMALLPAGE (3U << 0) + +#define MM_PTE_PRESENT (MM_PTEL2_TYPE_SMALLPAGE)// | MM_PTEL2_B | MM_PTEL2_C) +#define MM_PTE_READWRITE (MM_PTEL2_AP_ALL_SUPERREADWRITE) +#define MM_PTE_USERACCESS (MM_PTEL2_AP_ALL_USERREADONLY) +#define MM_PTE_NOEXEC (0) +#define MM_PTE_ISPRESENT(Pte) (((Pte) & 0x3) != 0) + +#else // ARMv6 + +// AP = PTE[5:4], APX = PTE[9] +#define MM_PTEL2_AP_NOACCESS ((0U << 4)) // super N/A, user N/A +#define MM_PTEL2_AP_SUPERREADWRITE ((1U << 4)) // super R/W, user N/A +#define MM_PTEL2_AP_USERREADONLY ((2U << 4)) // super R/W, user R/O +#define MM_PTEL2_AP_USERREADWRITE ((3U << 4)) // super R/W, user R/W +#define MM_PTEL2_AP_SUPERREADONLY ((1U << 4) | (1U << 9)) // super R/O, user N/A +#define MM_PTEL2_AP_BOTHREADONLY ((3U << 4) | (1U << 9)) // super R/O, user R/O + +// TEX = PTE[8:6], C = PTE[3], B = PTE[2] +#define MM_PTEL2_TEXCB_STRONGORDER ((0U << 6) | (0U << 2)) +#define MM_PTEL2_TEXCB_SHAREDDEVICE ((0U << 6) | (1U << 2)) +#define MM_PTEL2_TEXCB_CACHEABLE ((7U << 6) | (3U << 2)) // b11 inner and outer policy. +#define MM_PTEL2_TEXCB_NORMALMEM ((1U << 6) | (3U << 2)) // TEX=0b001, CB=0b11 + +// Type +#define MM_PTEL2_TYPE_TRANSFAULT (0U << 0) +#define MM_PTEL2_TYPE_LARGEPAGE (1U << 0) +#define MM_PTEL2_TYPE_SMALLPAGE (2U << 0) +#define MM_PTEL2_TYPE_SMALLPAGENX (3U << 0) + +#define MM_PTE_PRESENT (MM_PTEL2_TYPE_SMALLPAGE | MM_PTEL2_TEXCB_NORMALMEM) +#define MM_PTE_READWRITE MM_PTEL2_AP_SUPERREADWRITE +#define MM_PTE_USERACCESS MM_PTEL2_AP_USERREADONLY // USERREADONLY | SUPERREADWRITE = USERREADWRITE +#define MM_PTE_NOEXEC MM_PTEL2_TYPE_LARGEPAGE // LARGEPAGE | SMALLPAGE = SMALLPAGENX +#define MM_PTE_ISPRESENT(Pte) (((Pte) & 0x3) != 0) + +#endif + +#define MM_PTE_WRITETHRU (0) // seemingly unused +#define MM_PTE_NOCACHE (0) // TODO: supported by hardware, but to use it you must *turn off* bits +#define MM_PTE_ACCESSED (0) // TODO: supported by hardware (through an exception), but ForceAP must be set and we kind of rely on permission bits +#define MM_PTE_DIRTY (0) // TODO: unused +#define MM_PTE_GLOBAL (0) // TODO: unsupported? +#define MM_PTE_ISFROMPMM (0) // there are no software usable free bits on ARM :( +#define MM_PTE_COW (0) // unused +#define MM_PTE_TRANSITION (0) // unused +#define MM_PTE_PKMASK (0) // no such thing on 32-bit + +// 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_DPTE_ISPOOLHDR (1U << 2) +#define MM_DPTE_COMMITTED (1U << 3) +#define MM_DPTE_WASPRESENT (1U << 6) +#define MM_DPTE_DECOMMITTED (1U << 7) + +// TODO: just assume all of them are from PMM +#define MM_PTE_CHECKFROMPMM(Pte) (true) + +#define MM_PTE_PFN(Pte) (((Pte) >> 12) & 0xFFFFF) +#define MM_PTE_NEWPFN(Pfn) ((Pfn) << 12) +#define MM_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 + +// 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, *PMMPTE; + +#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 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 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" + ); +} + +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 aa8c2c6c..b8b7735b 100644 --- a/boron/include/arch/i386.h +++ b/boron/include/arch/i386.h @@ -81,14 +81,14 @@ MMADDRESS_CONVERT; #define MM_PTE_READWRITE (1 << 1) #define MM_PTE_USERACCESS (1 << 2) #define MM_PTE_WRITETHRU (1 << 3) -#define MM_PTE_CDISABLE (1 << 4) +#define MM_PTE_NOCACHE (1 << 4) #define MM_PTE_ACCESSED (1 << 5) #define MM_PTE_DIRTY (1 << 6) #define MM_PTE_PAT (1 << 7) #define MM_PTE_PAGESIZE (1 << 7) // for 4MB pages. not used by the kernel #define MM_PTE_GLOBAL (1 << 8) // doesn't invalidate the pages from the TLB when CR3 is changed #define MM_PTE_ISFROMPMM (1 << 9) // if the allocated memory is managed by the PFN database -#define MM_PTE_COW (1 << 10) // if this page is to be copied after a write +#define MM_PTE_COW (1 << 10) // if this page is to be copied after a write (UNUSED) #define MM_PTE_NOEXEC (0) // no such thing on 32-bit (without PAE) #define MM_PTE_PKMASK (0) // no such thing on 32-bit @@ -96,6 +96,10 @@ MMADDRESS_CONVERT; #define MM_PTE_ADDRESSMASK (0xFFFFF000U) #define MM_PTE_PFN(Pte) (((Pte) & MM_PTE_ADDRESSMASK) / PAGE_SIZE) +#define MM_PTE_NEWPFN(Pfn) ((Pfn) << 12) + +#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 - Permission bits as usual @@ -119,8 +123,7 @@ MMADDRESS_CONVERT; #define MM_DPTE_COMMITTED (1 << 8) #define MM_DPTE_BACKEDBYFILE (1 << 9) #define MM_DPTE_SWAPPED (1 << 10) - -#define MM_PTE_ISPOOLHDR (1 << 11) // see src/mm/poolsup.c for an explanation +#define MM_DPTE_ISPOOLHDR (1 << 11) // see src/mm/poolsup.c for an explanation // Page fault reasons #define MM_FAULT_PROTECTION (1 << 0) // 0: Page wasn't marked present; 1: Page protection violation (e.g. writing to a readonly page) @@ -318,3 +321,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..8f11a5d5 100644 --- a/boron/include/hal.h +++ b/boron/include/hal.h @@ -35,6 +35,12 @@ 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 HalRegisterInterruptHandler(int Irq, void(*Func)()); +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..00aed1d1 100644 --- a/boron/include/hal/init.h +++ b/boron/include/hal/init.h @@ -44,6 +44,13 @@ 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_REGISTER_INTERRUPT_HANDLER)(int, void(*)()); +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 +94,12 @@ 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_REGISTER_INTERRUPT_HANDLER RegisterInterruptHandler; + 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/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/thread.h b/boron/include/ke/thread.h index 08af9ea7..7822a6a9 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(); @@ -204,9 +228,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 53459c8a..d73ba15e 100644 --- a/boron/include/mm/pmm.h +++ b/boron/include/mm/pmm.h @@ -123,4 +123,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/linker.arm.ld b/boron/linker.arm.ld new file mode 100644 index 00000000..a28a9f08 --- /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 */ + . = 0x00000000; + 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/source/build_number b/boron/source/build_number index 64975abe..7a1ed495 100644 --- a/boron/source/build_number +++ b/boron/source/build_number @@ -1 +1 @@ -1126 +1385 diff --git a/boron/source/hal/hal.c b/boron/source/hal/hal.c index 32dbf944..1d7389ed 100644 --- a/boron/source/hal/hal.c +++ b/boron/source/hal/hal.c @@ -197,6 +197,31 @@ uintptr_t HalPciReadBarAddress(PPCI_ADDRESS Address, int BarIndex) } #endif +#ifdef TARGET_ARM + +int HalGetMaximumInterruptCount() +{ + return HalpVftable.GetMaximumInterruptCount(); +} + +// NOTE: This is low level and should not be used directly! +void HalRegisterInterruptHandler(int Irq, void(*Func)()) +{ + return HalpVftable.RegisterInterruptHandler(Irq, Func); +} + +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/hal_versatilepb/clcd.c b/boron/source/hal_versatilepb/clcd.c new file mode 100644 index 00000000..55bbe9f0 --- /dev/null +++ b/boron/source/hal_versatilepb/clcd.c @@ -0,0 +1,144 @@ +/*** + The Boron Operating System + Copyright (C) 2026 iProgramInCpp + +Module name: + ha/clcd.c + +Abstract: + This module implements CLCD framebuffer support. + +Author: + iProgramInCpp - 31 January 2026 +***/ +#include +#include +#include + +#ifdef TARGET_ARM + +#define PL110_BASE (0x10120000) + +typedef volatile struct +{ + /* 0x00 */ uint32_t LcdTiming0; + /* 0x04 */ uint32_t LcdTiming1; + /* 0x08 */ uint32_t LcdTiming2; + /* 0x0C */ uint32_t LcdTiming3; // 17 bit + /* 0x10 */ uint32_t LcdUPBase; + /* 0x14 */ uint32_t LcdLPBase; + /* 0x18 */ uint32_t LcdControl; // 16 bit + /* 0x1C */ uint32_t LcdImsc; // 5 bit + /* 0x20 */ uint32_t LcdRis; // 5 bit + /* 0x24 */ uint32_t LcdMis; // 5 bit + /* 0x28 */ uint32_t LcdIcr; // 5 bit + /* 0x2C */ uint32_t LcdUPCurr; + /* 0x30 */ uint32_t LcdLPCurr; + /* 0x034 - 0x1FC */ uint32_t Reserved[0x73]; + /* 0x200 - 0x3FC */ uint32_t LcdPalette[0x80]; + /* 0x400 - 0xFDC */ uint32_t Reserved2[0x2F8]; + /* 0xFE0 */ uint32_t ClcdPeriphId0; + /* 0xFE4 */ uint32_t ClcdPeriphId1; + /* 0xFE8 */ uint32_t ClcdPeriphId2; + /* 0xFEC */ uint32_t ClcdPeriphId3; + /* 0xFF0 */ uint32_t ClcdPCellId0; + /* 0xFF4 */ uint32_t ClcdPCellId1; + /* 0xFF8 */ uint32_t ClcdPCellId2; + /* 0xFFC */ uint32_t ClcdPCellId3; +} +PL111_MMIO, *PPL111_MMIO; + +static_assert(sizeof(PL111_MMIO) == 4096); + +static volatile PPL111_MMIO HalPL111; + +#define SCREEN_WIDTH 640 +#define SCREEN_HEIGHT 480 + +#define HORIZONTAL_BACK_PORCH (63) +#define HORIZONTAL_FRONT_PORCH (31) +#define HORIZONTAL_PULSE_WIDTH (95) +#define VERTICAL_BACK_PORCH (8) +#define VERTICAL_FRONT_PORCH (11) +#define VERTICAL_PULSE_WIDTH (24) + +#define TIM0_PPL(pixelsPerLine) ((((pixelsPerLine) / 16) - 1) << 2) +#define TIM0_HBP(backPorch) ((backPorch) << 24) +#define TIM0_HFP(frontPorch) ((frontPorch) << 16) +#define TIM0_HSW(pulseWidth) ((pulseWidth) << 8) + +#define TIM1_VBP(backPorch) ((backPorch) << 24) +#define TIM1_VFP(frontPorch) ((frontPorch) << 16) +#define TIM1_VSW(pulseWidth) ((pulseWidth) << 10) +#define TIM1_LPP(linesPerPanel) ((linesPerPanel) - 1) + +#define TIM2_PCD_HI(pcd) ((((pcd) >> 5) & 0x1F) << 27) +#define TIM2_PCD_LO(pcd) ((pcd) & 0x1F) +#define TIM2_PCD(pcd) TIM2_PCD_HI(pcd) | TIM2_PCD_LO(pcd) +#define TIM2_BCD (1 << 26) +#define TIM2_CPL(cpl) ((cpl) << 16) +#define TIM2_IEO (1 << 14) +#define TIM2_IPC (1 << 13) +#define TIM2_IHS (1 << 12) +#define TIM2_IVS (1 << 11) +#define TIM2_ACB(acb) ((acb) << 6) +#define TIM2_CLKSEL (1 << 5) + +#define CTL_LCDEN (1 << 0) +#define CTL_16BPP (0b100 << 1) +#define CTL_24BPP (0b101 << 1) +#define CTL_LCDBW (1 << 4) +#define CTL_LCDTFT (1 << 5) +#define CTL_LCDM8 (1 << 6) +#define CTL_LCDDUAL (1 << 7) +#define CTL_LCDBGR (1 << 8) +#define CTL_LCDBEBO (1 << 9) // big endian byte order +#define CTL_LCDBEPO (1 << 10) // big endian pixel order +#define CTL_LCDPWR (1 << 11) + +// TODO: make this more dynamic and everything... +static uint16_t HalFramebuffer[SCREEN_WIDTH * SCREEN_HEIGHT]; + +void HalVpbInitCLCD() +{ + HalPL111 = MmMapIoSpace(PL110_BASE, 4096, MM_PTE_READWRITE, POOL_TAG("CLCD")); + if (!HalPL111) + KeCrash("ERROR: Cannot map CLCD"); + + HalPL111->LcdControl = 0; + + // set up a 640x480x16 bpp framebuffer + HalPL111->LcdTiming0 = TIM0_PPL(SCREEN_WIDTH) | TIM0_HBP(HORIZONTAL_BACK_PORCH) | TIM0_HFP(HORIZONTAL_FRONT_PORCH) | TIM0_HSW(HORIZONTAL_PULSE_WIDTH); + HalPL111->LcdTiming1 = TIM1_LPP(SCREEN_HEIGHT) | TIM1_VBP(VERTICAL_BACK_PORCH) | TIM1_VFP(VERTICAL_FRONT_PORCH) | TIM1_VSW(VERTICAL_PULSE_WIDTH); + HalPL111->LcdTiming2 = TIM2_PCD(0) | TIM2_BCD | TIM2_IEO; // TODO: PCD ignored in QEMU, not on an actual board. + HalPL111->LcdTiming3 = 0; + + // TODO: dynamically allocate frame buffer. + uintptr_t HalFramebufferPhys = ((uintptr_t) HalFramebuffer - 0xC0000000); + HalPL111->LcdUPBase = HalFramebufferPhys; + HalPL111->LcdLPBase = HalFramebufferPhys; + + HalPL111->LcdControl = CTL_LCDEN | CTL_16BPP | CTL_LCDTFT | CTL_LCDPWR; + + // Modify the LPB to register this frame buffer. It should work, right? + PLOADER_PARAMETER_BLOCK Lpb = &KeLoaderParameterBlock; + int FramebufferIndex = Lpb->FramebufferCount++; + + PLOADER_FRAMEBUFFER FrameBufferObject = &Lpb->Framebuffers[FramebufferIndex]; + FrameBufferObject->Address = (void*) HalFramebuffer; + FrameBufferObject->Pitch = SCREEN_WIDTH * sizeof(uint16_t); + FrameBufferObject->Width = SCREEN_WIDTH; + FrameBufferObject->Height = SCREEN_HEIGHT; + FrameBufferObject->IsPhysicalAddress = false; + FrameBufferObject->BitDepth = 16; + FrameBufferObject->RedMaskSize = 5; + FrameBufferObject->GreenMaskSize = 6; + FrameBufferObject->BlueMaskSize = 5; + FrameBufferObject->RedMaskShift = 0; + FrameBufferObject->GreenMaskShift = 5; + FrameBufferObject->BlueMaskShift = 11; + + DbgPrint("CLCD initialized."); +} + +#endif \ No newline at end of file diff --git a/boron/source/hal_versatilepb/crash.c b/boron/source/hal_versatilepb/crash.c new file mode 100644 index 00000000..7abf15c1 --- /dev/null +++ b/boron/source/hal_versatilepb/crash.c @@ -0,0 +1,32 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + ha/timer.c + +Abstract: + This module implements the HAL-specific portions of the + crash process. + +Author: + iProgramInCpp - 31 December 2025 +***/ +#ifdef TARGET_ARM +#include +#include "hali.h" + +NO_RETURN +void HalVpbCrashSystem(const char* Message) +{ + DISABLE_INTERRUPTS(); + KeCrashConclusion(Message); +} + +NO_RETURN +void HalVpbProcessorCrashed() +{ + KeStopCurrentCPU(); +} + +#endif diff --git a/boron/source/hal_versatilepb/flanterm b/boron/source/hal_versatilepb/flanterm new file mode 120000 index 00000000..3436682c --- /dev/null +++ b/boron/source/hal_versatilepb/flanterm @@ -0,0 +1 @@ +../../../external/flanterm \ No newline at end of file diff --git a/boron/source/hal_versatilepb/flanterm_alt_fb.c b/boron/source/hal_versatilepb/flanterm_alt_fb.c new file mode 120000 index 00000000..0dd63727 --- /dev/null +++ b/boron/source/hal_versatilepb/flanterm_alt_fb.c @@ -0,0 +1 @@ +../../../external/flanterm_alt_fb.c \ No newline at end of file diff --git a/boron/source/hal_versatilepb/flanterm_alt_fb.h b/boron/source/hal_versatilepb/flanterm_alt_fb.h new file mode 120000 index 00000000..06b1592a --- /dev/null +++ b/boron/source/hal_versatilepb/flanterm_alt_fb.h @@ -0,0 +1 @@ +../../../external/flanterm_alt_fb.h \ No newline at end of file diff --git a/boron/source/hal_versatilepb/font.h b/boron/source/hal_versatilepb/font.h new file mode 120000 index 00000000..5caa9ffe --- /dev/null +++ b/boron/source/hal_versatilepb/font.h @@ -0,0 +1 @@ +../../../external/tamsyn_font.h \ No newline at end of file diff --git a/boron/source/hal_versatilepb/hali.h b/boron/source/hal_versatilepb/hali.h new file mode 100644 index 00000000..49c91221 --- /dev/null +++ b/boron/source/hal_versatilepb/hali.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +#define HAL_API // specify calling convention here if needed diff --git a/boron/source/hal_versatilepb/init.c b/boron/source/hal_versatilepb/init.c new file mode 100644 index 00000000..ddf10239 --- /dev/null +++ b/boron/source/hal_versatilepb/init.c @@ -0,0 +1,93 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + ha/init.c + +Abstract: + This module contains the two initialization functions + (UP-init and MP-init) + +Author: + iProgramInCpp - 31 December 2025 +***/ +#ifdef TARGET_ARM +#define IS_HAL +#include +#include +#include "hali.h" + +void HalVpbEndOfInterrupt(int InterruptNumber); +void HalVpbRequestIpi(uint32_t LapicId, uint32_t Flags, int Vector); +void HalVpbInitSystemUP(); +void HalVpbInitSystemMP(); +void HalVpbDisplayString(const char* Message); +void HalVpbCrashSystem(const char* Message) NO_RETURN; +bool HalVpbUseOneShotIntTimer(); +void HalVpbProcessorCrashed() NO_RETURN; +uint64_t HalVpbGetIntTimerFrequency(); +uint64_t HalVpbGetTickCount(); +uint64_t HalVpbGetTickFrequency(); +uint64_t HalVpbGetIntTimerDeltaTicks(); +int HalVpbGetMaximumInterruptCount(); +void HalVpbRegisterInterruptHandler(int Irq, void(*Func)()); +PKREGISTERS HalVpbOnInterruptRequest(PKREGISTERS Registers); +PKREGISTERS HalVpbOnFastInterruptRequest(PKREGISTERS Registers); +bool HalVpbUseOneShotTimer(); +void HalVpbRequestInterruptInTicks(uint64_t ticks); +uint64_t HalVpbGetInterruptDeltaTime(); +void HalVpbInitCLCD(); +void HalVpbInitTerminal(); +void HalInitPL190(); +void HalInitTimer(); + +// Initialize the HAL on the BSP, for all processors. +HAL_API void HalVpbInitSystemUP() +{ + HalVpbInitCLCD(); + HalVpbInitTerminal(); + HalInitPL190(); +} + +// Initialize the HAL separately for each processor. +// This function is run on ALL processors. +HAL_API void HalVpbInitSystemMP() +{ + HalInitTimer(); +} + +static const HAL_VFTABLE HalpVfTable = +{ + .EndOfInterrupt = HalVpbEndOfInterrupt, + .RequestInterruptInTicks = HalVpbRequestInterruptInTicks, + .RequestIpi = HalVpbRequestIpi, + .InitSystemUP = HalVpbInitSystemUP, + .InitSystemMP = HalVpbInitSystemMP, + .DisplayString = HalVpbDisplayString, + .CrashSystem = HalVpbCrashSystem, + .ProcessorCrashed = HalVpbProcessorCrashed, + .UseOneShotIntTimer = HalVpbUseOneShotIntTimer, + .GetIntTimerFrequency = HalVpbGetIntTimerFrequency, + .GetTickCount = HalVpbGetTickCount, + .GetTickFrequency = HalVpbGetTickFrequency, + .GetIntTimerDeltaTicks = HalVpbGetIntTimerDeltaTicks, + .GetMaximumInterruptCount = HalVpbGetMaximumInterruptCount, + .RegisterInterruptHandler = HalVpbRegisterInterruptHandler, + .OnInterruptRequest = HalVpbOnInterruptRequest, + .OnFastInterruptRequest = HalVpbOnFastInterruptRequest, + .Flags = HAL_VFTABLE_LOADED, +}; + +BSTATUS HAL_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; +} + +#endif diff --git a/boron/source/hal_versatilepb/irq.S b/boron/source/hal_versatilepb/irq.S new file mode 100644 index 00000000..63f8a956 --- /dev/null +++ b/boron/source/hal_versatilepb/irq.S @@ -0,0 +1,99 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + ha/irq.S + +Abstract: + This module contains the IRQ handlers. + +Author: + iProgramInCpp - 31 December 2025 +***/ +#ifdef TARGET_ARM + .section .text + .extern KeDispatchInterruptRequest + +.macro IRQ number + .global HalIrq\number + .type HalIrq\number, %function +HalIrq\number: + mov r1, r0 + mov r0, #\number + b KeDispatchInterruptRequest +.endm + + IRQ 0 + IRQ 1 + IRQ 2 + IRQ 3 + IRQ 4 + IRQ 5 + IRQ 6 + IRQ 7 + IRQ 8 + IRQ 9 + IRQ 10 + IRQ 11 + IRQ 12 + IRQ 13 + IRQ 14 + IRQ 15 + IRQ 16 + IRQ 17 + IRQ 18 + IRQ 19 + IRQ 20 + IRQ 21 + IRQ 22 + IRQ 23 + IRQ 24 + IRQ 25 + IRQ 26 + IRQ 27 + IRQ 28 + IRQ 29 + IRQ 30 + IRQ 31 + +.macro IRQD number + .word HalIrq\number +.endm + + .section .data + .global HalIrqList +HalIrqList: + IRQD 0 + IRQD 1 + IRQD 2 + IRQD 3 + IRQD 4 + IRQD 5 + IRQD 6 + IRQD 7 + IRQD 8 + IRQD 9 + IRQD 10 + IRQD 11 + IRQD 12 + IRQD 13 + IRQD 14 + IRQD 15 + IRQD 16 + IRQD 17 + IRQD 18 + IRQD 19 + IRQD 20 + IRQD 21 + IRQD 22 + IRQD 23 + IRQD 24 + IRQD 25 + IRQD 26 + IRQD 27 + IRQD 28 + IRQD 29 + IRQD 30 + IRQD 31 +#endif diff --git a/boron/source/hal_versatilepb/term.c b/boron/source/hal_versatilepb/term.c new file mode 100644 index 00000000..de9aaef2 --- /dev/null +++ b/boron/source/hal_versatilepb/term.c @@ -0,0 +1,148 @@ +/*** + The Boron Operating System + Copyright (C) 2026 iProgramInCpp + +Module name: + ha/term.c + +Abstract: + This module implements the terminal functions for the versatilepb + platform. + +Author: + iProgramInCpp - 1 February 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) +{ +} + +void HalVpbInitTerminal() +{ + 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; + 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; + + if (Framebuffer->IsPhysicalAddress) + { + FramebufferMemory = MmMapIoSpace( + (uintptr_t)Framebuffer->Address, + Framebuffer->Pitch * Framebuffer->Height, + MM_PTE_READWRITE | MM_PTE_NOCACHE, + POOL_TAG("HAFB") + ); + } + else + { + FramebufferMemory = Framebuffer->Address; + } + + 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 HalVpbDisplayString(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/boron/source/hal_versatilepb/timer.c b/boron/source/hal_versatilepb/timer.c new file mode 100644 index 00000000..5549e1f7 --- /dev/null +++ b/boron/source/hal_versatilepb/timer.c @@ -0,0 +1,65 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + ha/timer.c + +Abstract: + This module contains the driver for the timer. + +Author: + iProgramInCpp - 31 December 2025 +***/ +#ifdef TARGET_ARM +#include +#include "hali.h" + +bool HalVpbUseOneShotIntTimer() +{ + DbgPrint("TODO %s", __func__); + return false; +} + +bool HalVpbUseOneShotTimer() +{ + DbgPrint("TODO %s", __func__); + return false; +} + +uint64_t HalVpbGetIntTimerFrequency() +{ + DbgPrint("TODO %s", __func__); + return 1; +} + +uint64_t HalVpbGetTickCount() +{ + DbgPrint("TODO %s", __func__); + return 1; +} + +uint64_t HalVpbGetTickFrequency() +{ + DbgPrint("TODO %s", __func__); + return 1; +} + +uint64_t HalVpbGetIntTimerDeltaTicks() +{ + DbgPrint("TODO %s", __func__); + return 1; +} + +uint64_t HalVpbGetInterruptDeltaTime() +{ + DbgPrint("TODO %s", __func__); + return 1; +} + +void HalVpbRequestInterruptInTicks(uint64_t ticks) +{ + DbgPrint("TODO %s", __func__); +} + +#endif diff --git a/boron/source/hal_versatilepb/vic.c b/boron/source/hal_versatilepb/vic.c new file mode 100644 index 00000000..c36e1d7f --- /dev/null +++ b/boron/source/hal_versatilepb/vic.c @@ -0,0 +1,89 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + ha/gic.c + +Abstract: + This module contains the driver for the VIC (PL190). + +Author: + iProgramInCpp - 31 December 2025 +***/ +#ifdef TARGET_ARM +#include +#include "hali.h" + +#define VIC_BASE ((volatile unsigned int*) 0xD1840000) +#define VIC_IRQ_STATUS (VIC_BASE[0]) +#define VIC_FIQ_STATUS (VIC_BASE[1]) +#define VIC_RAWINT_STA (VIC_BASE[2]) +#define VIC_INT_SELECT (VIC_BASE[3]) +#define VIC_INT_ENABLE (VIC_BASE[4]) +#define VIC_INT_ENCLR (VIC_BASE[5]) +#define VIC_SWI (VIC_BASE[6]) +#define VIC_SWI_CLEAR (VIC_BASE[7]) +#define VIC_PROT (VIC_BASE[8]) +#define VIC_VECT_ADDR (VIC_BASE[12]) +#define VIC_DEFVECTADD (VIC_BASE[13]) +#define VIC_VECT_ADDRS (&VIC_BASE[64]) +#define VIC_VECT_CNTLS (&VIC_BASE[128]) +#define VIC_ITCR (VIC_BASE[192]) +#define VIC_ITIP1 (VIC_BASE[193]) +#define VIC_ITIP2 (VIC_BASE[194]) +#define VIC_ITOP1 (VIC_BASE[195]) +#define VIC_ITOP2 (VIC_BASE[196]) +#define VIC_PERIPHID0 (VIC_BASE[1016]) +#define VIC_PERIPHID1 (VIC_BASE[1017]) +#define VIC_PERIPHID2 (VIC_BASE[1018]) +#define VIC_PERIPHID3 (VIC_BASE[1019]) +#define VIC_CELLID0 (VIC_BASE[1020]) +#define VIC_CELLID1 (VIC_BASE[1021]) +#define VIC_CELLID2 (VIC_BASE[1022]) +#define VIC_CELLID3 (VIC_BASE[1023]) + +void HalVpbEndOfInterrupt(int InterruptNumber) +{ + DbgPrint("TODO %s", __func__); +} + +void HalVpbRequestIpi(uint32_t LapicId, uint32_t Flags, int Vector) +{ + DbgPrint("TODO %s", __func__); +} + +void HalVpbRegisterInterruptHandler(int Irq, void(*Func)()) +{ + DbgPrint("TODO %s", __func__); +} + +PKREGISTERS HalVpbOnInterruptRequest(PKREGISTERS Registers) +{ + DbgPrint("TODO %s", __func__); + return Registers; +} + +PKREGISTERS HalVpbOnFastInterruptRequest(PKREGISTERS Registers) +{ + DbgPrint("TODO %s", __func__); + return Registers; +} + +int HalVpbGetMaximumInterruptCount() +{ + DbgPrint("TODO %s", __func__); + return 1; +} + +void HalInitPL190() +{ + DbgPrint("TODO %s", __func__); +} + +void HalInitTimer() +{ + DbgPrint("TODO %s", __func__); +} + +#endif 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..20147024 --- /dev/null +++ b/boron/source/ke/arm/boot.c @@ -0,0 +1,90 @@ +/*** + 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" + +#define MAX_FRAMEBUFFER_COUNT 1 + +// TODO: a lot of stuff is going to be hardcoded for a start. +// hardcode properties HERE +#define MEMORY_START_ADDRESS (0x00000000) +#define MEMORY_SIZE (128*1024*1024) +static const char* KiKernelBootCmdLine = "Root=\"/InitRoot\" Init=\"/bin/Test\" NoInit=yes"; + +// end of hardcoded properties. + +INIT void KeMarkCrashedAp(UNUSED uint32_t ProcessorIndex) {} +INIT void KeJumpstartAp(UNUSED uint32_t ProcessorIndex) {} + +extern char KiKernelStart[], KiKernelEnd[], KiMemoryStart[]; + +LOADER_PARAMETER_BLOCK KeLoaderParameterBlock; + +static LOADER_MEMORY_REGION KiMemoryRegions[2]; +static LOADER_AP KiLoaderAp; +static void* KiLoaderApDummy; + +static LOADER_FRAMEBUFFER KiFramebufferInfo[MAX_FRAMEBUFFER_COUNT]; + +void MiInitializeBaseIdentityMapping(); + +INIT +void KiInitLoaderParameterBlock() +{ + MiInitializeBaseIdentityMapping(); + + PLOADER_MEMORY_REGION KernelRegion, FreeRegion; + KernelRegion = &KiMemoryRegions[0]; + FreeRegion = &KiMemoryRegions[1]; + + KernelRegion->Base = MEMORY_START_ADDRESS; + KernelRegion->Size = (KiKernelEnd - KiKernelStart + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); + KernelRegion->Type = LOADER_MEM_LOADED_PROGRAM; + + FreeRegion->Base = MEMORY_START_ADDRESS + KernelRegion->Size; + FreeRegion->Size = MEMORY_SIZE - KernelRegion->Size; + FreeRegion->Type = LOADER_MEM_FREE; + + KiLoaderAp.ProcessorId = 1; + KiLoaderAp.HardwareId = 1; + KiLoaderAp.TrampolineJumpAddress = &KiLoaderApDummy; + KiLoaderAp.ExtraArgument = NULL; + + PLOADER_PARAMETER_BLOCK Lpb = &KeLoaderParameterBlock; + + Lpb->MemoryRegions = KiMemoryRegions; + Lpb->MemoryRegionCount = 2; + + Lpb->Framebuffers = KiFramebufferInfo; + Lpb->FramebufferCount = 0; + + PLOADER_MODULE KernelModule = &Lpb->ModuleInfo.Kernel; + KernelModule->Path = "kernel.elf"; + KernelModule->String = ""; + KernelModule->Address = KiKernelStart - 0xC0000000; + KernelModule->Size = KiKernelEnd - KiKernelStart; + + Lpb->ModuleInfo.List = NULL; + Lpb->ModuleInfo.Count = 0; + + Lpb->Multiprocessor.List = &KiLoaderAp; + Lpb->Multiprocessor.Count = 1; + Lpb->Multiprocessor.BootstrapHardwareId = 1; + + Lpb->LoaderInfo.Name = "fake testing armv6 loader"; + Lpb->LoaderInfo.Version = "v1.0"; + + Lpb->CommandLine = KiKernelBootCmdLine; +} 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..03805461 --- /dev/null +++ b/boron/source/ke/arm/debug.c @@ -0,0 +1,88 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + ke/armv6/debug.c + +Abstract: + This module implements architecture specific debugging + routines. + +Author: + iProgramInCpp - 26 December 2025 +***/ +#include + +void DbgPrintDouble(const char* String) +{ + DbgPrintString(String); + HalDisplayString(String); +} + +#ifdef DEBUG + +#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 DbgPrintString(const char* str) +{ + while (*str) + { + if (*str == '\n') + DbgPrintChar('\r'); + DbgPrintChar(*str); + str++; + } +} + +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); +} + +void DbgPrintStackTrace(uintptr_t Rbp) +{ + (void) Rbp; + + DbgPrintDouble("TODO: DbgPrintStackTrace\n"); +} + +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..50897b7a --- /dev/null +++ b/boron/source/ke/arm/intobj.c @@ -0,0 +1,164 @@ +/*** + 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 may not be called", Vector); + + 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 + { + //HalPicRegisterInterrupt(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; + + 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); +} diff --git a/boron/source/ke/arm/misc.S b/boron/source/ke/arm/misc.S new file mode 100644 index 00000000..6b8b0a79 --- /dev/null +++ b/boron/source/ke/arm/misc.S @@ -0,0 +1,34 @@ +/*** + 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 + + @ void KeSetCurrentPageTable(HPAGEMAP PageMap); + .global KeSetCurrentPageTable +KeSetCurrentPageTable: + mcr p15, 0, r0, c2, c0, 0 @ Write TTBR0 + bx lr + + @ HPAGEMAP KeSetCurrentPageTable(); + .global KeGetCurrentPageTable +KeGetCurrentPageTable: + mrc p15, 0, r0, c2, c0, 0 @ Read TTBR0 + bx lr + + @ void KeFlushTLB(void); + .global KeFlushTLB +KeFlushTLB: + mcr p15, 0, r0, c8, c7, 0 @ Invalidate Unified TLB Register + bx lr 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..f92c40fc --- /dev/null +++ b/boron/source/ke/arm/probe2.S @@ -0,0 +1,59 @@ +/*** + 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: + 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 + bx lr + + @ Returns early from MmProbeAddressSub and MmSafeCopySub. + @ Called by the invalid page fault handler. + .global MmProbeAddressSubEarlyReturn +MmProbeAddressSubEarlyReturn: + bx lr diff --git a/boron/source/ke/arm/start.S b/boron/source/ke/arm/start.S new file mode 100644 index 00000000..d7192f63 --- /dev/null +++ b/boron/source/ke/arm/start.S @@ -0,0 +1,189 @@ +/*** + 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 = 0b111, AP = 0b01, P = 0, Domain = 0, CB = 0b11 */ +.equ L1PTE_FLAGS_SEC, 0b111010000001110 + +/* 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: + /* set up the root page table */ + ldr r2, =L1PTE_FLAGS_SEC + + /* map first 2MB 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] + + /* map first 2MB 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] + + /* 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] + + ldr r0, =KiRootPageTable + mcr p15, 0, r0, c2, c0, 0 /* write TTBR0 */ + + mov r0, #0x1 + mcr p15, 0, r0, c3, c0, 0 /* Domain Access Control: Domain 0 = Client */ + + mov r0, #0x0 /* clear TTBCR to always use TTBR0 */ + mcr p15, 0, r0, c2, c0, 2 + + 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 */ + 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 */ + + /* 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: diff --git a/boron/source/ke/arm/syscall.c b/boron/source/ke/arm/syscall.c new file mode 100644 index 00000000..4057754f --- /dev/null +++ b/boron/source/ke/arm/syscall.c @@ -0,0 +1,22 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + ke/armv6/syscall.c + +Abstract: + This module implements the system service dispatcher + for the armv6 architecture. + +Author: + iProgramInCpp - 30 December 2025 +***/ +#include + +void KiSystemServiceHandler(PKREGISTERS Regs) +{ + (void) Regs; + + KeCrash("NYI KiSystemServiceHandler"); +} 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..302041d9 --- /dev/null +++ b/boron/source/ke/arm/trap.S @@ -0,0 +1,256 @@ +/*** + The Boron Operating System + Copyright (C) 2025 iProgramInCpp + +Module name: + ke/armv6/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 i +#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 i +#endif +1: bx lr + + @ void KeOnUpdateIPL(KIPL OldIpl, KIPL NewIpl); + .global KeOnUpdateIPL +KeOnUpdateIPL: + bx lr + + @ NO_RETURN void KeDescendIntoUserMode( + @ void* InstructionPointer, + @ void* StackPointer, + @ void* UserContext, + @ uintptr_t ReturnCode + @ ); + .global KeDescendIntoUserMode + .extern KeCrash +KeDescendIntoUserMode: + @ TODO: actually implement this + ldr r0, =KeDescendIntoUserModeUnimplementedMessage + bl KeCrash + bx lr +KeDescendIntoUserModeUnimplementedMessage: + .asciz "NYI KeDescendIntoUserMode" + .align 4 + + @ 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 + bx r4 + + @ void KiSaveInterruptStacks( + @ uintptr_t* AbortStack, + @ uintptr_t* UndefinedStack, + @ uintptr_t* IrqStack, + @ uintptr_t* FiqStack + @ ); + .global KiSaveInterruptStacks +KiSaveInterruptStacks: + +#ifdef TARGET_ARMV5 + 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 + cpsid if + 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 + cpsie if +#endif + bx lr + + @ void KiRestoreInterruptStacks( + @ uintptr_t AbortStack, + @ uintptr_t UndefinedStack, + @ uintptr_t IrqStack, + @ uintptr_t FiqStack + @ ); + .global KiRestoreInterruptStacks +KiRestoreInterruptStacks: + +#ifdef TARGET_ARMV5 + 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 + cpsid if + 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 + cpsie if +#endif + bx lr + +#ifdef TARGET_ARMV5 + +.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 KiSoftwareInterruptHandler + .extern KiSystemServiceHandler +KiSoftwareInterruptHandler: + 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 + bl KiSystemServiceHandler @ handle the call here + pop {r0} + msr spsr, r0 @ restore old CPSR + pop {r0-r7,lr} @ restore GPRs and the LR + subs pc, lr, #4 @ and finally return 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..31640af4 --- /dev/null +++ b/boron/source/ke/arm/traps.c @@ -0,0 +1,52 @@ +/*** + 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 + +#define MAX_IRQS (32) // PL192 has 32 IRQs, PL190 has only 16 + +static KSPIN_LOCK KiTrapLock; + +extern void* const KiTrapList[]; +int8_t KiTrapIplList[MAX_IRQS]; +void* KiTrapCallList[MAX_IRQS]; + +void KeRegisterInterrupt(int Vector, PKINTERRUPT_HANDLER Handler) +{ + KIPL Ipl; + KeAcquireSpinLock(&KiTrapLock, &Ipl); + KiTrapCallList[Vector] = Handler; + KeReleaseSpinLock(&KiTrapLock, Ipl); +} + +void KeSetInterruptIPL(int Vector, KIPL Ipl) +{ + KiTrapIplList[Vector] = Ipl; +} + +void KiHandleInstructionFault(PKREGISTERS Registers) +{ + KeGetCurrentThread()->HandlingInstructionFault = true; + KeOnPageFault(Registers); +} + +void KiHandleDataFault(PKREGISTERS Registers) +{ + KeGetCurrentThread()->HandlingInstructionFault = false; + KeOnPageFault(Registers); +} diff --git a/boron/source/ke/crash.c b/boron/source/ke/crash.c index 761e3b65..fb51d18e 100644 --- a/boron/source/ke/crash.c +++ b/boron/source/ke/crash.c @@ -49,7 +49,13 @@ void KeCrashConclusion(const char* Message) KiPrintLock.Locked = 0; KiDebugPrintLock.Locked = 0; - 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/except.c b/boron/source/ke/except.c index 5be0f277..deb90afd 100644 --- a/boron/source/ke/except.c +++ b/boron/source/ke/except.c @@ -32,6 +32,21 @@ 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 (KeGetCurrentThread()->HandlingInstructionFault) { \ + FaultAddress = KiReadIfar(); \ + FaultMode = KiReadIfsr(); \ + } else { \ + FaultAddress = KiReadDfar(); \ + FaultMode = KiReadDfsr(); \ + } + #else #error Go implement KI_EXCEPTION_HANDLER_INIT! @@ -61,7 +76,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(); @@ -122,23 +143,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/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..f8ac249e 100644 --- a/boron/source/ke/ki.h +++ b/boron/source/ke/ki.h @@ -113,4 +113,19 @@ 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 +); +#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/sched.c b/boron/source/ke/sched.c index f5a6934f..a56ec410 100644 --- a/boron/source/ke/sched.c +++ b/boron/source/ke/sched.c @@ -731,6 +731,25 @@ 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) diff --git a/boron/source/ke/thread.c b/boron/source/ke/thread.c index 7531b2f0..d3ad5dd7 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); + KernelStackSize -= KERNEL_INTERRUPT_STACK_SIZE; + + Thread->InterruptStack = (void*)((uintptr_t) KernelStack + 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 f2133962..c50511e4 100644 --- a/boron/source/ldr/dll.c +++ b/boron/source/ldr/dll.c @@ -69,7 +69,7 @@ static void LdriMapInProgramHeader(PLOADER_MODULE File, PELF_PROGRAM_HEADER Phdr // Some entries overlap. Check if there's already a PTE beforehand. PMMPTE Pte = MmGetPteLocationCheck(VirtAddr, false); - if (Pte && (*Pte & MM_PTE_PRESENT)) + if (Pte && MM_PTE_ISPRESENT(*Pte)) { MMPTE OldPte = *Pte; @@ -343,7 +343,13 @@ static void LdrpInitializeDllByIndex(PLOADED_DLL Dll) INIT void LdrInitializeHal() { +#ifndef TARGET_ARM LdrpInitializeDllByIndex(&KeLoadedDLLs[0]); +#else + extern BSTATUS HAL_DriverEntry(PDRIVER_OBJECT Object); + DbgPrint("calling fake built-in HAL"); + HAL_DriverEntry(NULL); +#endif } INIT diff --git a/boron/source/ldr/loader.c b/boron/source/ldr/loader.c index d4b27663..1f505264 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 = "halarmqv.sys"; // TODO: make this configurable. for now, ARM QEMU virt machine. + #else #error Define your loader base and HAL path here. @@ -97,8 +102,10 @@ void LdrInit() if (!HalFile) { + /* KeCrashBeforeSMPInit("No HAL loaded"); return; + */ } LdriLoadFile(HalFile); diff --git a/boron/source/mm/amd64/pt.c b/boron/source/mm/amd64/pt.c index 26998ad6..b9968a8c 100644 --- a/boron/source/mm/amd64/pt.c +++ b/boron/source/mm/amd64/pt.c @@ -46,17 +46,17 @@ bool MmCheckPteLocation(uintptr_t Address, bool GenerateMissingLevels) PMMPTE Pte; Pte = MmGetPteLocation(MI_PTE_LOC(MI_PTE_LOC(MI_PTE_LOC(Address)))); - if (~(*Pte) & MM_PTE_PRESENT) + if (!MM_PTE_ISPRESENT(*Pte)) goto Missing; // PML4 exists, check PML3 Pte = MmGetPteLocation(MI_PTE_LOC(MI_PTE_LOC(Address))); - if (~(*Pte) & MM_PTE_PRESENT) + if (!MM_PTE_ISPRESENT(*Pte)) goto Missing; // PML3 exists, check PML2 Pte = MmGetPteLocation(MI_PTE_LOC(Address)); - if (~(*Pte) & MM_PTE_PRESENT) + if (!MM_PTE_ISPRESENT(*Pte)) { Missing: if (GenerateMissingLevels) @@ -117,6 +117,12 @@ HPAGEMAP MiCreatePageMapping() return (HPAGEMAP) NewPageMappingResult; } +// Frees a page mapping. +void MiFreePageMapping(HPAGEMAP PageMap) +{ + return MmFreePhysicalPage(MmPhysPageToPFN(PageMap)); +} + // TODO: this will most likely be rewritten bool MmpCloneUserHalfLevel(int Level, PMMPTE New, PMMPTE Old, int Index) { @@ -126,7 +132,7 @@ bool MmpCloneUserHalfLevel(int Level, PMMPTE New, PMMPTE Old, int Index) if (PageForThisLevelPFN == PFN_INVALID) return false; - if (~Old[Index] & MM_PTE_PRESENT) + if (!MM_PTE_ISPRESENT(Old[Index])) { // TODO handle a non present page... Currently just exit. New[Index] = 0; @@ -253,7 +259,7 @@ PMMPTE MiGetPTEPointer(HPAGEMAP Mapping, uintptr_t Address, bool AllocateMissing MMPTE Entry = *EntryPointer; - if (pml > 1 && (~Entry & MM_PTE_PRESENT)) + if (pml > 1 && !MM_PTE_ISPRESENT(Entry)) { // not present!! Do we allocate it? if (!AllocateMissingPMLs) @@ -338,7 +344,7 @@ static void MmpFreeVacantPMLsSub(HPAGEMAP Mapping, uintptr_t Address) MMPTE Entry = *EntryPointer; - if (pml > 1 && (~Entry & MM_PTE_PRESENT)) + if (pml > 1 && !MM_PTE_ISPRESENT(Entry)) { // if we don't have a parent, return if (!ParentEntryPointer) @@ -521,7 +527,7 @@ void MiUnmapPages(uintptr_t Address, size_t LengthPages) *pPTE &= ~MM_DPTE_COMMITTED; - if (*pPTE & MM_PTE_PRESENT) + if (MM_PTE_ISPRESENT(*pPTE)) { *pPTE &= ~MM_PTE_PRESENT; *pPTE |= MM_DPTE_WASPRESENT; diff --git a/boron/source/mm/amd64/ptfree.c b/boron/source/mm/amd64/ptfree.c index f91b927e..58e59f5b 100644 --- a/boron/source/mm/amd64/ptfree.c +++ b/boron/source/mm/amd64/ptfree.c @@ -80,7 +80,7 @@ void MiFreeUnusedMappingLevelsInCurrentMap(uintptr_t StartVa, size_t SizePages) PageNumber += PTES_COVERED_BY_PML4, ++Pte) { - if (~*Pte & MM_PTE_PRESENT) + if (!MM_PTE_ISPRESENT(*Pte)) continue; PMMPTE SubPte = MiGetSubPteAddress(Pte); @@ -113,7 +113,7 @@ static bool MmpFreeUnusedMappingLevelsInCurrentMapPML(PMMPTE Pte, int MapLevel) if (*Pte) { // The PTE exists, check if it was decommitted though. - if ((~*Pte & MM_PTE_PRESENT) && (*Pte & MM_DPTE_DECOMMITTED)) + if (!MM_PTE_ISPRESENT(*Pte) && (*Pte & MM_DPTE_DECOMMITTED)) continue; // This mapping level is busy. @@ -129,7 +129,7 @@ static bool MmpFreeUnusedMappingLevelsInCurrentMapPML(PMMPTE Pte, int MapLevel) // Walk the PML. for (size_t i = 0; i < PTES_PER_LEVEL; ++i, ++Pte) { - if (~*Pte & MM_PTE_PRESENT) + if (!MM_PTE_ISPRESENT(*Pte)) continue; PMMPTE SubPte = MiGetSubPteAddress(Pte); diff --git a/boron/source/mm/arm/idmap.c b/boron/source/mm/arm/idmap.c new file mode 100644 index 00000000..eae245db --- /dev/null +++ b/boron/source/mm/arm/idmap.c @@ -0,0 +1,78 @@ +/*** + 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" + +#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_PTEL1_COARSE_PAGE_TABLE) +#define L2PTE(Pfn) (MM_PTE_NEWPFN(Pfn) | MM_PTE_PRESENT | MM_PTE_READWRITE) + +#define L1PTE_FLAGS_SEC 0b111010000001110 // 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; + } + + for (uintptr_t i = 0; i < MI_POOL_HEADERS_SIZE; i += 1024 * 4096) + { + uintptr_t Address = MI_POOL_HEADERS_START + i; + KiRootPageTable[Address >> 20] = ((uintptr_t) &KiPoolHeadersPageTables[i / PAGE_SIZE]) | MM_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_PTE_PFN(KiRootPageTable[i * 4]); + KiRootPageTableDebbie[i] = L2PTE(Pfn); + } + else { + KiRootPageTableDebbie[i] = 0; + } + } + + for (int i = 0; i < 4; i++) { + KiRootPageTable[4088 + i] = L1PTE(DebbieAddress + i * 1024); + KiRootPageTable[4092 + i] = L1PTE(JibbieAddress + i * 1024); + } + + KeFlushTLB(); +} diff --git a/boron/source/mm/arm/pt.c b/boron/source/mm/arm/pt.c new file mode 100644 index 00000000..e07839da --- /dev/null +++ b/boron/source/mm/arm/pt.c @@ -0,0 +1,331 @@ +/*** + 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" + +#define L1PTE(Address) (((Address) & ~0x3FF) | MM_PTEL1_COARSE_PAGE_TABLE) +#define L2PTE(Pfn) (MM_PTE_NEWPFN(Pfn) | MM_PTE_PRESENT | MM_PTE_READWRITE) + +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] = L2PTE(NewPageMapping + i); + + JibbiePtr[4] = L2PTE(Jibbie); + JibbiePtr[5] = L2PTE(Debbie); + + // 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] = L2PTE((uintptr_t)KiExceptionHandlerTable); + + 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); + + for (int i = 512; i < 1024; i++) { + if (OldRootPtr[i * 4]) { + MMPFN Pfn = MM_PTE_PFN(OldRootPtr[i * 4]); + DebbiePtr[i] = L2PTE(Pfn); + } + } + + // 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] = L1PTE(DebbieAddress + i * 1024); + RootPtr[4092 + i] = L1PTE(JibbieAddress + i * 1024); + } + + MmEndUsingHHDM(); + MmUnlockKernelSpace(); + + 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 = MM_PTE_PFN(RootPtr[4088]); + Jibbie = MM_PTE_PFN(RootPtr[4092]); + + MmEndUsingHHDM(); + + MmFreePhysicalPage(Jibbie); + MmFreePhysicalPage(Debbie); + MmFreePhysicalContiguousRegion(Root, 4); +} + +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; + + // check if the corresponding L1 page table is present through Jibbie. + PMMPTE PtePtr = (PMMPTE) MI_PML1_LOCATION; + if ((PtePtr[Convert.Level1Index] & MM_PTEL1_TYPE) == 0) + { + 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] = L1PTE(Pfn * PAGE_SIZE + i * 1024); + } + + // Debbie must also be updated. + PtePtr = (PMMPTE) MI_PML2_MIRROR_LOCATION; + PtePtr[Convert.Level1Index >> 2] = L2PTE(Pfn); + } + + // 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); + + 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 = MM_PTE_PRESENT | Permissions | MmPFNToPhysPage(pfn); + return true; + } + + *Pte = MM_DPTE_COMMITTED | Permissions; + 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 = MM_PTE_NEWPFN(MmPhysPageToPFN(PhysicalPage)) | Permissions | MM_PTE_PRESENT; + return true; +} + +void MiUnmapPages(uintptr_t Address, size_t LengthPages) +{ + for (size_t i = 0; i < LengthPages; i++) + { + PMMPTE pPTE = MmGetPteLocationCheck(Address + i * PAGE_SIZE, false); + if (!pPTE) + continue; + + MMPTE Pte = *pPTE; + *pPTE = 0; + + if (Pte & MM_PTE_PRESENT) + MmFreePhysicalPage(MM_PTE_PFN(Pte)); + } + + 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); + return false; +} + +MMPTE MmGetPteBitsFromProtection(int Protection) +{ + MMPTE Pte = 0; + + if (Protection & PAGE_WRITE) + Pte |= MM_PTE_READWRITE; + + if (~Protection & PAGE_EXECUTE) + Pte |= MM_PTE_NOEXEC; + + return Pte; +} diff --git a/boron/source/mm/arm/ptfree.c b/boron/source/mm/arm/ptfree.c new file mode 100644 index 00000000..033d8d82 --- /dev/null +++ b/boron/source/mm/arm/ptfree.c @@ -0,0 +1,67 @@ +/*** + 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; + + for (size_t PteIndex = 0; PteIndex < 1024; PteIndex++) + { + if (Pte[PteIndex] != 0) + { + 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_PML1_LOCATION; + PMMPTE Pte = &Pml1[(StartVa >> 20) & ~3]; + + for (size_t i = 0; i < SizePages; i += 1024, Pte += 4) + { + if (!*Pte) + continue; + + PMMPTE SubPte = &Pml2[(StartVa >> 12)]; + if (MmpIsPteListCompletelyEmpty(SubPte)) + { + MMPFN Pfn = (*Pte >> 12); + MmFreePhysicalPage(Pfn); + + for (int m = 0; m < 4; m++) { + Pte[m] = 0; + } + + // also clear the pml2 mirror: + PMMPTE Pml2Mirror = (PMMPTE) MI_PML2_MIRROR_LOCATION; + Pml2Mirror[(StartVa >> 22)] = 0; + } + } + + KeFlushTLB(); +} diff --git a/boron/source/mm/clone.c b/boron/source/mm/clone.c index 21778cfa..83b79f8c 100644 --- a/boron/source/mm/clone.c +++ b/boron/source/mm/clone.c @@ -62,10 +62,10 @@ static BSTATUS MmpChangeAnonymousMemoryIntoSections(PMMVAD_LIST VadList) } MMPTE Pte = *PtePtr; - if (Pte & MM_PTE_PRESENT) + if (MM_PTE_ISPRESENT(Pte)) { // The PTE has to come from the PMM, I can't explain it otherwise. - ASSERT(Pte & MM_PTE_ISFROMPMM); + ASSERT(MM_PTE_CHECKFROMPMM(Pte)); MMPFN Pfn = MM_PTE_PFN(Pte); @@ -202,10 +202,10 @@ static BSTATUS MmpAddOverlaysIfNeeded(PMMVAD_LIST VadList, bool WritePTEs) } MMPTE Pte = *PtePtr; - if (Pte & MM_PTE_PRESENT) + if (MM_PTE_ISPRESENT(Pte)) { // The PTE has to come from the PMM, I can't explain it otherwise. - ASSERT(Pte & MM_PTE_ISFROMPMM); + ASSERT(MM_PTE_CHECKFROMPMM(Pte)); MMPFN Pfn = MM_PTE_PFN(Pte); MmFreePhysicalPage(Pfn); @@ -275,7 +275,7 @@ static BSTATUS MmpReplicateCommitPtesIfNeeded(PEPROCESS DestinationProcess, PMMV MMPTE DestPte = 0; MMPTE Pte = *PtePtr; - if (Pte & MM_PTE_PRESENT) + if (MM_PTE_ISPRESENT(Pte)) { DestPte = MM_DPTE_COMMITTED; } diff --git a/boron/source/mm/commit.c b/boron/source/mm/commit.c index 8adc2eca..97bc8e89 100644 --- a/boron/source/mm/commit.c +++ b/boron/source/mm/commit.c @@ -123,7 +123,7 @@ BSTATUS MmCommitVirtualMemory(uintptr_t StartVa, size_t SizePages, int Protectio } // The range is safe to commit. - uintptr_t PteFlags = MmGetPteBitsFromProtection(Protection); + MMPTE PteFlags = MmGetPteBitsFromProtection(Protection); // TODO: Enforce W^X here if the user doesn't have the relevant permissions @@ -235,16 +235,18 @@ void MiDecommitVad(PMMVAD_LIST VadList, PMMVAD Vad, size_t StartVa, size_t SizeP } } - if (*Pte & MM_PTE_PRESENT) + if (MM_PTE_ISPRESENT(*Pte)) { // The PTE is present. If it doesn't come from the PMM, then // it's MMIO and it's not tracked. - if (*Pte & MM_PTE_ISFROMPMM) - { - // Free the physical page. - MMPFN Pfn = MmPhysPageToPFN(*Pte & MM_PTE_ADDRESSMASK); - MmFreePhysicalPage(Pfn); - } + // + // NOTE 2025/12/25: This should never be false. MMIO is registered + // with the PMM. + ASSERT(MM_PTE_CHECKFROMPMM(*Pte)); + + // Free the physical page. + MMPFN Pfn = MM_PTE_PFN(*Pte); + MmFreePhysicalPage(Pfn); } else if (*Pte != 0) { diff --git a/boron/source/mm/fault.c b/boron/source/mm/fault.c index f15a1fbb..7cf042ed 100644 --- a/boron/source/mm/fault.c +++ b/boron/source/mm/fault.c @@ -56,7 +56,7 @@ static BSTATUS MmpHandleFaultCommittedPage(PMMPTE PtePtr, MMPTE SupervisorBit) MMPTE NewPte = *PtePtr; NewPte &= ~MM_DPTE_COMMITTED; NewPte |= MM_PTE_PRESENT | MM_PTE_ISFROMPMM | SupervisorBit; - NewPte |= MmPFNToPhysPage(Pfn); + NewPte |= MM_PTE_NEWPFN(Pfn); NewPte &= ~MM_PTE_PKMASK; *PtePtr = NewPte; @@ -181,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 ) @@ -262,7 +262,7 @@ BSTATUS MiNormalFault(PEPROCESS Process, uintptr_t Va, PMMPTE PtePtr, KIPL Space } // Now the PTE is here and we can commit it. - *PtePtr = Vad->Flags.Protection; + *PtePtr = 0; // (Access to the VAD is no longer required now) MmUnlockVadList(VadList); @@ -305,13 +305,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 ) @@ -378,7 +378,7 @@ BSTATUS MiWriteFault(UNUSED PEPROCESS Process, uintptr_t Va, PMMPTE PtePtr) *PtePtr = (*PtePtr & ~(MM_PTE_COW | MM_PTE_ADDRESSMASK)) | MM_PTE_READWRITE | - (NewPfn * PAGE_SIZE); + MM_PTE_NEWPFN(NewPfn); } else { @@ -427,7 +427,7 @@ BSTATUS MmPageFault(UNUSED uintptr_t FaultPC, uintptr_t FaultAddress, uintptr_t PMMPTE PtePtr = MmGetPteLocationCheck(FaultAddress, false); // If the PTE is present. - if (PtePtr && (*PtePtr & MM_PTE_PRESENT)) + if (PtePtr && MM_PTE_ISPRESENT(*PtePtr)) { // If this is a user mode thread and it's trying to access kernel mode addresses, // declare failure instantly. diff --git a/boron/source/mm/i386/pt.c b/boron/source/mm/i386/pt.c index 3768d601..9fe98a26 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; MMPTE SupervisorBit; @@ -48,12 +52,12 @@ bool MmCheckPteLocation(uintptr_t Address, bool GenerateMissingLevels) // Check the presence of the PT Pte = MmGetPteLocation(MI_PTE_LOC(Address)); - if (~(*Pte) & MM_PTE_PRESENT) + if (!MM_PTE_ISPRESENT(*Pte)) { 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)) @@ -76,7 +85,7 @@ PMMPTE MmGetPteLocationCheck(uintptr_t Address, bool GenerateMissingLevels) HPAGEMAP MiCreatePageMapping() { // Allocate the PML2. - int NewPageMappingPFN = MmAllocatePhysicalPage(); + MMPFN NewPageMappingPFN = MmAllocatePhysicalPage(); if (NewPageMappingPFN == PFN_INVALID) { LogMsg("Error, can't create a new page mapping. Can't allocate PML4"); @@ -108,6 +117,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)) @@ -137,7 +152,7 @@ static bool MmpMapSingleAnonPageAtPte(PMMPTE Pte, uintptr_t Permissions, bool No if (MM_DBG_NO_DEMAND_PAGING || NonPaged) { - int pfn = MmAllocatePhysicalPage(); + MMPFN pfn = MmAllocatePhysicalPage(); if (pfn == PFN_INVALID) { //DbgPrint("MiMapAnonPage(%p, %p) failed because we couldn't allocate physical memory", Mapping, Address); @@ -185,7 +200,7 @@ void MiUnmapPages(uintptr_t Address, size_t LengthPages) *pPTE &= ~MM_DPTE_COMMITTED; - if (*pPTE & MM_PTE_PRESENT) + if (MM_PTE_ISPRESENT(*pPTE)) { *pPTE &= ~MM_PTE_PRESENT; *pPTE |= MM_DPTE_WASPRESENT; diff --git a/boron/source/mm/i386/ptfree.c b/boron/source/mm/i386/ptfree.c index 9ea36888..c347971c 100644 --- a/boron/source/mm/i386/ptfree.c +++ b/boron/source/mm/i386/ptfree.c @@ -66,7 +66,7 @@ void MiFreeUnusedMappingLevelsInCurrentMap(uintptr_t StartVa, size_t SizePages) // Scan each page table in the range. for (size_t i = 0; i < SizePages; i += PTES_COVERED_BY_ONE_PT, ++Pte) { - if (~(*Pte) & MM_PTE_PRESENT) + if (!MM_PTE_ISPRESENT(*Pte)) continue; PMMPTE SubPte = MiGetSubPteAddress(Pte); diff --git a/boron/source/mm/mdl.c b/boron/source/mm/mdl.c index 7e547c60..cdab7ff2 100644 --- a/boron/source/mm/mdl.c +++ b/boron/source/mm/mdl.c @@ -212,12 +212,12 @@ BSTATUS MmProbeAndPinPagesMdl(PMDL Mdl, KPROCESSOR_MODE AccessMode, bool IsWrite { MMPTE Pte = *PtePtr; - if (~Pte & MM_PTE_PRESENT) + if (!MM_PTE_ISPRESENT(Pte)) { // Try to fault on this page. We don't know what kind of non-present page this is. TryFault = true; } - else if (~Pte & MM_PTE_ISFROMPMM) + else if (!MM_PTE_CHECKFROMPMM(Pte)) { // This is MMIO space or the HHDM. Disallow its capture. FailureReason = STATUS_INVALID_PARAMETER; @@ -257,8 +257,8 @@ BSTATUS MmProbeAndPinPagesMdl(PMDL Mdl, KPROCESSOR_MODE AccessMode, bool IsWrite // Probe was successful and this is a proper page (writable if IsWrite is true). ASSERT((Pte & MM_PTE_READWRITE) || !IsWrite); - ASSERT(Pte & MM_PTE_ISFROMPMM); ASSERT(Pte & MM_PTE_PRESENT); + ASSERT(MM_PTE_CHECKFROMPMM(Pte)); // Fetch the page frame number. int Pfn = MM_PTE_PFN(Pte); 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 103895c4..50bc6042 100644 --- a/boron/source/mm/pmm.c +++ b/boron/source/mm/pmm.c @@ -179,8 +179,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) @@ -230,6 +230,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 { uint64_t entries[512]; @@ -242,6 +252,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. PageMapLevel *pPML[4]; pPML[3] = (PageMapLevel*) MmGetHHDMOffsetAddr(pageTable); @@ -249,7 +261,7 @@ static bool MiMapNewPageAtAddressIfNeeded(uintptr_t pageTable, uintptr_t address for (int i = 3; i >= 0; i--) { int index = (address >> (12 + 9 * i)) & 0x1FF; - if (pPML[i]->entries[index] & MM_PTE_PRESENT) + if (MM_PTE_ISPRESENT(pPML[i]->entries[index])) { if (i == 0) return true; // didn't allocate anything @@ -280,50 +292,29 @@ static bool MiMapNewPageAtAddressIfNeeded(uintptr_t pageTable, uintptr_t address } 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 (~Level2[Convert.Level2Index] & MM_PTE_PRESENT) - { - uintptr_t Addr = MiAllocatePageFromMemMap(); - - if (!Addr) - { - // TODO: Allow rollback - return false; - } - - Level2[Convert.Level2Index] = Addr | MM_PTE_PRESENT | MM_PTE_READWRITE; + PMMPTE Pte = MmGetPteLocation(address); + if (*Pte) { + return true; } - if (~Level1[Convert.Level1Index] & MM_PTE_PRESENT) - { - uintptr_t Addr = MiAllocatePageFromMemMap(); - - if (!Addr) - { - // TODO: Allow rollback - return false; - } - - MmBeginUsingHHDM(); - memset(MmGetHHDMOffsetAddr(Addr), 0, PAGE_SIZE); - MmEndUsingHHDM(); - - Level1[Convert.Level1Index] = Addr | MM_PTE_PRESENT | MM_PTE_READWRITE; + MMPFN Pfn = MiAllocatePfnFromMemMap(); + if (Pfn == PFN_INVALID) { + return false; } + MmBeginUsingHHDM(); + memset(MmGetHHDMOffsetAddr(MmPFNToPhysPage(Pfn)), 0, PAGE_SIZE); + MmEndUsingHHDM(); + + *Pte = MM_PTE_NEWPFN(Pfn) | MM_PTE_PRESENT | MM_PTE_READWRITE; + return true; -#else - #error "Implement this for your platform!" #endif } @@ -468,7 +459,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); @@ -566,7 +557,7 @@ void MmRegisterMMIOAsMemory(uintptr_t Base, uintptr_t Length) KeCrash("MmRegisterMMIOAsMemory: could not ensure PTE location %p exists", currPage); PMMPTE Pte = MmGetPteLocation(currPage); - if (!(*Pte & MM_PTE_PRESENT)) + if (!MM_PTE_ISPRESENT(*Pte)) { // allocate it MMPFN PfnAlloc = MmAllocatePhysicalPage(); @@ -1119,3 +1110,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 90197c5a..3d0e04c4 100644 --- a/boron/source/mm/pool.c +++ b/boron/source/mm/pool.c @@ -133,3 +133,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/poolsup.c b/boron/source/mm/poolsup.c index c7e321bb..9b25b755 100644 --- a/boron/source/mm/poolsup.c +++ b/boron/source/mm/poolsup.c @@ -14,7 +14,7 @@ Module name: ***/ #include "mi.h" -#ifdef IS_32_BIT +#ifdef TARGET_I386 // the structure of the pool header PTE if this is set is as follows: // @@ -25,7 +25,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 { @@ -46,7 +46,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) { @@ -78,11 +78,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 @@ -104,7 +157,7 @@ static LIST_ENTRY MmpPoolList; #define MI_EMPTY_TAG MI_TAG(" ") -#ifdef IS_32_BIT +#ifdef TARGET_I386 void MiInitializeRootPageTable(int Idx) { @@ -114,9 +167,30 @@ void MiInitializeRootPageTable(int Idx) if (Pfn == PFN_INVALID) KeCrashBeforeSMPInit("MiCalculatePoolHeaderPte ERROR: Out of memory!"); - *Pte = MmPFNToPhysPage(Pfn) | MM_PTE_PRESENT | MM_PTE_READWRITE; + *Pte = MM_PTE_NEWPFN(Pfn) | MM_PTE_PRESENT | MM_PTE_READWRITE; } +#elif defined TARGET_ARM + +#define L1PTE_FLAGS_CPT 0b111010000001101 + +void MiInitializeRootPageTable(int Idx) +{ + PMMPTE Pte = (PMMPTE) MI_PML1_LOCATION + Idx; + MMPFN Pfn = MmAllocatePhysicalPage(); + + if (Pfn == PFN_INVALID) + KeCrashBeforeSMPInit("MiCalculatePoolHeaderPte ERROR: Out of memory!"); + + for (int i = 0; i < 4; i++) { + Pte[i] = ((Pfn << 12) + i) | L1PTE_FLAGS_CPT; + } +} + +#endif + +#ifdef IS_32_BIT + void MiInitializePoolPageTables() { int Size1 = 1 << (MI_POOL_LOG2_SIZE - 22); @@ -147,7 +221,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(); @@ -363,7 +437,7 @@ MIPOOL_SPACE_HANDLE MiReservePoolSpaceTagged(size_t SizeInPages, void** OutputAd } ASSERT(*PtePtr == 0); - ASSERT((Handle & MM_PTE_PRESENT) == 0); + ASSERT(!MM_PTE_ISPRESENT(Handle)); MMPTE Pte = MiCalculatePoolHeaderPte(Handle); ASSERT(MiReconstructPoolHandleFromPte(Pte) == Handle); @@ -446,10 +520,10 @@ MIPOOL_SPACE_HANDLE MiGetPoolSpaceHandleFromAddress(void* AddressV) // N.B. This kind of relies on the notion that the address doesn't have // the valid bit set. uintptr_t PAddress = *PtePtr; - if (~PAddress & MM_PTE_ISPOOLHDR) { + if (~PAddress & MM_DPTE_ISPOOLHDR) { KeCrash("Trying to access pool space handle from address %p, but its PTE says %p", AddressV, PAddress); } - ASSERT(PAddress & MM_PTE_ISPOOLHDR); + ASSERT(PAddress & MM_DPTE_ISPOOLHDR); PAddress = MiReconstructPoolHandleFromPte(PAddress); MIPOOL_SPACE_HANDLE Handle = PAddress; MmUnlockKernelSpace(); diff --git a/boron/source/mm/teardown.c b/boron/source/mm/teardown.c index 69c0d5d9..6d719a2b 100644 --- a/boron/source/mm/teardown.c +++ b/boron/source/mm/teardown.c @@ -65,15 +65,15 @@ void MmTearDownProcess(PEPROCESS Process) PMMPTE PteScan = MmGetHHDMOffsetAddr(Process->Pcb.PageMap); for (int i = 0; i < 256; i++) - ASSERT(~PteScan[i] & MM_PTE_PRESENT); + ASSERT(!MM_PTE_ISPRESENT(PteScan[i])); #endif // TARGET_AMD64 #ifdef TARGET_I386 MmBeginUsingHHDM(); PMMPTE PteScan = MmGetHHDMOffsetAddr(Process->Pcb.PageMap); - for (int i = 0; i < 256; i++) - ASSERT(~PteScan[i] & MM_PTE_PRESENT); + for (int i = 0; i < 512; i++) + ASSERT(!MM_PTE_ISPRESENT(PteScan[i])); MmEndUsingHHDM(); #endif // TARGET_I386 @@ -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 b58bc27f..ea823d11 100644 --- a/boron/source/mm/vad.c +++ b/boron/source/mm/vad.c @@ -454,7 +454,7 @@ void MiCleanUpVad(PMMVAD Vad) } // Assume that the page is NOT present. - ASSERT(~*Pte & MM_PTE_PRESENT); + ASSERT(!MM_PTE_ISPRESENT(*Pte)); *Pte = 0; Pte++; diff --git a/boron/source/ps/fork.c b/boron/source/ps/fork.c index 740314af..08281719 100644 --- a/boron/source/ps/fork.c +++ b/boron/source/ps/fork.c @@ -41,7 +41,7 @@ void PspUserThreadStartFork(void* ContextV) OSClose(Context->CloseProcessHandle); MmFreePool(Context); -#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 328ff712..73002d72 100644 --- a/boron/source/ps/initproc.c +++ b/boron/source/ps/initproc.c @@ -31,6 +31,7 @@ const char* PspInitialProcessEnvironment = bool PsShouldStartInitialProcess() { + return false; return !ExIsConfigValue("NoInit", CONFIG_YES); } @@ -89,8 +90,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); 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..44742ec2 100644 --- a/boron/source/rtl/elf.c +++ b/boron/source/rtl/elf.c @@ -84,6 +84,137 @@ 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_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 +578,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/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..40564d78 100644 --- a/common/include/elf.h +++ b/common/include/elf.h @@ -161,6 +161,26 @@ 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_RELATIVE = 23, // 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/framebuf/source/main.c b/drivers/framebuf/source/main.c index 3f02ffe2..8b88242e 100644 --- a/drivers/framebuf/source/main.c +++ b/drivers/framebuf/source/main.c @@ -72,7 +72,7 @@ BSTATUS FramebufferRead(PIO_STATUS_BLOCK Iosb, UNUSED PFCB Fcb, uint64_t Offset, void *TempMapping = MmMapIoSpace( Ext->Address + Offset, Size, - MM_PTE_READWRITE | MM_PTE_CDISABLE, + MM_PTE_READWRITE | MM_PTE_NOCACHE, POOL_TAG("FBCP") ); @@ -122,7 +122,7 @@ BSTATUS FramebufferWrite(PIO_STATUS_BLOCK Iosb, PFCB Fcb, uint64_t Offset, PMDL void *TempMapping = MmMapIoSpace( Ext->Address + Offset, Size, - MM_PTE_READWRITE | MM_PTE_CDISABLE, + MM_PTE_READWRITE | MM_PTE_NOCACHE, POOL_TAG("FBCP") ); diff --git a/drivers/hali386/source/term.c b/drivers/hali386/source/term.c index 46f3d8bc..66028070 100644 --- a/drivers/hali386/source/term.c +++ b/drivers/hali386/source/term.c @@ -80,7 +80,7 @@ void HalInitTerminal() void* FramebufferMemory = MmMapIoSpace( (uintptr_t)Framebuffer->Address, Framebuffer->Pitch * Framebuffer->Height, - MM_PTE_READWRITE | MM_PTE_CDISABLE, + MM_PTE_READWRITE | MM_PTE_NOCACHE, POOL_TAG("HAFB") ); diff --git a/drivers/halx86/source/acpi.c b/drivers/halx86/source/acpi.c index f041fb19..3fe0195d 100644 --- a/drivers/halx86/source/acpi.c +++ b/drivers/halx86/source/acpi.c @@ -94,7 +94,7 @@ void AcpiInitPmt() void* PageAddress = MmMapIoSpace( Addr, 1, // SizePages - MM_PTE_READWRITE | MM_PTE_CDISABLE | MM_PTE_GLOBAL | MM_PTE_NOEXEC, + MM_PTE_READWRITE | MM_PTE_NOCACHE | MM_PTE_GLOBAL | MM_PTE_NOEXEC, POOL_TAG("APMT") ); diff --git a/drivers/halx86/source/hpet.c b/drivers/halx86/source/hpet.c index 9dc4f9a4..a8ff155b 100644 --- a/drivers/halx86/source/hpet.c +++ b/drivers/halx86/source/hpet.c @@ -52,7 +52,7 @@ void HpetInitialize() void* Address = MmMapIoSpace( HpetAddress, 1, // SizePages - MM_PTE_READWRITE | MM_PTE_CDISABLE | MM_PTE_GLOBAL | MM_PTE_NOEXEC, + MM_PTE_READWRITE | MM_PTE_NOCACHE | MM_PTE_GLOBAL | MM_PTE_NOEXEC, POOL_TAG("HPET") ); 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 3f6ac0de..1b20efdd 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. @@ -124,7 +128,7 @@ void Init() PixBuff = MmMapIoSpace( (uintptr_t)Framebuffer->Address, Framebuffer->Pitch * Framebuffer->Height, - MM_PTE_READWRITE | MM_PTE_CDISABLE, + MM_PTE_READWRITE | MM_PTE_NOCACHE, POOL_TAG("FWFB") ); #else @@ -139,6 +143,8 @@ void Init() // ####### UTILITY LIBRARY ####### +#ifdef USE_TSC + uint64_t ReadTsc() { uintptr_t low, high; @@ -156,6 +162,8 @@ unsigned RandTscBased() return ((uint32_t)Tsc ^ (uint32_t)(Tsc >> 32)); } +#endif + int g_randGen = 0x9521af17; int Rand() { @@ -360,7 +368,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..92e7cbee 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(); diff --git a/drivers/test/source/mm1tst.c b/drivers/test/source/mm1tst.c index ebf85142..1f5367a6 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/tools/Makefile.arm b/tools/Makefile.arm new file mode 100644 index 00000000..e125a2c8 --- /dev/null +++ b/tools/Makefile.arm @@ -0,0 +1,6 @@ +# arm Makefile + +IMAGE_TARGET = $(BUILD_DIR)/../image.$(TARGETL).img + +DRIVERS_LIST = \ + test diff --git a/tools/build_image_arm.mk b/tools/build_image_arm.mk new file mode 100644 index 00000000..344e9154 --- /dev/null +++ b/tools/build_image_arm.mk @@ -0,0 +1,12 @@ +# TEMPORARY + +$(IMAGE_TARGET): kernel drivers apps initrd + @echo "[MK]\tBuilding iso..." + 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) + dd if=/dev/zero of=$(IMAGE_TARGET) bs=1M count=64 + mkfs.vfat $(IMAGE_TARGET) + mcopy -i $(IMAGE_TARGET) -s $(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..e1e8b003 --- /dev/null +++ b/tools/run-arm.bat @@ -0,0 +1,27 @@ +@rem Run script + +@echo off + +set backupPath=%path% +set NSPath=%CD% +cd /d c:\Program Files\qemu +set path=%path%;%NSPath% + +@rem NOTE: Placeholder !! +qemu-system-arm.exe ^ + -d int ^ + -M versatilepb ^ + -cpu arm926 ^ + -m 128M ^ + -kernel %nspath%\build\arm\kernel.elf ^ + -monitor telnet:127.0.0.1:56789,server,nowait ^ + -serial stdio ^ + -display sdl ^ + -no-reboot ^ + -no-shutdown ^ + -s + +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..fbfe8fd0 --- /dev/null +++ b/tools/run-unix-arm.sh @@ -0,0 +1,10 @@ +#!/bin/sh +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..539157e9 --- /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 ?= v5 +$(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/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..78a62676 --- /dev/null +++ b/user/libboron/source/calls_arm.S @@ -0,0 +1,17 @@ +// The Boron Operating System +// Copyright (C) 2025 iProgramInCpp + +#ifdef TARGET_ARM + +.macro CALL number, argcount, name + .global \name + .type \name, %function +\name: + @ TODO + mov r0, #1 + bx lr +.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/reloc.c b/user/libboron/source/reloc.c index d9f2bf48..00808c58 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 RelocateSelf(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 RelocateSelf(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 diff --git a/user/libboron/source/repentry.S b/user/libboron/source/repentry.S index 7dec6990..1a2198e8 100644 --- a/user/libboron/source/repentry.S +++ b/user/libboron/source/repentry.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 @@ -16,6 +16,7 @@ OSDLLJumpToEntry: jmp *%rsi #elif defined TARGET_I386 + .att_syntax /* void OSDLLJumpToEntry(uintptr_t StackBottom, void* EntryPoint, void* Context); */ .globl OSDLLJumpToEntry @@ -29,6 +30,15 @@ OSDLLJumpToEntry: push %ecx call *%ebx +#elif TARGET_ARM + +#warning Implement this TODO + + .globl OSDLLJumpToEntry + .type OSDLLJumpToEntry, %function +OSDLLJumpToEntry: + bx lr + #else #error Implement this TODO