291 lines
9.6 KiB
C++

#include "boot.hpp"
#include <algorithm>
#include <bit>
#include <cassert>
#include <cstdio>
#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<InterruptDescriptor, 256> 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<baos::MemoryRange> detectMemoryRangesFromEfi(const EfiMemoryMap& memoryMap) noexcept
{
std::vector<baos::MemoryRange> 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<initfunc_t*>(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<typename THandler>
void setupInterrupt(std::uint8_t index, THandler handler) noexcept
{
const std::uint64_t offset = std::bit_cast<std::uint64_t>(handler);
IDT[index] = InterruptDescriptor{
.offsetLow = static_cast<unsigned>(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<MemoryRange> 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<draw::Pixel*>(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<std::uint8_t, 16384> usermodeStack;
__enterUsermode(SEGIDX_USER_CODE, SEGIDX_USER_DATA, &tss.rsp0, &main, &usermodeStack.back());
}
} // extern "C"