158 lines
4.4 KiB
C++

#include "boot.hpp"
#include <bit>
#include <cstdio>
#include "os/draw.hpp"
#include "os/serial.hpp"
#include "os/tty.hpp"
#include "./x86_64.hpp"
extern "C" int gKernelEnd;
namespace
{
void initHeapFromEfiMemoryMap(const EfiMemoryMap& memoryMap) noexcept
{
const uint64_t minAddr = reinterpret_cast<uint64_t>(&gKernelEnd);
for (const EFI_MEMORY_DESCRIPTOR& descriptor : memoryMap)
{
if (!isEfiMemoryTypeUsable(descriptor.Type))
{
continue;
}
uint64_t addr = descriptor.PhysicalStart;
uint64_t len = descriptor.NumberOfPages * 4096; // TODO: is this the correct page size?
// anything before the kernel we ignore
if (addr < minAddr)
{
// if the entire entry is before the kernel, continue
if (addr + len <= minAddr)
{
continue;
}
// otherwise shrink it and use the rest
len -= (minAddr - addr);
addr = minAddr;
}
__ba_registerAllocatableMemory(reinterpret_cast<void*>(addr), len);
}
#if 0
const uint64_t minAddr = reinterpret_cast<uint64_t>(&gKernelEnd);
for (uint32_t addr = firstEntryAddr; addr < firstEntryAddr + bufferLength;)
{
multiboot_memory_map_t entry = *reinterpret_cast<multiboot_memory_map_t*>(addr);
if(entry.type == MULTIBOOT_MEMORY_AVAILABLE)
{
std::printf("Start Addr: %X | Length: %b\n",
static_cast<unsigned>(entry.addr), static_cast<size_t>(entry.len));
// anything before the kernel we ignore
if (entry.addr < minAddr)
{
// if the entire entry is before the kernel, continue
if (entry.addr + entry.len <= minAddr)
{
addr += sizeof(entry.size) + entry.size;
continue;
}
// otherwise shrink it and use the rest
entry.len -= (minAddr - entry.addr);
entry.addr = minAddr;
}
__ba_registerAllocatableMemory(reinterpret_cast<void*>(entry.addr), entry.len);
}
addr += sizeof(entry.size) + entry.size;
}
#endif
}
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)();
}
}
constinit std::array GDT alignas(16) = {
SEGMENT_NULL,
SEGMENT_KERNEL_CODE,
SEGMENT_KERNEL_DATA,
SEGMENT_USER_CODE,
SEGMENT_USER_DATA
};
constinit std::array<InterruptDescriptor, 256> IDT alignas(16);
extern "C" void __setGDT(uint16_t limit, void* base);
extern "C" void __setIDT(uint16_t limit, void* base);
void setupInterrupt(std::uint8_t index, void* handler, std::uint8_t flags) noexcept
{
const std::uint64_t offset = std::bit_cast<std::uint64_t>(handler);
IDT[index] = InterruptDescriptor{
.offsetLow = static_cast<unsigned>(offset & 0xFFFF),
.selector = 1, // kernel code
.interruptStackTable = 0,
.gateType = InterruptGateType::INTERRUPT_GATE,
.privilegeLevel = 0,
.present = true,
.offsetHigh = (offset >> 16)
};
}
}
extern "C"
{
EfiBootInfo* gBootInfo;
void main();
void kernel_main()
{
__setGDT(sizeof(GDT), &GDT);
__setIDT(sizeof(IDT), &IDT);
// done initializing OS stuff, enable interrupts
__asm__ __volatile__("sti");
initGlobals();
// init the heap (required for the double buffer)
// initHeapFromEfiMemoryMap(gBootInfo->memoryMap);
// 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
std::puts("This is BadAppleOS and everything is fine!\n");
main();
}
} // extern "C"