#include "boot.hpp" #include #include #include #include #include "os/draw.hpp" #include "os/memory.hpp" #include "os/segments.hpp" #include "os/serial.hpp" #include "os/syscall.hpp" #include "os/tty.hpp" #include "drivers/pci.hpp" #include "drivers/pic.hpp" #include "drivers/ps2.hpp" #include "drivers/usb.hpp" #include "./x86_64.hpp" namespace { inline constexpr std::uint8_t MASTER_PIC_OFFSET = 0x20; inline constexpr std::uint8_t SLAVE_PIC_OFFSET = 0x28; static_assert(isValidPICOffset(MASTER_PIC_OFFSET)); static_assert(isValidPICOffset(SLAVE_PIC_OFFSET)); constexpr std::uint8_t irqToInterrupt(std::uint8_t irq) noexcept { if (irq < 8) { return MASTER_PIC_OFFSET + irq; } return SLAVE_PIC_OFFSET + irq; } inline constexpr std::uint8_t INTERRUPT_DIVISION_ERROR = 0x00; inline constexpr std::uint8_t INTERRUPT_OVERFLOW = 0x04; inline constexpr std::uint8_t INTERRUPT_BOUND_RANGE_EXCEEDED = 0x05; inline constexpr std::uint8_t INTERRUPT_INVALID_OPCODE = 0x06; inline constexpr std::uint8_t INTERRUPT_DEVICE_NOT_AVAILABLE = 0x07; inline constexpr std::uint8_t INTERRUPT_DOUBLE_FAULT = 0x08; inline constexpr std::uint8_t INTERRUPT_INVALID_TSS = 0x0A; inline constexpr std::uint8_t INTERRUPT_SEGMENT_NOT_PRESENT = 0x0B; inline constexpr std::uint8_t INTERRUPT_STACK_SEGMENT_FAULT = 0x0C; inline constexpr std::uint8_t INTERRUPT_GENERAL_PROTECTION_FAULT = 0x0D; inline constexpr std::uint8_t INTERRUPT_PAGE_FAULT = 0x0E; inline constexpr std::uint8_t INTERRUPT_X87_FP_EXCEPTION = 0x10; inline constexpr std::uint8_t INTERRUPT_ALIGNMENT_CHECK = 0x11; inline constexpr std::uint8_t INTERRUPT_MACHINE_CHECK = 0x12; inline constexpr std::uint8_t INTERRUPT_SIMD_FP_EXCEPTION = 0x13; inline constexpr std::uint8_t INTERRUPT_VIRTUALIZATION_EXCEPTION = 0x14; inline constexpr std::uint8_t INTERRUPT_CONTROL_PROTECTION_EXCEPTION = 0x15; inline constexpr std::uint8_t INTERRUPT_KEYBOARD = irqToInterrupt(1); TaskStateSegment tss alignas(16); constinit std::array GDT alignas(16) = { SEGMENT_NULL, SEGMENT_KERNEL_CODE, SEGMENT_KERNEL_DATA, SEGMENT_USER_DATA, SEGMENT_USER_CODE, SEGMENT_NULL, // TSS lower SEGMENT_NULL // TSS higher }; constinit std::array IDT alignas(16); inline baos::MemoryType mapEfiMemoryType(UINT32 type) noexcept { switch (type) { case EfiLoaderCode: case EfiLoaderData: case EfiBootServicesCode: case EfiBootServicesData: case EfiConventionalMemory: return baos::MemoryType::USABLE; case EfiRuntimeServicesCode: case EfiRuntimeServicesData: case EfiUnusableMemory: case EfiACPIReclaimMemory: case EfiMaxMemoryType: default: return baos::MemoryType::UNUSABLE; case EfiMemoryMappedIO: case EfiMemoryMappedIOPortSpace: return baos::MemoryType::MMIO; case EfiACPIMemoryNVS: case EfiPalCode: case EfiReservedMemoryType: return baos::MemoryType::RESERVED; } } std::vector detectMemoryRangesFromEfi(const EfiMemoryMap& memoryMap) noexcept { std::vector ranges; ranges.reserve(memoryMap.mapSize / memoryMap.descriptorSize); for (const EFI_MEMORY_DESCRIPTOR& descriptor : memoryMap) { const baos::MemoryType type = mapEfiMemoryType(descriptor.Type); assert(descriptor.PhysicalStart % 4096 == 0); uint64_t pageBase = descriptor.PhysicalStart / 4096; uint64_t numPages = descriptor.NumberOfPages; ranges.push_back({.pageBase = pageBase, .numPages = numPages, .type = type}); } std::sort(ranges.begin(), ranges.end()); // merge adjacent ranges for (std::size_t idx = ranges.size() - 1; idx > 0; --idx) { baos::MemoryRange& left = ranges[idx-1]; baos::MemoryRange& right = ranges[idx]; if (left.type == right.type && left.pageBase + left.numPages == right.pageBase) { left.numPages += right.numPages; right.numPages = 0; } } std::erase_if(ranges, [](const baos::MemoryRange& range) { return range.numPages == 0; }); return ranges; } using initfunc_t = void *(); extern "C" initfunc_t* start_ctors; extern "C" initfunc_t* end_ctors; void initGlobals() { for (initfunc_t** initFunc = &start_ctors; initFunc != &end_ctors; ++initFunc) { // TODO: this confuses me if (*initFunc == reinterpret_cast(0xffffffffffffffff)) { break; } (*initFunc)(); } } extern "C" void __setGDT(uint16_t limit, void* base); extern "C" void __setIDT(uint16_t limit, void* base); extern "C" void __reloadSegments(uint64_t code, uint64_t data); extern "C" void __flushTSS(uint16_t segment); extern "C" void __enterUsermode(uint16_t codeSegment, uint16_t dataSegment, void* rsp, void (*function)(), void* stackPtr); template void setupInterrupt(std::uint8_t index, THandler handler) noexcept { const std::uint64_t offset = std::bit_cast(handler); IDT[index] = InterruptDescriptor{ .offsetLow = static_cast(offset & 0xFFFF), .selectorPrivilegeLevel = 0, .selectorIndex = 1, // kernel code .interruptStackTable = 0, .gateType = InterruptGateType::INTERRUPT_GATE, .privilegeLevel = 0, .present = true, .offsetHigh = (offset >> 16) }; } void initializePCIDevice(const baos::pci::Header& header) noexcept { using namespace baos; switch (header.baseClass) { case pci::BaseClass::SERIAL_BUS_CONTROLLER: switch (header.subClass) { case pci::SubClass::USB_CONTROLLER: (void) usb::initController(header); // TODO: print something on error? break; } } } } extern "C" { EfiBootInfo* gBootInfo; void main(); void kernel_main() { using namespace baos; setupInterrupt(INTERRUPT_DIVISION_ERROR, &baos::isrDivisionError); setupInterrupt(INTERRUPT_OVERFLOW, &baos::isrOverflow); setupInterrupt(INTERRUPT_BOUND_RANGE_EXCEEDED, &baos::isrBoundRangeExceeded); setupInterrupt(INTERRUPT_INVALID_OPCODE, &baos::isrInvalidOpCode); setupInterrupt(INTERRUPT_DEVICE_NOT_AVAILABLE, &baos::isrDeviceNotAvailable); setupInterrupt(INTERRUPT_DOUBLE_FAULT, &baos::isrDoubleFault); setupInterrupt(INTERRUPT_INVALID_TSS, &baos::isrInvalidTSS); setupInterrupt(INTERRUPT_SEGMENT_NOT_PRESENT, &baos::isrSegmentNotPresent); setupInterrupt(INTERRUPT_STACK_SEGMENT_FAULT, &baos::isrStackSegmentFault); setupInterrupt(INTERRUPT_GENERAL_PROTECTION_FAULT, &baos::isrGeneralProtectionFault); setupInterrupt(INTERRUPT_PAGE_FAULT, &baos::isrPageFault); setupInterrupt(INTERRUPT_X87_FP_EXCEPTION, &baos::isrX87FPException); setupInterrupt(INTERRUPT_ALIGNMENT_CHECK, &baos::isrAlignmentCheck); setupInterrupt(INTERRUPT_MACHINE_CHECK, &baos::isrMachineCheck); setupInterrupt(INTERRUPT_SIMD_FP_EXCEPTION, &baos::isrSimdFpException); setupInterrupt(INTERRUPT_VIRTUALIZATION_EXCEPTION, &baos::isrVirtualizationException); setupInterrupt(INTERRUPT_CONTROL_PROTECTION_EXCEPTION, &baos::isrControlProtectionException); setupInterrupt(INTERRUPT_KEYBOARD, &ps2::isrKeyboard); makeTSSSegment(&tss, &GDT[5]); __setGDT(sizeof(GDT), &GDT); __setIDT(sizeof(IDT), &IDT); __reloadSegments(SEGIDX_KERNEL_CODE, SEGIDX_KERNEL_DATA); __flushTSS(SEGIDX_TSS); setupSyscall(); const bool picsInited = initPICs(MASTER_PIC_OFFSET, SLAVE_PIC_OFFSET); unmaskIRQ(1); // initialize the hardware const bool ps2Inited = ps2::initialize(); // done initializing OS stuff, enable interrupts __asm__ __volatile__("sti"); // setup the "initial heap", required so we can use std::vector for detecting the memory ranges and stuff __ba_initInitialHeap(); // init the heap (required for the double buffer) const std::vector ranges = detectMemoryRangesFromEfi(gBootInfo->memoryMap); if (!setupPaging({ranges.begin(), ranges.end()})) { panic("Error setting up paging."); } // make sure boot info and the frame buffer are accessible identityMapRegion(gBootInfo, sizeof(*gBootInfo)); identityMapRegion(gBootInfo->displayInfo.frameBufferBase, gBootInfo->displayInfo.frameBufferPitch * gBootInfo->displayInfo.frameBufferHeight); initGlobals(); // initialize the framebuffer draw::initializeDefaultFramebuffer({ /* base = */ static_cast(gBootInfo->displayInfo.frameBufferBase), /* width = */ gBootInfo->displayInfo.frameBufferWidth, /* height = */ gBootInfo->displayInfo.frameBufferHeight, /* pitch = */ gBootInfo->displayInfo.frameBufferPitch / 4 }); // initialize terminal interface tty::initialize(); if (!initSerialPort(PORT_COM1)) { tty::write("Error initializing serial port.\n"); return; } serialWriteString(PORT_COM1, "\r\n\r\n"); // write newlines to separate from UEFI output for (const MemoryRange& range : ranges) { // std::printf("Start: 0x%lX, Pages: %ld, Type: %s\n", range.pageBase << 12, range.numPages, memoryTypeName(range.type)); } if (!picsInited) { std::puts("Error initializing the PICs!\n"); return; } if (!ps2Inited) { std::puts("Error initializing the PS2 controller!\n"); return; } for (const pci::Header header : pci::enumerateDevices()) { initializePCIDevice(header); } static std::array usermodeStack; __enterUsermode(SEGIDX_USER_CODE, SEGIDX_USER_DATA, &tss.rsp0, &main, &usermodeStack.back()); } } // extern "C"