WIP: paging and better malloc.
This commit is contained in:
@@ -42,10 +42,12 @@ any_target_sources = Split('''
|
||||
src/app/main.cpp
|
||||
|
||||
src/drivers/pci.cpp
|
||||
src/drivers/usb.cpp
|
||||
|
||||
src/libs/psf.cpp
|
||||
|
||||
src/os/draw.cpp
|
||||
src/os/memory.cpp
|
||||
src/os/panic.cpp
|
||||
src/os/tty.cpp
|
||||
src/os/resources/lat9-08.psf.s
|
||||
|
||||
17
targets/_any/include/drivers/usb.hpp
Normal file
17
targets/_any/include/drivers/usb.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(BAD_APPLE_OS_DRIVERS_USB_HPP_INCLUDED)
|
||||
#define BAD_APPLE_OS_DRIVERS_USB_HPP_INCLUDED
|
||||
|
||||
namespace baos::pci
|
||||
{
|
||||
struct Header;
|
||||
}
|
||||
|
||||
namespace baos::usb
|
||||
{
|
||||
[[nodiscard]] bool initController(const pci::Header& pciHeader) noexcept;
|
||||
}
|
||||
|
||||
#endif // !defined(BAD_APPLE_OS_DRIVERS_USB_HPP_INCLUDED)
|
||||
72
targets/_any/include/os/memory.hpp
Normal file
72
targets/_any/include/os/memory.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if !defined(BAD_APPLE_OS_OS_MEMORY_HPP_INCLUDED)
|
||||
#define BAD_APPLE_OS_OS_MEMORY_HPP_INCLUDED
|
||||
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
|
||||
extern "C" int gKernelStart;
|
||||
extern "C" int gKernelEnd;
|
||||
|
||||
namespace baos
|
||||
{
|
||||
struct PageTableEntry
|
||||
{
|
||||
bool present : 1;
|
||||
bool writable : 1;
|
||||
bool user : 1;
|
||||
bool writeThrough : 1;
|
||||
bool cacheDisabled : 1;
|
||||
bool accessed : 1;
|
||||
bool dirty : 1;
|
||||
bool pageSize : 1;
|
||||
bool global : 1;
|
||||
std::uint8_t avl : 3;
|
||||
std::uint64_t address : 40;
|
||||
std::uint16_t avl2 : 11;
|
||||
bool executeDisable : 1;
|
||||
};
|
||||
|
||||
enum class MemoryType
|
||||
{
|
||||
USABLE,
|
||||
UNUSABLE,
|
||||
MMIO,
|
||||
RESERVED
|
||||
};
|
||||
|
||||
struct MemoryRange
|
||||
{
|
||||
std::uint64_t pageBase;
|
||||
std::uint64_t numPages;
|
||||
MemoryType type = MemoryType::USABLE;
|
||||
|
||||
MemoryRange& operator=(const MemoryRange&) noexcept = default;
|
||||
bool operator<(const MemoryRange& other) const noexcept
|
||||
{
|
||||
return pageBase < other.pageBase;
|
||||
}
|
||||
};
|
||||
|
||||
void setupPaging(std::span<const MemoryRange> memoryRanges) noexcept;
|
||||
[[nodiscard]] const char* memoryTypeName(MemoryType type) noexcept;
|
||||
|
||||
[[nodiscard]] void* allocatePages(unsigned numConsecutive) noexcept;
|
||||
[[nodiscard]] inline void* allocatePage() noexcept { return allocatePages(1); }
|
||||
|
||||
void setupIdentityPaging(std::uint64_t page) noexcept;
|
||||
void setupIdentityPaging2M(std::uint64_t page) noexcept;
|
||||
void setupIdentityPaging(std::uint64_t firstPage, std::uint64_t lastPage) noexcept;
|
||||
|
||||
inline void identityMapRegion(void* start, std::size_t bytes) noexcept
|
||||
{
|
||||
std::uint64_t firstPage = std::bit_cast<std::uint64_t>(start) / 4096;
|
||||
std::uint64_t lastPage = std::bit_cast<std::uint64_t>(static_cast<std::uint8_t*>(start) + bytes) / 4096 + 1;
|
||||
setupIdentityPaging(firstPage, lastPage);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !defined(BAD_APPLE_OS_OS_MEMORY_HPP_INCLUDED)
|
||||
@@ -1,8 +1,10 @@
|
||||
|
||||
#include <bit>
|
||||
#include <cstdio>
|
||||
|
||||
#include "drivers/pci.hpp"
|
||||
#include "drivers/ps2.hpp"
|
||||
#include "os/memory.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -49,6 +51,61 @@ void cmdShowKeys() noexcept
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmdDumpPageTable() noexcept
|
||||
{
|
||||
std::uint64_t cr3 = 0;
|
||||
__asm__ __volatile__(
|
||||
"mov %%cr3, %0"
|
||||
: "=a"(cr3)
|
||||
:
|
||||
);
|
||||
std::uint64_t pml4Addr = cr3 & ~0xFFF;
|
||||
std::printf("pml4 address: 0x%lX\n", pml4Addr);
|
||||
baos::PageTableEntry* pml4 = std::bit_cast<baos::PageTableEntry*>(pml4Addr);
|
||||
auto printPDE = [](const baos::PageTableEntry& pde)
|
||||
{
|
||||
std::printf("%s%s%s%s%s%s%s%s%s, address=%lX\n",
|
||||
pde.present ? " P" : "",
|
||||
pde.writable ? " W" : "",
|
||||
pde.user ? " U" : "",
|
||||
pde.writeThrough ? " WT" : "",
|
||||
pde.cacheDisabled ? " CD" : "",
|
||||
pde.accessed ? " A" : "",
|
||||
pde.dirty ? " D" : "",
|
||||
pde.pageSize ? " PS" : "",
|
||||
pde.executeDisable ? " XD" : "",
|
||||
pde.address * 4096);
|
||||
};
|
||||
for (unsigned pml4e = 0; pml4e < 512; ++pml4e)
|
||||
{
|
||||
if (!pml4[pml4e].present) {
|
||||
continue;
|
||||
}
|
||||
std::printf("%d]", pml4e);
|
||||
printPDE(pml4[pml4e]);
|
||||
|
||||
baos::PageTableEntry* pdp = std::bit_cast<baos::PageTableEntry*>(pml4[pml4e].address * 4096);
|
||||
for (unsigned pdpe = 0; pdpe < 512; ++pdpe)
|
||||
{
|
||||
if (!pdp[pdpe].present) {
|
||||
continue;
|
||||
}
|
||||
std::printf("%d.%d]", pml4e, pdpe);
|
||||
printPDE(pdp[pdpe]);
|
||||
|
||||
baos::PageTableEntry* pd = std::bit_cast<baos::PageTableEntry*>(pdp[pdpe].address * 4096);
|
||||
for (unsigned pde = 0; pde < 512; ++pde)
|
||||
{
|
||||
if (!pd[pde].present) {
|
||||
continue;
|
||||
}
|
||||
std::printf("%d.%d.%d]", pml4e, pdpe, pde);
|
||||
printPDE(pd[pde]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void main()
|
||||
@@ -72,6 +129,11 @@ extern "C" void main()
|
||||
cmdShowKeys();
|
||||
continue;
|
||||
}
|
||||
else if (cmd == "dump-pages")
|
||||
{
|
||||
cmdDumpPageTable();
|
||||
continue;
|
||||
}
|
||||
std::printf("Unknown command: %s.\n", cmd.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
namespace
|
||||
{
|
||||
inline const size_t INIT_MALLOC_SPACE_ELEMENTS = 1024 * 1024;
|
||||
inline const size_t INIT_MALLOC_SPACE_BYTES = INIT_MALLOC_SPACE_ELEMENTS * sizeof(max_align_t);
|
||||
inline const size_t INIT_MALLOC_SPACE_BYTES = INIT_MALLOC_SPACE_ELEMENTS * alignof(max_align_t);
|
||||
|
||||
max_align_t gInitMallocSpace[INIT_MALLOC_SPACE_ELEMENTS];
|
||||
|
||||
@@ -20,8 +20,8 @@ struct AllocInfo
|
||||
{
|
||||
size_t elements;
|
||||
};
|
||||
static_assert(sizeof(MallocBlock) <= sizeof(max_align_t));
|
||||
static_assert(sizeof(AllocInfo) <= sizeof(max_align_t));
|
||||
static_assert(sizeof(MallocBlock) <= alignof(max_align_t));
|
||||
static_assert(sizeof(AllocInfo) <= alignof(max_align_t));
|
||||
|
||||
MallocBlock* gNextBlock = []()
|
||||
{
|
||||
|
||||
272
targets/_any/src/drivers/usb.cpp
Normal file
272
targets/_any/src/drivers/usb.cpp
Normal file
@@ -0,0 +1,272 @@
|
||||
|
||||
#include "drivers/usb.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include "drivers/pci.hpp"
|
||||
|
||||
namespace baos::usb
|
||||
{
|
||||
namespace
|
||||
{
|
||||
inline constexpr std::uint8_t USB_PROGIF_UHCI_CONTROLLER = 0x0;
|
||||
inline constexpr std::uint8_t USB_PROGIF_OHCI_CONTROLLER = 0x10;
|
||||
inline constexpr std::uint8_t USB_PROGIF_EHCI_CONTROLLER = 0x20;
|
||||
inline constexpr std::uint8_t USB_PROGIF_XHCI_CONTROLLER = 0x30;
|
||||
|
||||
#define BAOS_MEMORY_REGISTER_RO(offset, type, name) \
|
||||
[[nodiscard]] const Register<type> name() const noexcept { return Register(*reinterpret_cast<type*>(base + offset)); }
|
||||
#define BAOS_MEMORY_REGISTER_RW(offset, type, name) \
|
||||
[[nodiscard]] Register<type> name() const noexcept { return Register(*reinterpret_cast<type*>(base + offset)); }
|
||||
#define BAOS_MEMORY_REGISTER_RO_PARAM(offset, type, name, paramType, paramName) \
|
||||
[[nodiscard]] const Register<type> name(paramType paramName) const noexcept { return Register(*reinterpret_cast<type*>(base + offset)); }
|
||||
#define BAOS_MEMORY_REGISTER_RW_PARAM(offset, type, name, paramType, paramName) \
|
||||
[[nodiscard]] Register<type> name(paramType paramName) const noexcept { return Register(*reinterpret_cast<type*>(base + offset)); }
|
||||
|
||||
struct XHCIHCSParams1
|
||||
{
|
||||
std::uint8_t maxSlots;
|
||||
std::uint16_t maxInterrupts : 11;
|
||||
std::uint8_t reserved : 5;
|
||||
std::uint8_t maxPorts;
|
||||
} __attribute__((packed));
|
||||
static_assert(sizeof(XHCIHCSParams1) == 4);
|
||||
|
||||
#define BAOS_MEMORY_BARRIER() __asm__ __volatile__ ("" : : : "memory")
|
||||
#define BAOS_STORE_FENCE() __asm__ __volatile__ ("sfence" : : : "memory")
|
||||
#define BAOS_LOAD_FENCE() __asm__ __volatile__ ("lfence" : : : "memory")
|
||||
#define BAOS_MEMORY_FENCE() __asm__ __volatile__ ("mfence" : : : "memory")
|
||||
|
||||
template<typename T>
|
||||
class Register
|
||||
{
|
||||
private:
|
||||
T& mMemory;
|
||||
public:
|
||||
explicit Register(T& memory) noexcept : mMemory(memory) {}
|
||||
|
||||
Register& operator=(const T& value) noexcept
|
||||
{
|
||||
set(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator T() const noexcept { return get(); }
|
||||
|
||||
void set(const T& value) noexcept
|
||||
{
|
||||
BAOS_STORE_FENCE();
|
||||
mMemory = value;
|
||||
}
|
||||
|
||||
[[nodiscard]] T get() const noexcept
|
||||
{
|
||||
BAOS_MEMORY_BARRIER();
|
||||
T value = mMemory;
|
||||
BAOS_LOAD_FENCE();
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
struct XHCICapabilityRegisters
|
||||
{
|
||||
std::uint64_t base;
|
||||
|
||||
BAOS_MEMORY_REGISTER_RO(0x00, std::uint8_t, caplength)
|
||||
BAOS_MEMORY_REGISTER_RO(0x02, std::uint16_t, ifaceVersion)
|
||||
BAOS_MEMORY_REGISTER_RO(0x04, XHCIHCSParams1, hcsParams1)
|
||||
BAOS_MEMORY_REGISTER_RO(0x08, std::uint32_t, hcsParams2)
|
||||
BAOS_MEMORY_REGISTER_RO(0x0C, std::uint32_t, hcsParams3)
|
||||
BAOS_MEMORY_REGISTER_RO(0x10, std::uint32_t, hccParams1)
|
||||
BAOS_MEMORY_REGISTER_RO(0x14, std::uint32_t, dbOff)
|
||||
BAOS_MEMORY_REGISTER_RO(0x18, std::uint32_t, rtsOff)
|
||||
BAOS_MEMORY_REGISTER_RO(0x1C, std::uint32_t, hccParams2)
|
||||
BAOS_MEMORY_REGISTER_RO(0x20, std::uint32_t, vtiosOff)
|
||||
};
|
||||
|
||||
struct XHCIUSBCommand
|
||||
{
|
||||
/* 0 */ bool runStop : 1;
|
||||
/* 1 */ bool hostControllerReset : 1;
|
||||
/* 2 */ bool interrupterEnable : 1;
|
||||
/* 3 */ bool hostSystemErrorEnable : 1;
|
||||
/* 4 */ std::uint8_t reserved0 : 3;
|
||||
/* 7 */ bool lightHostControllerReset : 1;
|
||||
/* 8 */ bool controllerSaveState : 1;
|
||||
/* 9 */ bool controllerRestoreState : 1;
|
||||
/* 10 */ bool enableWrapEvent : 1;
|
||||
/* 11 */ bool enableU3MFINDEXStop : 1;
|
||||
/* 12 */ std::uint8_t reserved1 : 1;
|
||||
/* 13 */ bool cemEnable : 1;
|
||||
/* 14 */ bool extendedTBCEnable : 1;
|
||||
/* 15 */ bool extendedTBCTRBStatusEnable : 1;
|
||||
/* 16 */ bool vtioEnable : 1;
|
||||
/* 17 */ std::uint16_t reserved2 : 15;
|
||||
};
|
||||
static_assert(sizeof(XHCIUSBCommand) == 4);
|
||||
|
||||
struct XHCIUSBStatus
|
||||
{
|
||||
/* 0 */ bool hcHalted : 1;
|
||||
/* 1 */ std::uint8_t reserved0 : 1;
|
||||
/* 2 */ bool hostSystemError : 1;
|
||||
/* 3 */ bool eventInterrupt : 1;
|
||||
/* 4 */ bool portChangeDetect : 1;
|
||||
/* 5 */ std::uint8_t reserved1 : 3;
|
||||
/* 8 */ const bool saveStateStatus : 1;
|
||||
/* 9 */ const bool restoreStateStatus : 1;
|
||||
/* 10 */ bool saveRestoreError : 1;
|
||||
/* 11 */ const bool controllerNotReady : 1;
|
||||
/* 12 */ const bool hostControllerError : 1;
|
||||
/* 13 */ std::uint32_t reserved2 : 19;
|
||||
};
|
||||
static_assert(sizeof(XHCIUSBStatus) == 4);
|
||||
|
||||
struct XHCIConfig
|
||||
{
|
||||
std::uint8_t maxDeviceSlotsEnabled : 7;
|
||||
bool u3EntryEnable : 1;
|
||||
bool configurationInformationEnable : 1;
|
||||
std::uint32_t reserved : 22;
|
||||
};
|
||||
static_assert(sizeof(XHCIConfig) == 4);
|
||||
|
||||
struct XHCIOperationalRegisters
|
||||
{
|
||||
std::uint64_t base;
|
||||
|
||||
BAOS_MEMORY_REGISTER_RW(0x00, XHCIUSBCommand, usbCmd)
|
||||
BAOS_MEMORY_REGISTER_RW(0x04, XHCIUSBStatus, usbStatus)
|
||||
BAOS_MEMORY_REGISTER_RO(0x08, std::uint32_t, pageSize)
|
||||
BAOS_MEMORY_REGISTER_RW(0x14, std::uint32_t, dnCtrl)
|
||||
BAOS_MEMORY_REGISTER_RW(0x18, std::uint64_t, crcr)
|
||||
BAOS_MEMORY_REGISTER_RW(0x30, std::uint64_t, dcbaap)
|
||||
BAOS_MEMORY_REGISTER_RW(0x38, XHCIConfig, config)
|
||||
BAOS_MEMORY_REGISTER_RW_PARAM(0x400 + 0x10 * (port-1), std::uint32_t, portSC, std::uint32_t, port)
|
||||
BAOS_MEMORY_REGISTER_RW_PARAM(0x404 + 0x10 * (port-1), std::uint32_t, portPMSC, std::uint32_t, port)
|
||||
BAOS_MEMORY_REGISTER_RO_PARAM(0x408 + 0x10 * (port-1), std::uint32_t, portLI, std::uint32_t, port)
|
||||
BAOS_MEMORY_REGISTER_RW_PARAM(0x40C + 0x10 * (port-1), std::uint32_t, portHLPMC, std::uint32_t, port)
|
||||
};
|
||||
|
||||
struct XHCIHostControllerRuntimeRegisters
|
||||
{
|
||||
std::uint64_t base;
|
||||
|
||||
BAOS_MEMORY_REGISTER_RO(0x00, std::uint32_t, mfIndex)
|
||||
BAOS_MEMORY_REGISTER_RW_PARAM(0x20 + (32 * interrupter), std::uint32_t, iman, std::uint32_t, interrupter)
|
||||
BAOS_MEMORY_REGISTER_RW_PARAM(0x24 + (32 * interrupter), std::uint32_t, imod, std::uint32_t, interrupter)
|
||||
BAOS_MEMORY_REGISTER_RW_PARAM(0x28 + (32 * interrupter), std::uint32_t, erstsz, std::uint32_t, interrupter)
|
||||
BAOS_MEMORY_REGISTER_RW_PARAM(0x30 + (32 * interrupter), std::uint64_t, erstba, std::uint32_t, interrupter)
|
||||
BAOS_MEMORY_REGISTER_RW_PARAM(0x38 + (32 * interrupter), std::uint64_t, erdp, std::uint32_t, interrupter)
|
||||
};
|
||||
|
||||
struct XHCIHostController
|
||||
{
|
||||
XHCICapabilityRegisters capabilityRegs = {};
|
||||
XHCIOperationalRegisters operationalRegs = {};
|
||||
XHCIHostControllerRuntimeRegisters controllerRuntimeRegs = {};
|
||||
|
||||
explicit XHCIHostController(std::uint64_t base) noexcept : capabilityRegs(base)
|
||||
{
|
||||
operationalRegs.base = capabilityRegs.base + capabilityRegs.caplength();
|
||||
controllerRuntimeRegs.base = capabilityRegs.base + capabilityRegs.rtsOff();
|
||||
}
|
||||
XHCIHostController(const XHCIHostController&) noexcept = default;
|
||||
|
||||
XHCIHostController& operator=(const XHCIHostController&) noexcept = default;
|
||||
};
|
||||
|
||||
bool initUHCIController(const pci::Header& pciHeader) noexcept
|
||||
{
|
||||
(void) pciHeader;
|
||||
// TODO: not implemented yet
|
||||
return false;
|
||||
}
|
||||
|
||||
bool initOHCIController(const pci::Header& pciHeader) noexcept
|
||||
{
|
||||
(void) pciHeader;
|
||||
// TODO: not implemented yet
|
||||
return false;
|
||||
}
|
||||
|
||||
bool initEHCIController(const pci::Header& pciHeader) noexcept
|
||||
{
|
||||
(void) pciHeader;
|
||||
// TODO: not implemented yet
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
std::array<void*, 32> gDeviceContextBasePointers alignas(64);
|
||||
|
||||
bool initXHCIController(const pci::Header& pciHeader) noexcept
|
||||
{
|
||||
return false;
|
||||
|
||||
pci::GeneralDeviceHeader gdHeader = {};
|
||||
if (!pci::getGeneralDeviceHeader(pciHeader, gdHeader)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::uint64_t registerMemory = static_cast<std::uint64_t>(gdHeader.bar1) << 32 | gdHeader.bar0;
|
||||
std::printf("XHCI register memory: 0x%lX\n", registerMemory);
|
||||
|
||||
XHCIHostController controller(registerMemory);
|
||||
// see chapter 4.2 (Host Controller Initialization) of the XHCI spec
|
||||
// https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/extensible-host-controler-interface-usb-xhci.pdf
|
||||
|
||||
// 1. wait until the controller is ready
|
||||
while (controller.operationalRegs.usbStatus().get().controllerNotReady);
|
||||
|
||||
// 2. set maximum enabled slots
|
||||
const XHCIHCSParams1 hcsParams1 = controller.capabilityRegs.hcsParams1();
|
||||
XHCIConfig config = controller.operationalRegs.config();
|
||||
config.maxDeviceSlotsEnabled = std::min(hcsParams1.maxSlots, static_cast<std::uint8_t>(gDeviceContextBasePointers.size() - 1));
|
||||
controller.operationalRegs.config() = config;
|
||||
|
||||
// 3. setup the device context base address pointer
|
||||
const std::uint64_t pointer = std::bit_cast<std::uint64_t>(gDeviceContextBasePointers.data());
|
||||
assert((pointer & 0b11111) == 0);
|
||||
controller.operationalRegs.dcbaap() = pointer;
|
||||
|
||||
XHCIUSBCommand cmd = controller.operationalRegs.usbCmd();
|
||||
cmd.runStop = true;
|
||||
controller.operationalRegs.usbCmd() = cmd;
|
||||
|
||||
cmd = controller.operationalRegs.usbCmd();
|
||||
while (true)
|
||||
{
|
||||
const XHCIUSBStatus status = controller.operationalRegs.usbStatus();
|
||||
if (!status.hcHalted) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::printf("MaxSlots: %d, MaxInterrupts: %d, MaxPorts: %d\n", hcsParams1.maxSlots, hcsParams1.maxInterrupts, hcsParams1.maxPorts);
|
||||
//const XHCIOperationalRegisters operationalRegisters{registerMemory + capabilityRegisters.caplength()};
|
||||
|
||||
// TODO: not implemented yet
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool initController(const pci::Header& pciHeader) noexcept
|
||||
{
|
||||
switch (pciHeader.progIf)
|
||||
{
|
||||
case USB_PROGIF_UHCI_CONTROLLER:
|
||||
return initUHCIController(pciHeader);
|
||||
case USB_PROGIF_OHCI_CONTROLLER:
|
||||
return initOHCIController(pciHeader);
|
||||
case USB_PROGIF_EHCI_CONTROLLER:
|
||||
return initEHCIController(pciHeader);
|
||||
case USB_PROGIF_XHCI_CONTROLLER:
|
||||
return initXHCIController(pciHeader);
|
||||
default:
|
||||
return false; // what is this?
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <cstring>
|
||||
#include "libs/psf.hpp"
|
||||
#include "os/memory.hpp"
|
||||
|
||||
namespace draw
|
||||
{
|
||||
|
||||
@@ -31,6 +31,15 @@ void iprintf(const char* format, ...) noexcept
|
||||
PrintFHelper<InterruptPrinter>().vprintf(format, parameters);
|
||||
va_end(parameters);
|
||||
}
|
||||
|
||||
__attribute__((no_caller_saved_registers))
|
||||
void splitPageToIndices(std::uint64_t page, std::uint16_t& outPML4Entry, std::uint16_t& outPDPEntry, std::uint16_t& outPDEntry, std::uint16_t& outPTEntry) noexcept
|
||||
{
|
||||
outPML4Entry = (page >> 27);
|
||||
outPDPEntry = (page >> 18) & 0x1FF;
|
||||
outPDEntry = (page >> 9) & 0x1FF;
|
||||
outPTEntry = page & 0x1FF;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" [[noreturn]] void __halt();
|
||||
@@ -110,6 +119,37 @@ void isrGeneralProtectionFault(InterruptFrame* interruptFrame, interrupt_error_c
|
||||
__attribute__((interrupt))
|
||||
void isrPageFault(InterruptFrame* interruptFrame, interrupt_error_code_t errorCode) noexcept
|
||||
{
|
||||
static constexpr std::uint64_t PRESENT_BIT = (1 << 0);
|
||||
static constexpr std::uint64_t WRITE_BIT = (1 << 1);
|
||||
static constexpr std::uint64_t USER_BIT = (1 << 2);
|
||||
static constexpr std::uint64_t RESERVED_WRITE_BIT = (1 << 3);
|
||||
static constexpr std::uint64_t INSTRUCTION_FETCH_BIT = (1 << 4);
|
||||
static constexpr std::uint64_t PROTECTION_KEY_BIT = (1 << 5);
|
||||
static constexpr std::uint64_t SHADOW_STACK_BIT = (1 << 6);
|
||||
static constexpr std::uint64_t SGX_BIT = (1 << 7);
|
||||
|
||||
std::uint64_t cr2 = 0;
|
||||
__asm__ __volatile__(
|
||||
"mov %%cr2, %0"
|
||||
: "=a"(cr2)
|
||||
:
|
||||
);
|
||||
iprintf("address: 0x%lX\n", cr2);
|
||||
std::uint16_t pml4Index = 0;
|
||||
std::uint16_t pdpIndex = 0;
|
||||
std::uint16_t pdIndex = 0;
|
||||
std::uint16_t ptIndex = 0;
|
||||
splitPageToIndices(cr2 >> 12, pml4Index, pdpIndex, pdIndex, ptIndex);
|
||||
iprintf("page: %d.%d.%d.%d\n", pml4Index, pdpIndex, pdIndex, ptIndex);
|
||||
|
||||
iprintf("present: %s\n", (errorCode & PRESENT_BIT) ? "yes" : "no");
|
||||
iprintf("access: %s\n", (errorCode & WRITE_BIT) ? "write" : "read");
|
||||
iprintf("mode: %s\n", (errorCode & USER_BIT) ? "user" : "supervisor");
|
||||
iprintf("reserved write: %s\n", (errorCode & RESERVED_WRITE_BIT) ? "yes" : "no");
|
||||
iprintf("instruction fetch: %s\n", (errorCode & INSTRUCTION_FETCH_BIT) ? "yes" : "no");
|
||||
iprintf("protection key: %s\n", (errorCode & PROTECTION_KEY_BIT) ? "yes" : "no");
|
||||
iprintf("shadow stack: %s\n", (errorCode & SHADOW_STACK_BIT) ? "yes" : "no");
|
||||
iprintf("software guard extensions: %s\n", (errorCode & SGX_BIT) ? "yes" : "no");
|
||||
handleException("PageFault", interruptFrame);
|
||||
}
|
||||
|
||||
|
||||
326
targets/_any/src/os/memory.cpp
Normal file
326
targets/_any/src/os/memory.cpp
Normal file
@@ -0,0 +1,326 @@
|
||||
|
||||
#include "os/memory.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
|
||||
namespace baos
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using PageTable = std::array<PageTableEntry, 512>;
|
||||
constinit PageTable gPageMapLevel4 alignas(4096);
|
||||
|
||||
struct MemoryRangeAllocationInfo
|
||||
{
|
||||
std::array<std::uint8_t, 4086> usedPages;
|
||||
std::uint16_t numFreePages = 0;
|
||||
MemoryRangeAllocationInfo* next = nullptr;
|
||||
};
|
||||
static_assert(sizeof(MemoryRangeAllocationInfo) == 4096);
|
||||
|
||||
inline constexpr unsigned MAX_USABLE_RANGES = 32;
|
||||
std::array<MemoryRange, MAX_USABLE_RANGES> gUsableMemoryRanges;
|
||||
std::array<MemoryRangeAllocationInfo, MAX_USABLE_RANGES> gRangeAllocationInfo;
|
||||
std::uint8_t gNumUsableRanges = 0;
|
||||
|
||||
// initial 2MB page for setting up the page table
|
||||
// std::array<std::uint8_t, 2ul << 20> gInitPage alignas(2ul << 20);
|
||||
std::span<std::uint8_t> gPageTableSpace;
|
||||
std::span<std::uint8_t> gNextPageTableSpace;
|
||||
|
||||
void splitPageToIndices(std::uint64_t page, std::uint16_t& outPML4Entry, std::uint16_t& outPDPEntry, std::uint16_t& outPDEntry, std::uint16_t& outPTEntry) noexcept
|
||||
{
|
||||
outPML4Entry = (page >> 27);
|
||||
outPDPEntry = (page >> 18) & 0x1FF;
|
||||
outPDEntry = (page >> 9) & 0x1FF;
|
||||
outPTEntry = page & 0x1FF;
|
||||
}
|
||||
|
||||
PageTable& allocatePageTable() noexcept
|
||||
{
|
||||
PageTable* pageTable = std::bit_cast<PageTable*>(&*gPageTableSpace.begin());
|
||||
if (!pageTable)
|
||||
{
|
||||
int i = 5;
|
||||
}
|
||||
::new(pageTable) PageTable;
|
||||
gPageTableSpace = {gPageTableSpace.begin() + sizeof(PageTable), gPageTableSpace.end()};
|
||||
return *pageTable;
|
||||
}
|
||||
|
||||
PageTableEntry& getOrCreatePageTableEntry(PageTable& pageTable, std::uint16_t index)
|
||||
{
|
||||
PageTableEntry& entry = pageTable[index];
|
||||
if (!entry.present)
|
||||
{
|
||||
PageTable& childTable = allocatePageTable();
|
||||
entry.address = std::bit_cast<std::uint64_t>(&childTable) >> 12;
|
||||
entry.writable = true;
|
||||
entry.present = true;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
PageTableEntry& getOrCreatePage(std::uint64_t page, bool bigPage = false)
|
||||
{
|
||||
std::uint16_t pml4Index = 0;
|
||||
std::uint16_t pdpIndex = 0;
|
||||
std::uint16_t pdIndex = 0;
|
||||
std::uint16_t ptIndex = 0;
|
||||
splitPageToIndices(page, pml4Index, pdpIndex, pdIndex, ptIndex);
|
||||
|
||||
PageTableEntry& pml4Entry = getOrCreatePageTableEntry(gPageMapLevel4, pml4Index);
|
||||
|
||||
PageTable& pdp = *std::bit_cast<PageTable*>(pml4Entry.address << 12);
|
||||
PageTableEntry& pdpEntry = getOrCreatePageTableEntry(pdp, pdpIndex);
|
||||
|
||||
PageTable& pd = *std::bit_cast<PageTable*>(pdpEntry.address << 12);
|
||||
PageTableEntry& pdEntry = getOrCreatePageTableEntry(pd, pdIndex);
|
||||
|
||||
if (bigPage)
|
||||
{
|
||||
pdEntry.pageSize = true;
|
||||
return pdEntry;
|
||||
}
|
||||
|
||||
PageTable& pt = *std::bit_cast<PageTable*>(pdEntry.address << 12);
|
||||
return getOrCreatePageTableEntry(pt, ptIndex);
|
||||
}
|
||||
|
||||
void addUsableRange(const MemoryRange& range) noexcept
|
||||
{
|
||||
if (gNumUsableRanges >= MAX_USABLE_RANGES) {
|
||||
return;
|
||||
}
|
||||
gUsableMemoryRanges[gNumUsableRanges] = range;
|
||||
gRangeAllocationInfo[gNumUsableRanges].numFreePages = range.numPages;
|
||||
++gNumUsableRanges;
|
||||
}
|
||||
|
||||
std::uint64_t findFreePages(unsigned numConsecutive) noexcept
|
||||
{
|
||||
if (numConsecutive == 0 || numConsecutive > 4086) {
|
||||
return 0;
|
||||
}
|
||||
if (numConsecutive != 1) {
|
||||
return 0; // TODO!!!
|
||||
}
|
||||
|
||||
for (std::uint8_t rangeIdx = 0; rangeIdx < gNumUsableRanges; ++rangeIdx)
|
||||
{
|
||||
std::uint64_t page = gUsableMemoryRanges[rangeIdx].pageBase;
|
||||
for (MemoryRangeAllocationInfo* allocInfo = &gRangeAllocationInfo[rangeIdx]; allocInfo != nullptr; page += allocInfo->usedPages.size(), allocInfo = allocInfo->next)
|
||||
{
|
||||
if (allocInfo->numFreePages < numConsecutive) {
|
||||
continue;
|
||||
}
|
||||
for (unsigned pagesIdx = 0; pagesIdx < allocInfo->usedPages.size(); ++pagesIdx)
|
||||
{
|
||||
if (allocInfo->usedPages[pagesIdx] != 0xFF)
|
||||
{
|
||||
for (unsigned bit = 0; bit < 8; ++bit)
|
||||
{
|
||||
if ((allocInfo->usedPages[pagesIdx] & (1 << bit)) == 0)
|
||||
{
|
||||
allocInfo->usedPages[pagesIdx] |= (1 << bit);
|
||||
return page + (8 * pagesIdx) + bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
PageTable& setupPageTableEntry(PageTableEntry& entry, std::uint64_t page) noexcept
|
||||
{
|
||||
entry.address = page;
|
||||
entry.present = true;
|
||||
entry.writable = true;
|
||||
return *(::new(std::bit_cast<void*>(entry.address << 12)) PageTable);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void cmdDumpPageTable() noexcept
|
||||
{
|
||||
std::uint64_t pml4Addr = std::bit_cast<std::uint64_t>(&gPageMapLevel4);
|
||||
std::printf("pml4 address: 0x%lX\n", pml4Addr);
|
||||
baos::PageTableEntry* pml4 = std::bit_cast<baos::PageTableEntry*>(pml4Addr);
|
||||
auto printPDE = [](const baos::PageTableEntry& pde)
|
||||
{
|
||||
std::printf("%s%s%s%s%s%s%s%s%s, address=%lX\n",
|
||||
pde.present ? " P" : "",
|
||||
pde.writable ? " W" : "",
|
||||
pde.user ? " U" : "",
|
||||
pde.writeThrough ? " WT" : "",
|
||||
pde.cacheDisabled ? " CD" : "",
|
||||
pde.accessed ? " A" : "",
|
||||
pde.dirty ? " D" : "",
|
||||
pde.pageSize ? " PS" : "",
|
||||
pde.executeDisable ? " XD" : "",
|
||||
pde.address * 4096);
|
||||
};
|
||||
for (unsigned pml4e = 0; pml4e < 512; ++pml4e)
|
||||
{
|
||||
if (!pml4[pml4e].present) {
|
||||
continue;
|
||||
}
|
||||
std::printf("%d]", pml4e);
|
||||
printPDE(pml4[pml4e]);
|
||||
|
||||
baos::PageTableEntry* pdp = std::bit_cast<baos::PageTableEntry*>(pml4[pml4e].address * 4096);
|
||||
for (unsigned pdpe = 0; pdpe < 512; ++pdpe)
|
||||
{
|
||||
if (!pdp[pdpe].present) {
|
||||
continue;
|
||||
}
|
||||
std::printf("%d.%d]", pml4e, pdpe);
|
||||
printPDE(pdp[pdpe]);
|
||||
|
||||
baos::PageTableEntry* pd = std::bit_cast<baos::PageTableEntry*>(pdp[pdpe].address * 4096);
|
||||
for (unsigned pde = 0; pde < 512; ++pde)
|
||||
{
|
||||
if (!pd[pde].present) {
|
||||
continue;
|
||||
}
|
||||
std::printf("%d.%d.%d]", pml4e, pdpe, pde);
|
||||
printPDE(pd[pde]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool canFitPageTableSpace(MemoryRange range, std::uint64_t& outBase) noexcept
|
||||
{
|
||||
if (range.type != MemoryType::USABLE) {
|
||||
return false;
|
||||
}
|
||||
const std::uint64_t possibleBase = std::alignUp(range.pageBase, 512ul); // needs to be 2MB aligned
|
||||
if (range.numPages - (possibleBase - range.pageBase) >= 512)
|
||||
{
|
||||
outBase = possibleBase;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void setupPaging(std::span<const MemoryRange> memoryRanges) noexcept
|
||||
{
|
||||
// addUsableRange({
|
||||
// .pageBase = std::bit_cast<std::uint64_t>(gInitPages.data()) >> 12,
|
||||
// .numPages = gInitPages.size() / 4096,
|
||||
// .type = MemoryType::USABLE
|
||||
// });
|
||||
std::uint64_t firstKernelPage = std::bit_cast<std::uint64_t>(&gKernelStart) / 4096;
|
||||
std::uint64_t lastKernelPage = std::bit_cast<std::uint64_t>(&gKernelEnd) / 4096 + 1;
|
||||
for (MemoryRange range : memoryRanges)
|
||||
{
|
||||
if (range.type == MemoryType::USABLE) // ignore anything before the kernel
|
||||
{
|
||||
if (range.pageBase < lastKernelPage)
|
||||
{
|
||||
if (range.numPages < (lastKernelPage - range.pageBase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
range.numPages -= (lastKernelPage - range.pageBase);
|
||||
range.pageBase = lastKernelPage;
|
||||
}
|
||||
addUsableRange(range);
|
||||
|
||||
std::uint64_t tableSpaceBase = 0;
|
||||
if (gPageTableSpace.empty() && canFitPageTableSpace(range, tableSpaceBase))
|
||||
{
|
||||
std::uint8_t* tableSpacePtr = std::bit_cast<std::uint8_t*>(tableSpaceBase * 4096);
|
||||
gPageTableSpace = {tableSpacePtr, 2ul << 30};
|
||||
setupIdentityPaging2M(tableSpaceBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
// start by setting up identity paging for the kernel
|
||||
// __get_cpuid()
|
||||
setupIdentityPaging(firstKernelPage, lastKernelPage);
|
||||
// std::printf("Kernel pages: %ld-%ld\n", firstKernelPage, lastKernelPage);
|
||||
|
||||
// setup identity paging for all reserved ranges so we can continue using them
|
||||
// for (const MemoryRange& range : memoryRanges)
|
||||
// {
|
||||
// if (range.type == MemoryType::RESERVED || range.type == MemoryType::MMIO)
|
||||
// {
|
||||
// setupIdentityPaging(range.pageBase, range.pageBase + range.numPages);
|
||||
// }
|
||||
// }
|
||||
|
||||
// cmdDumpPageTable();
|
||||
std::uint64_t cr3 = std::bit_cast<std::uint64_t>(gPageMapLevel4.data());
|
||||
__asm__ __volatile__ (
|
||||
"mov %0, %%cr3"
|
||||
:
|
||||
: "a"(cr3)
|
||||
);
|
||||
}
|
||||
|
||||
const char* memoryTypeName(MemoryType type) noexcept
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case MemoryType::USABLE:
|
||||
return "USABLE";
|
||||
case MemoryType::UNUSABLE:
|
||||
return "UNUSABLE";
|
||||
case MemoryType::MMIO:
|
||||
return "MMIO";
|
||||
case MemoryType::RESERVED:
|
||||
return "RESERVED";
|
||||
}
|
||||
return "<INVALID>";
|
||||
}
|
||||
|
||||
void* allocatePages(unsigned numConsecutive) noexcept
|
||||
{
|
||||
const std::uint64_t firstPage = findFreePages(numConsecutive);
|
||||
if (firstPage == 0)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
setupIdentityPaging(firstPage, firstPage + numConsecutive);
|
||||
return std::bit_cast<void*>(firstPage << 12);
|
||||
}
|
||||
|
||||
|
||||
void setupIdentityPaging(std::uint64_t page) noexcept
|
||||
{
|
||||
PageTableEntry& pageTableEntry = getOrCreatePage(page);
|
||||
pageTableEntry.address = page;
|
||||
}
|
||||
|
||||
void setupIdentityPaging2M(std::uint64_t page) noexcept
|
||||
{
|
||||
PageTableEntry& pageTableEntry = getOrCreatePage(page, true);
|
||||
pageTableEntry.address = page;
|
||||
}
|
||||
|
||||
void setupIdentityPaging(std::uint64_t firstPage, std::uint64_t lastPage) noexcept
|
||||
{
|
||||
for (std::uint64_t page = firstPage; page != lastPage; ++page)
|
||||
{
|
||||
if (page % 512 == 0 && (lastPage - page) >= 512)
|
||||
{
|
||||
setupIdentityPaging2M(page);
|
||||
page += 511; // loop will add the last 1
|
||||
}
|
||||
else
|
||||
{
|
||||
setupIdentityPaging(page);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user