Merge remote-tracking branch 'Kernel/master'

This commit is contained in:
EnderIce2
2024-11-20 05:00:33 +02:00
468 changed files with 112800 additions and 1 deletions

207
Kernel/core/acpi.cpp Normal file
View File

@ -0,0 +1,207 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <acpi.hpp>
#include <debug.h>
#include <io.h>
#include "../kernel.h"
namespace ACPI
{
__no_sanitize("alignment") void *ACPI::FindTable(ACPI::ACPIHeader *ACPIHeader, char *Signature)
{
for (uint64_t t = 0; t < ((ACPIHeader->Length - sizeof(ACPI::ACPIHeader)) / (XSDTSupported ? 8 : 4)); t++)
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
// TODO: Should I be concerned about unaligned memory access?
ACPI::ACPIHeader *SDTHdr = nullptr;
if (XSDTSupported)
SDTHdr = (ACPI::ACPIHeader *)(*(uint64_t *)((uint64_t)ACPIHeader + sizeof(ACPI::ACPIHeader) + (t * 8)));
else
SDTHdr = (ACPI::ACPIHeader *)(*(uint32_t *)((uint64_t)ACPIHeader + sizeof(ACPI::ACPIHeader) + (t * 4)));
#pragma GCC diagnostic pop
size_t signLength = strlen(Signature);
for (size_t i = 0; i < signLength; i++)
{
if (SDTHdr->Signature[i] != Signature[i])
break;
if (i == 3)
{
#ifdef DEBUG
KPrint("ACPI: %.4s [%.6s:%.8s] found at address %#lx",
Signature,
SDTHdr->OEMID,
SDTHdr->OEMTableID,
(uintptr_t)SDTHdr);
#endif
trace("%s found at address %#lx", Signature, (uintptr_t)SDTHdr);
Tables[Signature] = SDTHdr;
return SDTHdr;
}
}
}
// warn("%s not found!", Signature);
return nullptr;
}
void ACPI::SearchTables(ACPIHeader *Header)
{
if (!Header)
return;
HPET = (HPETHeader *)FindTable(Header, (char *)"HPET");
FADT = (FADTHeader *)FindTable(Header, (char *)"FACP");
MCFG = (MCFGHeader *)FindTable(Header, (char *)"MCFG");
BGRT = (BGRTHeader *)FindTable(Header, (char *)"BGRT");
SRAT = (SRATHeader *)FindTable(Header, (char *)"SRAT");
TPM2 = (TPM2Header *)FindTable(Header, (char *)"TPM2");
TCPA = (TCPAHeader *)FindTable(Header, (char *)"TCPA");
WAET = (WAETHeader *)FindTable(Header, (char *)"WAET");
MADT = (MADTHeader *)FindTable(Header, (char *)"APIC");
HEST = (HESTHeader *)FindTable(Header, (char *)"HEST");
SSDT = (SSDTHeader *)FindTable(Header, (char *)"SSDT");
DBGP = (DBGPHeader *)FindTable(Header, (char *)"DBGP");
DBG2 = (DBG2Header *)FindTable(Header, (char *)"DBG2");
FindTable(Header, (char *)"BERT");
FindTable(Header, (char *)"CPEP");
FindTable(Header, (char *)"DSDT");
FindTable(Header, (char *)"ECDT");
FindTable(Header, (char *)"EINJ");
FindTable(Header, (char *)"ERST");
FindTable(Header, (char *)"FACS");
FindTable(Header, (char *)"MSCT");
FindTable(Header, (char *)"MPST");
FindTable(Header, (char *)"OEMx");
FindTable(Header, (char *)"PMTT");
FindTable(Header, (char *)"PSDT");
FindTable(Header, (char *)"RASF");
FindTable(Header, (char *)"RSDT");
FindTable(Header, (char *)"SBST");
FindTable(Header, (char *)"SLIT");
FindTable(Header, (char *)"XSDT");
FindTable(Header, (char *)"DRTM");
FindTable(Header, (char *)"FPDT");
FindTable(Header, (char *)"GTDT");
FindTable(Header, (char *)"PCCT");
FindTable(Header, (char *)"S3PT");
FindTable(Header, (char *)"MATR");
FindTable(Header, (char *)"MSDM");
FindTable(Header, (char *)"WPBT");
FindTable(Header, (char *)"OSDT");
FindTable(Header, (char *)"RSDP");
FindTable(Header, (char *)"NFIT");
FindTable(Header, (char *)"ASF!");
FindTable(Header, (char *)"BOOT");
FindTable(Header, (char *)"CSRT");
FindTable(Header, (char *)"BDAT");
FindTable(Header, (char *)"CDAT");
FindTable(Header, (char *)"DMAR");
FindTable(Header, (char *)"IBFT");
FindTable(Header, (char *)"IORT");
FindTable(Header, (char *)"IVRS");
FindTable(Header, (char *)"LPIT");
FindTable(Header, (char *)"MCHI");
FindTable(Header, (char *)"MTMR");
FindTable(Header, (char *)"SLIC");
FindTable(Header, (char *)"SPCR");
FindTable(Header, (char *)"SPMI");
FindTable(Header, (char *)"UEFI");
FindTable(Header, (char *)"VRTC");
FindTable(Header, (char *)"WDAT");
FindTable(Header, (char *)"WDDT");
FindTable(Header, (char *)"WDRT");
FindTable(Header, (char *)"ATKG");
FindTable(Header, (char *)"GSCI");
FindTable(Header, (char *)"IEIT");
FindTable(Header, (char *)"HMAT");
FindTable(Header, (char *)"CEDT");
FindTable(Header, (char *)"AEST");
FindTable(Header, (char *)"AGDI");
FindTable(Header, (char *)"APMT");
FindTable(Header, (char *)"ETDT");
FindTable(Header, (char *)"MPAM");
FindTable(Header, (char *)"PDTT");
FindTable(Header, (char *)"PPTT");
FindTable(Header, (char *)"RAS2");
FindTable(Header, (char *)"SDEI");
FindTable(Header, (char *)"STAO");
FindTable(Header, (char *)"XENV");
FindTable(Header, (char *)"PHAT");
FindTable(Header, (char *)"SVKL");
FindTable(Header, (char *)"UUID");
FindTable(Header, (char *)"CCEL");
FindTable(Header, (char *)"WSMT");
FindTable(Header, (char *)"PRMT");
FindTable(Header, (char *)"NBFT");
FindTable(Header, (char *)"RSD PTR");
FindTable(Header, (char *)"TDX");
FindTable(Header, (char *)"CXL");
FindTable(Header, (char *)"DSD");
FindTable(Header, (char *)"BSA");
}
ACPI::ACPI()
{
trace("Initializing ACPI");
if (!bInfo.RSDP)
{
error("RSDP not found!");
return;
}
if (bInfo.RSDP->Revision >= 2 && bInfo.RSDP->XSDTAddress)
{
debug("XSDT supported");
XSDTSupported = true;
XSDT = (ACPIHeader *)(bInfo.RSDP->XSDTAddress);
}
else
{
debug("RSDT supported");
XSDT = (ACPIHeader *)(uintptr_t)bInfo.RSDP->RSDTAddress;
}
if (!Memory::Virtual().Check(XSDT))
{
warn("%s is not mapped!",
XSDTSupported ? "XSDT" : "RSDT");
debug("XSDT: %p", XSDT);
Memory::Virtual().Map(XSDT, XSDT, Memory::RW);
}
this->SearchTables(XSDT);
if (FADT)
{
outb(s_cst(uint16_t, FADT->SMI_CommandPort), FADT->AcpiEnable);
/* TODO: Sleep for ~5 seconds before polling PM1a CB? */
while (!(inw(s_cst(uint16_t, FADT->PM1aControlBlock)) & 1))
CPU::Pause();
}
}
ACPI::~ACPI()
{
}
}

348
Kernel/core/console.cpp Normal file
View File

@ -0,0 +1,348 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <kcon.hpp>
#include <memory.hpp>
#include <stropts.h>
#include <string.h>
#include <ini.h>
#include "../kernel.h"
namespace KernelConsole
{
int TermColors[] = {
[TerminalColor::BLACK] = 0x000000,
[TerminalColor::RED] = 0xAA0000,
[TerminalColor::GREEN] = 0x00AA00,
[TerminalColor::YELLOW] = 0xAA5500,
[TerminalColor::BLUE] = 0x0000AA,
[TerminalColor::MAGENTA] = 0xAA00AA,
[TerminalColor::CYAN] = 0x00AAAA,
[TerminalColor::GREY] = 0xAAAAAA,
};
int TermBrightColors[] = {
[TerminalColor::BLACK] = 0x858585,
[TerminalColor::RED] = 0xFF5555,
[TerminalColor::GREEN] = 0x55FF55,
[TerminalColor::YELLOW] = 0xFFFF55,
[TerminalColor::BLUE] = 0x5555FF,
[TerminalColor::MAGENTA] = 0xFF55FF,
[TerminalColor::CYAN] = 0x55FFFF,
[TerminalColor::GREY] = 0xFFFFFF,
};
__no_sanitize("undefined") char FontRenderer::Paint(long CellX, long CellY, char Char, uint32_t Foreground, uint32_t Background)
{
uint64_t x = CellX * CurrentFont->GetInfo().Width;
uint64_t y = CellY * CurrentFont->GetInfo().Height;
switch (CurrentFont->GetInfo().Type)
{
case Video::FontType::PCScreenFont1:
{
uint32_t *PixelPtr = (uint32_t *)Display->GetBuffer;
char *FontPtr = (char *)CurrentFont->GetInfo().PSF1Font->GlyphBuffer + (Char * CurrentFont->GetInfo().PSF1Font->Header->charsize);
for (uint64_t Y = 0; Y < 16; Y++)
{
for (uint64_t X = 0; X < 8; X++)
{
if ((*FontPtr & (0b10000000 >> X)) > 0)
*(unsigned int *)(PixelPtr + (x + X) + ((y + Y) * Display->GetWidth)) = Foreground;
else
*(unsigned int *)(PixelPtr + (x + X) + ((y + Y) * Display->GetWidth)) = Background;
}
FontPtr++;
}
break;
}
case Video::FontType::PCScreenFont2:
{
Video::FontInfo fInfo = CurrentFont->GetInfo();
int BytesPerLine = (fInfo.PSF2Font->Header->width + 7) / 8;
char *FontAddress = (char *)fInfo.StartAddress;
uint32_t FontHeaderSize = fInfo.PSF2Font->Header->headersize;
uint32_t FontCharSize = fInfo.PSF2Font->Header->charsize;
uint32_t FontLength = fInfo.PSF2Font->Header->length;
char *FontPtr = FontAddress + FontHeaderSize + (Char > 0 && (uint32_t)Char < FontLength ? Char : 0) * FontCharSize;
uint32_t FontHdrWidth = fInfo.PSF2Font->Header->width;
uint32_t FontHdrHeight = fInfo.PSF2Font->Header->height;
for (uint32_t Y = 0; Y < FontHdrHeight; Y++)
{
for (uint32_t X = 0; X < FontHdrWidth; X++)
{
void *FramebufferAddress = (void *)((uintptr_t)Display->GetBuffer +
((y + Y) * Display->GetWidth + (x + X)) *
(Display->GetFramebufferStruct().BitsPerPixel / 8));
if ((*FontPtr & (0b10000000 >> (X % 8))) > 0)
*(uint32_t *)FramebufferAddress = Foreground;
else
*(uint32_t *)FramebufferAddress = Background;
}
FontPtr += BytesPerLine;
}
break;
}
default:
warn("Unsupported font type");
break;
}
return Char;
}
FontRenderer Renderer;
void paint_callback(TerminalCell *cell, long x, long y)
{
if (cell->attr.Bright)
Renderer.Paint(x, y, cell->c, TermBrightColors[cell->attr.Foreground], TermColors[cell->attr.Background]);
else
Renderer.Paint(x, y, cell->c, TermColors[cell->attr.Foreground], TermColors[cell->attr.Background]);
}
void cursor_callback(TerminalCursor *cur)
{
Renderer.Cursor = {cur->X, cur->Y};
}
VirtualTerminal *Terminals[16] = {nullptr};
std::atomic<VirtualTerminal *> CurrentTerminal = nullptr;
bool SetTheme(std::string Theme)
{
FileNode *rn = fs->GetByPath("/etc/term", thisProcess->Info.RootNode);
if (rn == nullptr)
return false;
kstat st{};
rn->Stat(&st);
char *sh = new char[st.Size];
rn->Read(sh, st.Size, 0);
ini_t *ini = ini_load(sh, NULL);
int themeSection, c0, c1, c2, c3, c4, c5, c6, c7, colorsIdx;
const char *colors[8];
debug("Loading terminal theme: \"%s\"", Theme.c_str());
themeSection = ini_find_section(ini, Theme.c_str(), NULL);
if (themeSection == INI_NOT_FOUND)
{
ini_destroy(ini);
delete[] sh;
return false;
}
auto getColorComponent = [](const char *str, int &index) -> int
{
int value = 0;
while (str[index] >= '0' && str[index] <= '9')
{
value = value * 10 + (str[index] - '0');
++index;
}
return value;
};
auto parseColor = [getColorComponent](const char *colorStr) -> unsigned int
{
int index = 0;
int r = getColorComponent(colorStr, index);
if (colorStr[index] == ',')
++index;
int g = getColorComponent(colorStr, index);
if (colorStr[index] == ',')
++index;
int b = getColorComponent(colorStr, index);
return (r << 16) | (g << 8) | b;
};
c0 = ini_find_property(ini, themeSection, "color0", NULL);
c1 = ini_find_property(ini, themeSection, "color1", NULL);
c2 = ini_find_property(ini, themeSection, "color2", NULL);
c3 = ini_find_property(ini, themeSection, "color3", NULL);
c4 = ini_find_property(ini, themeSection, "color4", NULL);
c5 = ini_find_property(ini, themeSection, "color5", NULL);
c6 = ini_find_property(ini, themeSection, "color6", NULL);
c7 = ini_find_property(ini, themeSection, "color7", NULL);
colors[0] = ini_property_value(ini, themeSection, c0);
colors[1] = ini_property_value(ini, themeSection, c1);
colors[2] = ini_property_value(ini, themeSection, c2);
colors[3] = ini_property_value(ini, themeSection, c3);
colors[4] = ini_property_value(ini, themeSection, c4);
colors[5] = ini_property_value(ini, themeSection, c5);
colors[6] = ini_property_value(ini, themeSection, c6);
colors[7] = ini_property_value(ini, themeSection, c7);
colorsIdx = 0;
for (auto color : colors)
{
colorsIdx++;
if (color == 0)
continue;
char *delimiterPos = strchr(color, ':');
if (delimiterPos == NULL)
continue;
char colorStr[20], colorBrightStr[20];
strncpy(colorStr, color, delimiterPos - color);
colorStr[delimiterPos - color] = '\0';
strcpy(colorBrightStr, delimiterPos + 1);
TermColors[colorsIdx - 1] = parseColor(colorStr);
TermBrightColors[colorsIdx - 1] = parseColor(colorBrightStr);
}
ini_destroy(ini);
delete[] sh;
return true;
}
#ifdef DEBUG
void __test_themes()
{
printf("\x1b[H\x1b[2J");
auto testTheme = [](const char *theme)
{
KernelConsole::SetTheme("vga");
printf("== Theme: \"%s\" ==\n", theme);
KernelConsole::SetTheme(theme);
const char *txtColors[] = {
"30", "31", "32", "33", "34", "35", "36", "37", "39"};
const char *bgColors[] = {
"40", "41", "42", "43", "44", "45", "46", "47", "49"};
const int numTxtColors = sizeof(txtColors) / sizeof(txtColors[0]);
const int numBgColors = sizeof(bgColors) / sizeof(bgColors[0]);
const char *msg = "H";
for (int i = 0; i < numTxtColors; i++)
for (int j = 0; j < numBgColors; j++)
printf("\x1b[%sm\x1b[%sm%s\x1b[0m", txtColors[i], bgColors[j], msg);
for (int i = 0; i < numTxtColors; i++)
for (int j = 0; j < numBgColors; j++)
printf("\x1b[1;%sm\x1b[1;%sm%s\x1b[0m", txtColors[i], bgColors[j], msg);
KernelConsole::SetTheme("vga");
printf("\n");
};
testTheme("vga");
testTheme("breeze");
testTheme("coolbreeze");
testTheme("softlight");
testTheme("calmsea");
testTheme("warmember");
// CPU::Stop();
}
#endif
void EarlyInit()
{
Renderer.CurrentFont = new Video::Font(&_binary_files_tamsyn_font_1_11_Tamsyn7x14r_psf_start,
&_binary_files_tamsyn_font_1_11_Tamsyn7x14r_psf_end,
Video::FontType::PCScreenFont2);
size_t Rows = Display->GetWidth / Renderer.CurrentFont->GetInfo().Width;
size_t Cols = Display->GetHeight / Renderer.CurrentFont->GetInfo().Height;
debug("Terminal size: %ux%u", Rows, Cols);
Terminals[0] = new VirtualTerminal(Rows, Cols, Display->GetWidth, Display->GetHeight, paint_callback, cursor_callback);
Terminals[0]->Clear(0, 0, Rows, Cols - 1);
CurrentTerminal.store(Terminals[0], std::memory_order_release);
}
void LateInit()
{
FileNode *rn = fs->GetByPath("/etc/term", thisProcess->Info.RootNode);
if (rn == nullptr)
return;
kstat st{};
rn->Stat(&st);
char *sh = new char[st.Size];
rn->Read(sh, st.Size, 0);
ini_t *ini = ini_load(sh, NULL);
int general = ini_find_section(ini, "general", NULL);
int cursor = ini_find_section(ini, "cursor", NULL);
assert(general != INI_NOT_FOUND && cursor != INI_NOT_FOUND);
int themeIndex = ini_find_property(ini, general, "theme", NULL);
assert(themeIndex != INI_NOT_FOUND);
int cursorColor = ini_find_property(ini, cursor, "color", NULL);
int cursorBlink = ini_find_property(ini, cursor, "blink", NULL);
assert(cursorColor != INI_NOT_FOUND && cursorBlink != INI_NOT_FOUND);
const char *colorThemeStr = ini_property_value(ini, general, themeIndex);
const char *cursorColorStr = ini_property_value(ini, cursor, cursorColor);
const char *cursorBlinkStr = ini_property_value(ini, cursor, cursorBlink);
debug("colorThemeStr=%s", colorThemeStr);
debug("cursorColorStr=%s", cursorColorStr);
debug("cursorBlinkStr=%s", cursorBlinkStr);
auto getColorComponent = [](const char *str, int &index) -> int
{
int value = 0;
while (str[index] >= '0' && str[index] <= '9')
{
value = value * 10 + (str[index] - '0');
++index;
}
return value;
};
auto parseColor = [getColorComponent](const char *colorStr) -> unsigned int
{
int index = 0;
int r = getColorComponent(colorStr, index);
if (colorStr[index] == ',')
++index;
int g = getColorComponent(colorStr, index);
if (colorStr[index] == ',')
++index;
int b = getColorComponent(colorStr, index);
return (r << 16) | (g << 8) | b;
};
if (colorThemeStr != 0)
SetTheme(colorThemeStr);
if (cursorBlinkStr != 0 && strncmp(cursorBlinkStr, "true", 4) == 0)
{
uint32_t blinkColor = 0xFFFFFF;
if (cursorColorStr != 0)
blinkColor = parseColor(cursorColorStr);
fixme("cursor blink with colors %X", blinkColor);
}
ini_destroy(ini);
delete[] sh;
#ifdef DEBUG
// __test_themes();
#endif
}
}

551
Kernel/core/cpu.cpp Normal file
View File

@ -0,0 +1,551 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <cpu.hpp>
#include <memory.hpp>
#include <convert.h>
#include <debug.h>
#include <smp.hpp>
#include "../kernel.h"
#if defined(a64)
using namespace CPU::x64;
#elif defined(a32)
using namespace CPU::x32;
#elif defined(aa64)
#endif
namespace CPU
{
static bool SSEEnabled = false;
const char *Vendor()
{
static char Vendor[13] = {0};
if (Vendor[0] != 0)
return Vendor;
#if defined(a64)
uint32_t eax, ebx, ecx, edx;
x64::cpuid(0x0, &eax, &ebx, &ecx, &edx);
memcpy(Vendor + 0, &ebx, 4);
memcpy(Vendor + 4, &edx, 4);
memcpy(Vendor + 8, &ecx, 4);
#elif defined(a32)
uint32_t eax, ebx, ecx, edx;
x32::cpuid(0x0, &eax, &ebx, &ecx, &edx);
memcpy(Vendor + 0, &ebx, 4);
memcpy(Vendor + 4, &edx, 4);
memcpy(Vendor + 8, &ecx, 4);
#elif defined(aa64)
#error "Not implemented"
#endif
return Vendor;
}
const char *Name()
{
static char Name[49] = {0};
if (Name[0] != 0)
return Name;
#if defined(a64)
uint32_t eax, ebx, ecx, edx;
x64::cpuid(0x80000002, &eax, &ebx, &ecx, &edx);
memcpy(Name + 0, &eax, 4);
memcpy(Name + 4, &ebx, 4);
memcpy(Name + 8, &ecx, 4);
memcpy(Name + 12, &edx, 4);
x64::cpuid(0x80000003, &eax, &ebx, &ecx, &edx);
memcpy(Name + 16, &eax, 4);
memcpy(Name + 20, &ebx, 4);
memcpy(Name + 24, &ecx, 4);
memcpy(Name + 28, &edx, 4);
x64::cpuid(0x80000004, &eax, &ebx, &ecx, &edx);
memcpy(Name + 32, &eax, 4);
memcpy(Name + 36, &ebx, 4);
memcpy(Name + 40, &ecx, 4);
memcpy(Name + 44, &edx, 4);
#elif defined(a32)
uint32_t eax, ebx, ecx, edx;
x32::cpuid(0x80000002, &eax, &ebx, &ecx, &edx);
memcpy(Name + 0, &eax, 4);
memcpy(Name + 4, &ebx, 4);
memcpy(Name + 8, &ecx, 4);
memcpy(Name + 12, &edx, 4);
x32::cpuid(0x80000003, &eax, &ebx, &ecx, &edx);
memcpy(Name + 16, &eax, 4);
memcpy(Name + 20, &ebx, 4);
memcpy(Name + 24, &ecx, 4);
memcpy(Name + 28, &edx, 4);
x32::cpuid(0x80000004, &eax, &ebx, &ecx, &edx);
memcpy(Name + 32, &eax, 4);
memcpy(Name + 36, &ebx, 4);
memcpy(Name + 40, &ecx, 4);
memcpy(Name + 44, &edx, 4);
#elif defined(aa64)
#error "Not implemented"
#endif
return Name;
}
const char *Hypervisor()
{
static char Hypervisor[13] = {0};
if (Hypervisor[0] != 0)
return Hypervisor;
#if defined(a64)
uint32_t eax, ebx, ecx, edx;
x64::cpuid(0x1, &eax, &ebx, &ecx, &edx);
if (!(ecx & (1 << 31))) /* Intel & AMD are the same */
{
Hypervisor[0] = 'N';
Hypervisor[1] = 'o';
Hypervisor[2] = 'n';
Hypervisor[3] = 'e';
return Hypervisor;
}
x64::cpuid(0x40000000, &eax, &ebx, &ecx, &edx);
memcpy(Hypervisor + 0, &ebx, 4);
memcpy(Hypervisor + 4, &ecx, 4);
memcpy(Hypervisor + 8, &edx, 4);
#elif defined(a32)
uint32_t eax, ebx, ecx, edx;
x32::cpuid(0x1, &eax, &ebx, &ecx, &edx);
if (!(ecx & (1 << 31))) /* Intel & AMD are the same */
{
Hypervisor[0] = 'N';
Hypervisor[1] = 'o';
Hypervisor[2] = 'n';
Hypervisor[3] = 'e';
return Hypervisor;
}
x32::cpuid(0x40000000, &eax, &ebx, &ecx, &edx);
memcpy(Hypervisor + 0, &ebx, 4);
memcpy(Hypervisor + 4, &ecx, 4);
memcpy(Hypervisor + 8, &edx, 4);
#elif defined(aa64)
#error "Not implemented"
#endif
return Hypervisor;
}
bool Interrupts(InterruptsType Type)
{
switch (Type)
{
case Check:
{
uintptr_t Flags;
#if defined(a64)
asmv("pushfq");
asmv("popq %0"
: "=r"(Flags));
return Flags & (1 << 9);
#elif defined(a32)
asmv("pushfl");
asmv("popl %0"
: "=r"(Flags));
return Flags & (1 << 9);
#elif defined(aa64)
asmv("mrs %0, cpsr"
: "=r"(Flags));
return Flags & (1 << 7);
#endif
}
case Enable:
{
#if defined(a86)
asmv("sti");
#elif defined(aa64)
asmv("cpsie i");
#endif
return true;
}
case Disable:
{
#if defined(a86)
asmv("cli");
#elif defined(aa64)
asmv("cpsid i");
#endif
return true;
}
default:
assert(!"Unknown InterruptsType");
break;
}
return false;
}
void *PageTable(void *PT)
{
void *ret;
#if defined(a64)
asmv("movq %%cr3, %0"
: "=r"(ret));
if (PT)
{
asmv("movq %0, %%cr3"
:
: "r"(PT)
: "memory");
}
#elif defined(a32)
asmv("movl %%cr3, %0"
: "=r"(ret));
if (PT)
{
asmv("movl %0, %%cr3"
:
: "r"(PT)
: "memory");
}
#elif defined(aa64)
asmv("mrs %0, ttbr0_el1"
: "=r"(ret));
if (PT)
{
asmv("msr ttbr0_el1, %0"
:
: "r"(PT)
: "memory");
}
#endif
return ret;
}
struct SupportedFeat
{
bool PGE = false;
bool SSE = false;
bool UMIP = false;
bool SMEP = false;
bool SMAP = false;
};
SupportedFeat GetCPUFeat()
{
SupportedFeat feat{};
if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0)
{
CPU::x86::AMD::CPUID0x00000001 cpuid1;
CPU::x86::AMD::CPUID0x00000007_ECX_0 cpuid7;
feat.PGE = cpuid1.EDX.PGE;
feat.SSE = cpuid1.EDX.SSE;
feat.SMEP = cpuid7.EBX.SMEP;
feat.SMAP = cpuid7.EBX.SMAP;
feat.UMIP = cpuid7.ECX.UMIP;
}
else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0)
{
CPU::x86::Intel::CPUID0x00000001 cpuid1;
CPU::x86::Intel::CPUID0x00000007_0 cpuid7_0;
feat.PGE = cpuid1.EDX.PGE;
feat.SSE = cpuid1.EDX.SSE;
feat.SMEP = cpuid7_0.EBX.SMEP;
feat.SMAP = cpuid7_0.EBX.SMAP;
feat.UMIP = cpuid7_0.ECX.UMIP;
}
return feat;
}
void InitializeFeatures(int Core)
{
static int BSP = 0;
SupportedFeat feat = GetCPUFeat();
CR0 cr0 = readcr0();
CR4 cr4 = readcr4();
if (Config.SIMD == false)
{
debug("Disabling SSE support...");
feat.SSE = false;
}
if (feat.PGE)
{
debug("Enabling global pages support...");
if (!BSP)
KPrint("Global Pages is supported.");
cr4.PGE = true;
}
bool SSEEnableAfter = false;
/* Not sure if my code is not working properly or something else is the issue. */
if ((strcmp(Hypervisor(), x86_CPUID_VENDOR_VIRTUALBOX) != 0) &&
feat.SSE)
{
debug("Enabling SSE support...");
if (!BSP)
KPrint("SSE is supported.");
cr0.EM = false;
cr0.MP = true;
cr4.OSFXSR = true;
cr4.OSXMMEXCPT = true;
CPUData *CoreData = GetCPU(Core);
CoreData->Data.FPU.mxcsr = 0b0001111110000000;
CoreData->Data.FPU.mxcsrmask = 0b1111111110111111;
CoreData->Data.FPU.fcw = 0b0000001100111111;
fxrstor(&CoreData->Data.FPU);
SSEEnableAfter = true;
}
/* More info in AMD64 Architecture Programmer's Manual
Volume 2: 3.1.1 CR0 Register */
/* Not Write-Through
This is ignored on recent processors.
*/
cr0.NW = false;
/* Cache Disable
Wether the CPU should cache memory or not.
If it's enabled, PWT and PCD are ignored.
*/
cr0.CD = false;
/* Write Protect
When set, the supervisor can't write to read-only pages.
*/
cr0.WP = true;
/* Alignment check
The CPU checks the alignment of memory operands
and generates #AC if the alignment is incorrect.
The condition for an alignment check is:
- The AM flag in CR0 is set.
- The AC flag in the RFLAGS register is set.
- CPL is 3.
*/
cr0.AM = true;
debug("Updating CR0...");
writecr0(cr0);
debug("Updated CR0.");
debug("CPU Prevention Features:%s%s%s",
feat.SMEP ? " SMEP" : "",
feat.SMAP ? " SMAP" : "",
feat.UMIP ? " UMIP" : "");
/* User-Mode Instruction Prevention
This prevents user-mode code from executing these instructions:
SGDT, SIDT, SLDT, SMSW, STR
If any of these instructions are executed with CPL > 0, a #GP is generated.
*/
// cr4.UMIP = feat.UMIP;
/* Supervisor Mode Execution Prevention
This prevents user-mode code from executing code in the supervisor mode.
*/
cr4.SMEP = feat.SMEP;
/* Supervisor Mode Access Prevention
This prevents supervisor-mode code from accessing user-mode pages.
*/
cr4.SMAP = feat.SMAP;
debug("Updating CR4...");
writecr4(cr4);
debug("Updated CR4.");
debug("Enabling PAT support...");
wrmsr(MSR_CR_PAT, 0x6 | (0x0 << 8) | (0x1 << 16));
if (!BSP++)
trace("Features for BSP initialized.");
if (SSEEnableAfter)
{
SSEEnabled = true;
debug("SSE support enabled.");
}
}
uint64_t Counter()
{
// TODO: Get the counter from the x2APIC or any other timer that is available. (TSC is not available on all CPUs)
uint64_t Counter;
#if defined(a86)
uint32_t eax, edx;
asmv("rdtsc"
: "=a"(eax),
"=d"(edx));
Counter = ((uint64_t)eax) | (((uint64_t)edx) << 32);
#elif defined(aa64)
asmv("mrs %0, cntvct_el0"
: "=r"(Counter));
#endif
return Counter;
}
uint64_t CheckSIMD()
{
if (unlikely(!SSEEnabled))
return SIMD_NONE;
#warning "TODO: Proper SIMD support"
return SIMD_NONE;
#if defined(a86)
static uint64_t SIMDType = SIMD_NONE;
if (likely(SIMDType != SIMD_NONE))
return SIMDType;
if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0)
{
CPU::x86::AMD::CPUID0x00000001 cpuid;
asmv("cpuid"
: "=a"(cpuid.EAX.raw), "=b"(cpuid.EBX.raw),
"=c"(cpuid.ECX.raw), "=d"(cpuid.EDX.raw)
: "a"(0x1));
if (cpuid.ECX.SSE42)
SIMDType |= SIMD_SSE42;
else if (cpuid.ECX.SSE41)
SIMDType |= SIMD_SSE41;
else if (cpuid.ECX.SSSE3)
SIMDType |= SIMD_SSSE3;
else if (cpuid.ECX.SSE3)
SIMDType |= SIMD_SSE3;
else if (cpuid.EDX.SSE2)
SIMDType |= SIMD_SSE2;
else if (cpuid.EDX.SSE)
SIMDType |= SIMD_SSE;
#ifdef DEBUG
if (cpuid.ECX.SSE42)
debug("SSE4.2 is supported.");
if (cpuid.ECX.SSE41)
debug("SSE4.1 is supported.");
if (cpuid.ECX.SSSE3)
debug("SSSE3 is supported.");
if (cpuid.ECX.SSE3)
debug("SSE3 is supported.");
if (cpuid.EDX.SSE2)
debug("SSE2 is supported.");
if (cpuid.EDX.SSE)
debug("SSE is supported.");
#endif
return SIMDType;
}
else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0)
{
CPU::x86::Intel::CPUID0x00000001 cpuid;
asmv("cpuid"
: "=a"(cpuid.EAX.raw), "=b"(cpuid.EBX.raw), "=c"(cpuid.ECX.raw), "=d"(cpuid.EDX.raw)
: "a"(0x1));
if (cpuid.ECX.SSE4_2)
SIMDType |= SIMD_SSE42;
else if (cpuid.ECX.SSE4_1)
SIMDType |= SIMD_SSE41;
else if (cpuid.ECX.SSSE3)
SIMDType |= SIMD_SSSE3;
else if (cpuid.ECX.SSE3)
SIMDType |= SIMD_SSE3;
else if (cpuid.EDX.SSE2)
SIMDType |= SIMD_SSE2;
else if (cpuid.EDX.SSE)
SIMDType |= SIMD_SSE;
#ifdef DEBUG
if (cpuid.ECX.SSE4_2)
debug("SSE4.2 is supported.");
if (cpuid.ECX.SSE4_1)
debug("SSE4.1 is supported.");
if (cpuid.ECX.SSSE3)
debug("SSSE3 is supported.");
if (cpuid.ECX.SSE3)
debug("SSE3 is supported.");
if (cpuid.EDX.SSE2)
debug("SSE2 is supported.");
if (cpuid.EDX.SSE)
debug("SSE is supported.");
#endif
return SIMDType;
}
debug("No SIMD support.");
#endif // a64 || a32
return SIMD_NONE;
}
bool CheckSIMD(x86SIMDType Type)
{
if (unlikely(!SSEEnabled))
return false;
#if defined(a86)
if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0)
{
CPU::x86::AMD::CPUID0x00000001 cpuid;
asmv("cpuid"
: "=a"(cpuid.EAX.raw), "=b"(cpuid.EBX.raw), "=c"(cpuid.ECX.raw), "=d"(cpuid.EDX.raw)
: "a"(0x1));
if (Type == SIMD_SSE42)
return cpuid.ECX.SSE42;
else if (Type == SIMD_SSE41)
return cpuid.ECX.SSE41;
else if (Type == SIMD_SSSE3)
return cpuid.ECX.SSSE3;
else if (Type == SIMD_SSE3)
return cpuid.ECX.SSE3;
else if (Type == SIMD_SSE2)
return cpuid.EDX.SSE2;
else if (Type == SIMD_SSE)
return cpuid.EDX.SSE;
}
else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0)
{
CPU::x86::Intel::CPUID0x00000001 cpuid;
asmv("cpuid"
: "=a"(cpuid.EAX.raw), "=b"(cpuid.EBX.raw), "=c"(cpuid.ECX.raw), "=d"(cpuid.EDX.raw)
: "a"(0x1));
if (Type == SIMD_SSE42)
return cpuid.ECX.SSE4_2;
else if (Type == SIMD_SSE41)
return cpuid.ECX.SSE4_1;
else if (Type == SIMD_SSSE3)
return cpuid.ECX.SSSE3;
else if (Type == SIMD_SSE3)
return cpuid.ECX.SSE3;
else if (Type == SIMD_SSE2)
return cpuid.EDX.SSE2;
else if (Type == SIMD_SSE)
return cpuid.EDX.SSE;
}
#endif // a64 || a32
return false;
}
}

236
Kernel/core/debugger.cpp Normal file
View File

@ -0,0 +1,236 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <debug.h>
#include <printf.h>
#include <lock.hpp>
#include <io.h>
#include "../kernel.h"
NewLock(DebuggerLock);
extern bool serialports[8];
EXTERNC NIF void uart_wrapper(char c, void *unused)
{
static int once = 0;
if (unlikely(!once++))
{
uint8_t com = inb(0x3F8);
if (com != 0xFF)
{
outb(s_cst(uint16_t, 0x3F8 + 1), 0x00); // Disable all interrupts
outb(s_cst(uint16_t, 0x3F8 + 3), 0x80); // Enable DLAB (set baud rate divisor)
outb(s_cst(uint16_t, 0x3F8 + 0), 0x1); // Set divisor to 1 (lo byte) 115200 baud
outb(s_cst(uint16_t, 0x3F8 + 1), 0x0); // (hi byte)
outb(s_cst(uint16_t, 0x3F8 + 3), 0x03); // 8 bits, no parity, one stop bit
outb(s_cst(uint16_t, 0x3F8 + 2), 0xC7); // Enable FIFO, clear them, with 14-byte threshold
outb(s_cst(uint16_t, 0x3F8 + 4), 0x0B); // IRQs enabled, RTS/DSR set
/* FIXME https://wiki.osdev.org/Serial_Ports */
// outb(s_cst(uint16_t, 0x3F8 + 0), 0x1E);
// outb(s_cst(uint16_t, 0x3F8 + 0), 0xAE);
// Check if the serial port is faulty.
// if (inb(s_cst(uint16_t, 0x3F8 + 0)) != 0xAE)
// {
// static int once = 0;
// if (!once++)
// warn("Serial port %#llx is faulty.", 0x3F8);
// // serialports[0x3F8] = false; // ignore for now
// // return;
// }
// Set to normal operation mode.
outb(s_cst(uint16_t, 0x3F8 + 4), 0x0F);
serialports[0] = true;
}
}
if (likely(serialports[0]))
{
while ((inb(s_cst(uint16_t, 0x3F8 + 5)) & 0x20) == 0)
;
outb(0x3F8, c);
}
UNUSED(unused);
}
static inline NIF bool WritePrefix(DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, va_list args)
{
const char *DbgLvlString;
switch (Level)
{
case DebugLevelError:
DbgLvlString = "ERROR";
break;
case DebugLevelWarning:
DbgLvlString = "WARN ";
break;
case DebugLevelInfo:
DbgLvlString = "INFO ";
break;
case DebugLevelDebug:
DbgLvlString = "DEBUG";
break;
case DebugLevelTrace:
DbgLvlString = "TRACE";
break;
case DebugLevelFixme:
DbgLvlString = "FIXME";
break;
case DebugLevelStub:
fctprintf(uart_wrapper, nullptr, "STUB | %s>%s() is stub\n", File, Function);
return false;
case DebugLevelFunction:
fctprintf(uart_wrapper, nullptr, "FUNC | %s>%s( ", File, Function);
vfctprintf(uart_wrapper, nullptr, Format, args);
fctprintf(uart_wrapper, nullptr, " )\n");
return false;
case DebugLevelUbsan:
{
DbgLvlString = "UBSAN";
fctprintf(uart_wrapper, nullptr, "%s| ", DbgLvlString);
return true;
}
default:
DbgLvlString = "UNKNW";
break;
}
fctprintf(uart_wrapper, nullptr, "%s| %s>%s:%d: ", DbgLvlString, File, Function, Line);
return true;
}
namespace SysDbg
{
NIF void Write(DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...)
{
va_list args;
va_start(args, Format);
if (!WritePrefix(Level, File, Line, Function, Format, args))
{
va_end(args);
return;
}
vfctprintf(uart_wrapper, nullptr, Format, args);
va_end(args);
}
NIF void WriteLine(DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...)
{
va_list args;
va_start(args, Format);
if (!WritePrefix(Level, File, Line, Function, Format, args))
{
va_end(args);
return;
}
vfctprintf(uart_wrapper, nullptr, Format, args);
va_end(args);
uart_wrapper('\n', nullptr);
}
NIF void LockedWrite(DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...)
{
SmartTimeoutLock(DebuggerLock, 1000);
va_list args;
va_start(args, Format);
if (!WritePrefix(Level, File, Line, Function, Format, args))
{
va_end(args);
return;
}
vfctprintf(uart_wrapper, nullptr, Format, args);
va_end(args);
}
NIF void LockedWriteLine(DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...)
{
SmartTimeoutLock(DebuggerLock, 1000);
va_list args;
va_start(args, Format);
if (!WritePrefix(Level, File, Line, Function, Format, args))
{
va_end(args);
return;
}
vfctprintf(uart_wrapper, nullptr, Format, args);
va_end(args);
uart_wrapper('\n', nullptr);
}
}
// C compatibility
extern "C" NIF void SysDbgWrite(enum DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...)
{
va_list args;
va_start(args, Format);
if (!WritePrefix(Level, File, Line, Function, Format, args))
{
va_end(args);
return;
}
vfctprintf(uart_wrapper, nullptr, Format, args);
va_end(args);
}
// C compatibility
extern "C" NIF void SysDbgWriteLine(enum DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...)
{
va_list args;
va_start(args, Format);
if (!WritePrefix(Level, File, Line, Function, Format, args))
{
va_end(args);
return;
}
vfctprintf(uart_wrapper, nullptr, Format, args);
va_end(args);
uart_wrapper('\n', nullptr);
}
// C compatibility
extern "C" NIF void SysDbgLockedWrite(enum DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...)
{
SmartTimeoutLock(DebuggerLock, 1000);
va_list args;
va_start(args, Format);
if (!WritePrefix(Level, File, Line, Function, Format, args))
{
va_end(args);
return;
}
vfctprintf(uart_wrapper, nullptr, Format, args);
va_end(args);
}
// C compatibility
extern "C" NIF void SysDbgLockedWriteLine(enum DebugLevel Level, const char *File, int Line, const char *Function, const char *Format, ...)
{
SmartTimeoutLock(DebuggerLock, 1000);
va_list args;
va_start(args, Format);
if (!WritePrefix(Level, File, Line, Function, Format, args))
{
va_end(args);
return;
}
vfctprintf(uart_wrapper, nullptr, Format, args);
va_end(args);
uart_wrapper('\n', nullptr);
}

193
Kernel/core/disk.cpp Normal file
View File

@ -0,0 +1,193 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <disk.hpp>
#include <memory.hpp>
#include <printf.h>
#include "../kernel.h"
namespace Disk
{
void Manager::FetchDisks(unsigned long modUniqueID)
{
/* KernelCallback */
// KernelCallback callback{};
// callback.Reason = QueryReason;
// DriverManager->IOCB(modUniqueID, &callback);
// this->AvailablePorts = callback.DiskCallback.Fetch.Ports;
// this->BytesPerSector = callback.DiskCallback.Fetch.BytesPerSector;
debug("AvailablePorts:%ld BytesPerSector:%ld", this->AvailablePorts, this->BytesPerSector);
if (this->AvailablePorts <= 0)
return;
uint8_t *RWBuffer = (uint8_t *)KernelAllocator.RequestPages(TO_PAGES(this->BytesPerSector + 1));
for (unsigned char ItrPort = 0; ItrPort < this->AvailablePorts; ItrPort++)
{
Drive *drive = new Drive{};
sprintf(drive->Name, "sd%ld", modUniqueID);
debug("Drive Name: %s", drive->Name);
// TODO: Implement disk type detection. Very useful in the future.
drive->MechanicalDisk = true;
memset(RWBuffer, 0, this->BytesPerSector);
/* KernelCallback */
// callback.Reason = ReceiveReason;
// callback.DiskCallback.RW = {
// .Sector = 0,
// .SectorCount = 2,
// .Port = ItrPort,
// .Buffer = RWBuffer,
// .Write = false,
// };
// DriverManager->IOCB(modUniqueID, &callback);
memcpy(&drive->Table, RWBuffer, sizeof(PartitionTable));
/*
TODO: Add to devfs the disk
*/
if (drive->Table.GPT.Signature == GPT_MAGIC)
{
drive->Style = GPT;
uint32_t Entries = 512 / drive->Table.GPT.EntrySize;
uint32_t Sectors = drive->Table.GPT.PartCount / Entries;
for (uint32_t Block = 0; Block < Sectors; Block++)
{
memset(RWBuffer, 0, this->BytesPerSector);
/* KernelCallback */
// callback.Reason = ReceiveReason;
// callback.DiskCallback.RW = {
// .Sector = 2 + Block,
// .SectorCount = 1,
// .Port = ItrPort,
// .Buffer = RWBuffer,
// .Write = false,
// };
// DriverManager->IOCB(modUniqueID, &callback);
for (uint32_t e = 0; e < Entries; e++)
{
GUIDPartitionTableEntry GPTPartition = reinterpret_cast<GUIDPartitionTableEntry *>(RWBuffer)[e];
if (memcmp(GPTPartition.PartitionType, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", sizeof(GPTPartition.PartitionType)) != 0)
{
debug("Partition Type: %02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
GPTPartition.PartitionType[0], GPTPartition.PartitionType[1], GPTPartition.PartitionType[2], GPTPartition.PartitionType[3],
GPTPartition.PartitionType[4], GPTPartition.PartitionType[5], GPTPartition.PartitionType[6], GPTPartition.PartitionType[7],
GPTPartition.PartitionType[8], GPTPartition.PartitionType[9], GPTPartition.PartitionType[10], GPTPartition.PartitionType[11],
GPTPartition.PartitionType[12], GPTPartition.PartitionType[13], GPTPartition.PartitionType[14], GPTPartition.PartitionType[15]);
debug("Unique Partition GUID: %02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
GPTPartition.UniquePartitionGUID[0], GPTPartition.UniquePartitionGUID[1], GPTPartition.UniquePartitionGUID[2], GPTPartition.UniquePartitionGUID[3],
GPTPartition.UniquePartitionGUID[4], GPTPartition.UniquePartitionGUID[5], GPTPartition.UniquePartitionGUID[6], GPTPartition.UniquePartitionGUID[7],
GPTPartition.UniquePartitionGUID[8], GPTPartition.UniquePartitionGUID[9], GPTPartition.UniquePartitionGUID[10], GPTPartition.UniquePartitionGUID[11],
GPTPartition.UniquePartitionGUID[12], GPTPartition.UniquePartitionGUID[13], GPTPartition.UniquePartitionGUID[14], GPTPartition.UniquePartitionGUID[15]);
Partition *partition = new Partition{};
memset(partition->Label, '\0', sizeof(partition->Label));
// TODO: Add support for UTF-16 partition names.
/* Convert utf16 to utf8 */
for (int i = 0; i < 36; i++)
{
uint16_t utf16 = GPTPartition.PartitionName[i];
if (utf16 == 0)
break;
if (utf16 < 0x80)
partition->Label[i] = (char)utf16;
else if (utf16 < 0x800)
{
partition->Label[i] = (char)(0xC0 | (utf16 >> 6));
partition->Label[i + 1] = (char)(0x80 | (utf16 & 0x3F));
i++;
}
else
{
partition->Label[i] = (char)(0xE0 | (utf16 >> 12));
partition->Label[i + 1] = (char)(0x80 | ((utf16 >> 6) & 0x3F));
partition->Label[i + 2] = (char)(0x80 | (utf16 & 0x3F));
i += 2;
}
}
partition->StartLBA = GPTPartition.FirstLBA;
partition->EndLBA = GPTPartition.LastLBA;
partition->Sectors = (size_t)(partition->EndLBA - partition->StartLBA);
partition->Port = ItrPort;
partition->Flags = Present;
partition->Style = GPT;
if (GPTPartition.Attributes & 1)
partition->Flags |= EFISystemPartition;
partition->Index = drive->Partitions.size();
trace("GPT partition \"%s\" found with %lld sectors",
partition->Label, partition->Sectors);
drive->Partitions.push_back(partition);
char PartitionName[64];
sprintf(PartitionName, "sd%ldp%ld", drives.size(), partition->Index);
fixme("PartitionName: %s", PartitionName);
/*
TODO: Add to devfs the disk
*/
}
}
}
trace("%d GPT partitions found.", drive->Partitions.size());
}
else if (drive->Table.MBR.Signature[0] == MBR_MAGIC0 &&
drive->Table.MBR.Signature[1] == MBR_MAGIC1)
{
drive->Style = MBR;
for (size_t p = 0; p < 4; p++)
if (drive->Table.MBR.Partitions[p].LBAFirst != 0)
{
Partition *partition = new Partition{};
partition->StartLBA = drive->Table.MBR.Partitions[p].LBAFirst;
partition->EndLBA = drive->Table.MBR.Partitions[p].LBAFirst + drive->Table.MBR.Partitions[p].Sectors;
partition->Sectors = drive->Table.MBR.Partitions[p].Sectors;
partition->Port = ItrPort;
partition->Flags = Present;
partition->Style = MBR;
partition->Index = drive->Partitions.size();
trace("MBR Partition %x found with %d sectors.",
drive->Table.MBR.UniqueID, partition->Sectors);
drive->Partitions.push_back(partition);
char PartitionName[64];
sprintf(PartitionName, "sd%ldp%ld", drives.size(), partition->Index);
fixme("PartitionName: %s", PartitionName);
/*
TODO: Add to devfs the disk
*/
}
trace("%d MBR partitions found.", drive->Partitions.size());
}
else
warn("No partition table found on port %d!", ItrPort);
drives.push_back(drive);
}
KernelAllocator.FreePages(RWBuffer, TO_PAGES(this->BytesPerSector + 1));
}
Manager::Manager() {}
Manager::~Manager() {}
}

924
Kernel/core/driver/api.cpp Normal file
View File

@ -0,0 +1,924 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <driver.hpp>
#include <interface/driver.h>
#include <interface/fs.h>
#include <type_traits>
#include <interface/aip.h>
#include <interface/input.h>
#include <interface/pci.h>
#include "../../kernel.h"
#define DEBUG_API
#ifdef DEBUG_API
#define dbg_api(Format, ...) func(Format, ##__VA_ARGS__)
#else
#define dbg_api(Format, ...)
#endif
namespace v0
{
typedef int CriticalState;
void KernelPrint(dev_t DriverID, const char *Format, va_list args)
{
dbg_api("%d, %s, %#lx", DriverID, Format, args);
_KPrint(Format, args);
}
void KernelLog(dev_t DriverID, const char *Format, va_list args)
{
dbg_api("%d, %s, %#lx", DriverID, Format, args);
fctprintf(uart_wrapper, nullptr, "DRVER| %ld: ", DriverID);
vfctprintf(uart_wrapper, nullptr, Format, args);
uart_wrapper('\n', nullptr);
}
/* --------- */
CriticalState EnterCriticalSection(dev_t DriverID)
{
dbg_api("%d", DriverID);
CriticalState cs;
#if defined(__i386__) || defined(__x86_64__)
uintptr_t Flags;
#if defined(__x86_64__)
asmv("pushfq");
asmv("popq %0"
: "=r"(Flags));
#else
asmv("pushfl");
asmv("popl %0"
: "=r"(Flags));
#endif
cs = Flags & (1 << 9);
asmv("cli");
#elif defined(__arm__) || defined(__aarch64__)
uintptr_t Flags;
asmv("mrs %0, cpsr"
: "=r"(Flags));
cs = Flags & (1 << 7);
asmv("cpsid i");
#endif
return cs;
}
void LeaveCriticalSection(dev_t DriverID, CriticalState PreviousState)
{
dbg_api("%d, %d", DriverID, PreviousState);
#if defined(__i386__) || defined(__x86_64__)
if (PreviousState)
asmv("sti");
#elif defined(__arm__) || defined(__aarch64__)
if (PreviousState)
asmv("cpsie i");
#endif
}
int RegisterInterruptHandler(dev_t DriverID, uint8_t IRQ, void *Handler)
{
dbg_api("%d, %d, %#lx", DriverID, IRQ, Handler);
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
DriverManager->GetDrivers();
const auto it = drivers.find(DriverID);
if (it == drivers.end())
ReturnLogError(-EINVAL, "Driver %d not found", DriverID);
const Driver::DriverObject *drv = &it->second;
if (drv->InterruptHandlers->contains(IRQ))
return -EEXIST;
Interrupts::AddHandler((void (*)(CPU::TrapFrame *))Handler, IRQ);
auto ih = drv->InterruptHandlers;
ih->insert(std::pair<uint8_t, void *>(IRQ, Handler));
return 0;
}
int OverrideInterruptHandler(dev_t DriverID, uint8_t IRQ, void *Handler)
{
dbg_api("%d, %d, %#lx", DriverID, IRQ, Handler);
debug("Overriding IRQ %d with %#lx", IRQ, Handler);
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
DriverManager->GetDrivers();
for (auto &var : drivers)
{
Driver::DriverObject *drv = &var.second;
for (const auto &ih : *drv->InterruptHandlers)
{
if (ih.first != IRQ)
continue;
debug("Removing IRQ %d: %#lx for %s", IRQ, (uintptr_t)ih.second, drv->Path.c_str());
Interrupts::RemoveHandler((void (*)(CPU::TrapFrame *))ih.second, IRQ);
drv->InterruptHandlers->erase(IRQ);
break;
}
}
return RegisterInterruptHandler(DriverID, IRQ, Handler);
}
int UnregisterInterruptHandler(dev_t DriverID, uint8_t IRQ, void *Handler)
{
dbg_api("%d, %d, %#lx", DriverID, IRQ, Handler);
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
DriverManager->GetDrivers();
const auto it = drivers.find(DriverID);
if (it == drivers.end())
ReturnLogError(-EINVAL, "Driver %d not found", DriverID);
const Driver::DriverObject *drv = &it->second;
Interrupts::RemoveHandler((void (*)(CPU::TrapFrame *))Handler, IRQ);
auto ih = drv->InterruptHandlers;
ih->erase(IRQ);
return 0;
}
int UnregisterAllInterruptHandlers(dev_t DriverID, void *Handler)
{
dbg_api("%d, %#lx", DriverID, Handler);
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
DriverManager->GetDrivers();
const auto it = drivers.find(DriverID);
if (it == drivers.end())
ReturnLogError(-EINVAL, "Driver %d not found", DriverID);
const Driver::DriverObject *drv = &it->second;
for (auto &i : *drv->InterruptHandlers)
{
Interrupts::RemoveHandler((void (*)(CPU::TrapFrame *))Handler, i.first);
debug("Removed IRQ %d: %#lx for %s", i.first, (uintptr_t)Handler, drv->Path.c_str());
}
auto ih = drv->InterruptHandlers;
ih->clear();
return 0;
}
/* --------- */
dev_t RegisterFileSystem(dev_t DriverID, FileSystemInfo *Info, struct Inode *Root)
{
dbg_api("%d, %#lx, %#lx", DriverID, Info, Root);
return fs->RegisterFileSystem(Info, Root);
}
int UnregisterFileSystem(dev_t DriverID, dev_t Device)
{
dbg_api("%d, %d", DriverID, Device);
return fs->UnregisterFileSystem(Device);
}
/* --------- */
pid_t CreateKernelProcess(dev_t DriverID, const char *Name)
{
dbg_api("%d, %s", DriverID, Name);
Tasking::PCB *pcb = TaskManager->CreateProcess(nullptr, Name, Tasking::System,
true, 0, 0);
return pcb->ID;
}
pid_t CreateKernelThread(dev_t DriverID, pid_t pId, const char *Name, void *EntryPoint, void *Argument)
{
dbg_api("%d, %d, %s, %#lx, %#lx", DriverID, pId, Name, EntryPoint, Argument);
Tasking::PCB *parent = TaskManager->GetProcessByID(pId);
if (!parent)
return -EINVAL;
CriticalSection cs;
Tasking::TCB *tcb = TaskManager->CreateThread(parent, (Tasking::IP)EntryPoint);
if (Argument)
tcb->SYSV_ABI_Call((uintptr_t)Argument);
tcb->Rename(Name);
return tcb->ID;
}
pid_t GetCurrentProcess(dev_t DriverID)
{
dbg_api("%d", DriverID);
return TaskManager->GetCurrentProcess()->ID;
}
int KillProcess(dev_t DriverID, pid_t pId, int ExitCode)
{
dbg_api("%d, %d, %d", DriverID, pId, ExitCode);
Tasking::PCB *pcb = TaskManager->GetProcessByID(pId);
if (!pcb)
return -EINVAL;
TaskManager->KillProcess(pcb, (Tasking::KillCode)ExitCode);
return 0;
}
int KillThread(dev_t DriverID, pid_t tId, pid_t pId, int ExitCode)
{
dbg_api("%d, %d, %d", DriverID, tId, ExitCode);
Tasking::TCB *tcb = TaskManager->GetThreadByID(tId, TaskManager->GetProcessByID(pId));
if (!tcb)
return -EINVAL;
TaskManager->KillThread(tcb, (Tasking::KillCode)ExitCode);
return 0;
}
void Yield(dev_t DriverID)
{
dbg_api("%d", DriverID);
TaskManager->Yield();
}
void Sleep(dev_t DriverID, uint64_t Milliseconds)
{
dbg_api("%d, %d", DriverID, Milliseconds);
TaskManager->Sleep(Milliseconds);
}
/* --------- */
void PIC_EOI(dev_t DriverID, uint8_t IRQ)
{
dbg_api("%d, %d", DriverID, IRQ);
if (IRQ >= 8)
outb(PIC2_CMD, _PIC_EOI);
outb(PIC1_CMD, _PIC_EOI);
}
void IRQ_MASK(dev_t DriverID, uint8_t IRQ)
{
dbg_api("%d, %d", DriverID, IRQ);
uint16_t port;
uint8_t value;
if (IRQ < 8)
port = PIC1_DATA;
else
{
port = PIC2_DATA;
IRQ -= 8;
}
value = inb(port) | (1 << IRQ);
outb(port, value);
}
void IRQ_UNMASK(dev_t DriverID, uint8_t IRQ)
{
dbg_api("%d, %d", DriverID, IRQ);
uint16_t port;
uint8_t value;
if (IRQ < 8)
port = PIC1_DATA;
else
{
port = PIC2_DATA;
IRQ -= 8;
}
value = inb(port) & ~(1 << IRQ);
outb(port, value);
}
void PS2Wait(dev_t DriverID, const bool Output)
{
dbg_api("%d, %d", DriverID, Output);
int Timeout = 100000;
PS2_STATUSES Status = {.Raw = inb(PS2_STATUS)};
while (Timeout--)
{
if (!Output) /* FIXME: Reverse? */
{
if (Status.OutputBufferFull == 0)
return;
}
else
{
if (Status.InputBufferFull == 0)
return;
}
Status.Raw = inb(PS2_STATUS);
}
warn("PS/2 controller timeout! (Status: %#x, %d)", Status, Output);
}
void PS2WriteCommand(dev_t DriverID, uint8_t Command)
{
dbg_api("%d, %d", DriverID, Command);
WaitInput;
outb(PS2_CMD, Command);
}
void PS2WriteData(dev_t DriverID, uint8_t Data)
{
dbg_api("%d, %d", DriverID, Data);
WaitInput;
outb(PS2_DATA, Data);
}
uint8_t PS2ReadData(dev_t DriverID)
{
dbg_api("%d", DriverID);
WaitOutput;
return inb(PS2_DATA);
}
uint8_t PS2ReadStatus(dev_t DriverID)
{
dbg_api("%d", DriverID);
WaitOutput;
return inb(PS2_STATUS);
}
uint8_t PS2ReadAfterACK(dev_t DriverID)
{
dbg_api("%d", DriverID);
uint8_t ret = PS2ReadData(DriverID);
while (ret == PS2_ACK)
{
WaitOutput;
ret = inb(PS2_DATA);
}
return ret;
}
void PS2ClearOutputBuffer(dev_t DriverID)
{
dbg_api("%d", DriverID);
PS2_STATUSES Status;
int timeout = 0x500;
while (timeout--)
{
Status.Raw = inb(PS2_STATUS);
if (Status.OutputBufferFull == 0)
return;
inb(PS2_DATA);
}
}
int PS2ACKTimeout(dev_t DriverID)
{
dbg_api("%d", DriverID);
int timeout = 0x500;
while (timeout > 0)
{
if (PS2ReadData(DriverID) == PS2_ACK)
return 0;
timeout--;
}
return -ETIMEDOUT;
}
/* --------- */
void *AllocateMemory(dev_t DriverID, size_t Pages)
{
dbg_api("%d, %d", DriverID, Pages);
std::unordered_map<dev_t, Driver::DriverObject> &Drivers =
DriverManager->GetDrivers();
auto itr = Drivers.find(DriverID);
assert(itr != Drivers.end());
void *ptr = itr->second.vma->RequestPages(Pages);
memset(ptr, 0, FROM_PAGES(Pages));
return ptr;
}
void FreeMemory(dev_t DriverID, void *Pointer, size_t Pages)
{
dbg_api("%d, %#lx, %d", DriverID, Pointer, Pages);
std::unordered_map<dev_t, Driver::DriverObject> &Drivers =
DriverManager->GetDrivers();
auto itr = Drivers.find(DriverID);
assert(itr != Drivers.end());
itr->second.vma->FreePages(Pointer, Pages);
}
void *MemoryCopy(dev_t DriverID, void *Destination, const void *Source, size_t Length)
{
dbg_api("%d, %#lx, %#lx, %d", DriverID, Destination, Source, Length);
return memcpy(Destination, Source, Length);
}
void *MemorySet(dev_t DriverID, void *Destination, int Value, size_t Length)
{
dbg_api("%d, %#lx, %d, %d", DriverID, Destination, Value, Length);
return memset(Destination, Value, Length);
}
void *MemoryMove(dev_t DriverID, void *Destination, const void *Source, size_t Length)
{
dbg_api("%d, %#lx, %#lx, %d", DriverID, Destination, Source, Length);
return memmove(Destination, Source, Length);
}
size_t StringLength(dev_t DriverID, const char String[])
{
dbg_api("%d, %s", DriverID, String);
return strlen(String);
}
char *_strstr(dev_t DriverID, const char *Haystack, const char *Needle)
{
dbg_api("%d, %s, %s", DriverID, Haystack, Needle);
return (char *)strstr(Haystack, Needle);
}
void MapPages(dev_t MajorID, void *PhysicalAddress, void *VirtualAddress, size_t Pages, uint32_t Flags)
{
dbg_api("%d, %#lx, %#lx, %d, %d", MajorID, PhysicalAddress, VirtualAddress, Pages, Flags);
Memory::Virtual vmm(KernelPageTable);
vmm.Map(VirtualAddress, PhysicalAddress, Pages, Flags);
}
void UnmapPages(dev_t MajorID, void *VirtualAddress, size_t Pages)
{
dbg_api("%d, %#lx, %d", MajorID, VirtualAddress, Pages);
Memory::Virtual vmm(KernelPageTable);
vmm.Unmap(VirtualAddress, Pages);
}
void AppendMapFlag(dev_t MajorID, void *Address, PageMapFlags Flag)
{
dbg_api("%d, %#lx, %d", MajorID, Address, Flag);
Memory::Virtual vmm(KernelPageTable);
vmm.GetPTE(Address)->raw |= Flag;
}
void RemoveMapFlag(dev_t MajorID, void *Address, PageMapFlags Flag)
{
dbg_api("%d, %#lx, %d", MajorID, Address, Flag);
Memory::Virtual vmm(KernelPageTable);
vmm.GetPTE(Address)->raw &= ~Flag;
}
void *Znwm(size_t Size)
{
dbg_api("%d", Size);
return malloc(Size);
}
void ZdlPvm(void *Pointer, size_t Size)
{
dbg_api("%d, %#lx", Pointer, Size);
free(Pointer);
}
/* --------- */
__PCIArray *GetPCIDevices(dev_t DriverID, uint16_t _Vendors[], uint16_t _Devices[])
{
dbg_api("%d, %#lx, %#lx", DriverID, _Vendors, _Devices);
std::unordered_map<dev_t, Driver::DriverObject> &Drivers =
DriverManager->GetDrivers();
auto itr = Drivers.find(DriverID);
if (itr == Drivers.end())
return nullptr;
std::list<uint16_t> VendorIDs;
for (int i = 0; _Vendors[i] != 0x0; i++)
VendorIDs.push_back(_Vendors[i]);
std::list<uint16_t> DeviceIDs;
for (int i = 0; _Devices[i] != 0x0; i++)
DeviceIDs.push_back(_Devices[i]);
std::list<PCI::PCIDevice> Devices = PCIManager->FindPCIDevice(VendorIDs, DeviceIDs);
if (Devices.empty())
return nullptr;
Memory::VirtualMemoryArea *vma = itr->second.vma;
__PCIArray *head = nullptr;
__PCIArray *array = nullptr;
foreach (auto &dev in Devices)
{
/* TODO: optimize memory allocation */
PCI::PCIDevice *dptr = (PCI::PCIDevice *)vma->RequestPages(TO_PAGES(sizeof(PCI::PCIDevice)));
memcpy(dptr, &dev, sizeof(PCI::PCIDevice));
__PCIArray *newArray = (__PCIArray *)vma->RequestPages(TO_PAGES(sizeof(__PCIArray)));
if (unlikely(head == nullptr))
{
head = newArray;
array = head;
}
else
{
array->Next = newArray;
array = newArray;
}
array->Device = dptr;
array->Next = nullptr;
debug("Found %02x.%02x.%02x: %04x:%04x",
dev.Bus, dev.Device, dev.Function,
dev.Header->VendorID, dev.Header->DeviceID);
}
return head;
}
void InitializePCI(dev_t DriverID, void *_Header)
{
dbg_api("%d, %#lx", DriverID, _Header);
PCI::PCIDevice *__device = (PCI::PCIDevice *)_Header;
PCI::PCIDeviceHeader *Header = (PCI::PCIDeviceHeader *)__device->Header;
debug("Header Type: %d", Header->HeaderType);
switch (Header->HeaderType)
{
case 128:
warn("Unknown header type %d! Guessing PCI Header 0",
Header->HeaderType);
[[fallthrough]];
case 0: /* PCI Header 0 */
{
PCI::PCIHeader0 *hdr0 = (PCI::PCIHeader0 *)Header;
uint32_t BAR[6];
size_t BARsSize[6];
BAR[0] = hdr0->BAR0;
BAR[1] = hdr0->BAR1;
BAR[2] = hdr0->BAR2;
BAR[3] = hdr0->BAR3;
BAR[4] = hdr0->BAR4;
BAR[5] = hdr0->BAR5;
debug("Type: %d; IOBase: %#lx; MemoryBase: %#lx",
BAR[0] & 1, BAR[1] & (~3), BAR[0] & (~15));
/* BARs Size */
for (short i = 0; i < 6; i++)
{
if (BAR[i] == 0)
continue;
size_t size;
if ((BAR[i] & 1) == 0) /* Memory Base */
{
hdr0->BAR0 = 0xFFFFFFFF;
size = hdr0->BAR0;
hdr0->BAR0 = BAR[i];
BARsSize[i] = size & (~15);
BARsSize[i] = ~BARsSize[i] + 1;
BARsSize[i] = BARsSize[i] & 0xFFFFFFFF;
debug("BAR%d %#lx size: %d",
i, BAR[i], BARsSize[i]);
}
else if ((BAR[i] & 1) == 1) /* I/O Base */
{
hdr0->BAR1 = 0xFFFFFFFF;
size = hdr0->BAR1;
hdr0->BAR1 = BAR[i];
BARsSize[i] = size & (~3);
BARsSize[i] = ~BARsSize[i] + 1;
BARsSize[i] = BARsSize[i] & 0xFFFF;
debug("BAR%d %#lx size: %d",
i, BAR[i], BARsSize[i]);
}
}
Memory::Virtual vmm(KernelPageTable);
/* Mapping the BARs */
for (short i = 0; i < 6; i++)
{
if (BAR[i] == 0)
continue;
if ((BAR[i] & 1) == 0) /* Memory Base */
{
uintptr_t BARBase = BAR[i] & (~15);
size_t BARSize = BARsSize[i];
debug("Mapping BAR%d %#lx-%#lx",
i, BARBase, BARBase + BARSize);
if (BARSize == 0)
{
warn("BAR%d size is zero!", i);
BARSize++;
}
vmm.Map((void *)BARBase, (void *)BARBase,
BARSize, Memory::RW | Memory::PWT | Memory::PCD);
}
else if ((BAR[i] & 1) == 1) /* I/O Base */
{
uintptr_t BARBase = BAR[i] & (~3);
size_t BARSize = BARsSize[i];
debug("Mapping BAR%d %#x-%#x",
i, BARBase, BARBase + BARSize);
if (BARSize == 0)
{
warn("BAR%d size is zero!", i);
BARSize++;
}
vmm.Map((void *)BARBase, (void *)BARBase,
BARSize, Memory::RW | Memory::PWT | Memory::PCD);
}
}
break;
}
case 1: /* PCI Header 1 (PCI-to-PCI Bridge) */
{
fixme("PCI Header 1 (PCI-to-PCI Bridge) not implemented yet");
break;
}
case 2: /* PCI Header 2 (PCI-to-CardBus Bridge) */
{
fixme("PCI Header 2 (PCI-to-CardBus Bridge) not implemented yet");
break;
}
default:
{
error("Unknown header type %d", Header->HeaderType);
break;
}
}
Header->Command |= PCI_COMMAND_MASTER |
PCI_COMMAND_IO |
PCI_COMMAND_MEMORY;
Header->Command &= ~PCI_COMMAND_INTX_DISABLE;
}
uint32_t GetBAR(dev_t DriverID, uint8_t i, void *_Header)
{
dbg_api("%d, %d, %#lx", DriverID, i, _Header);
PCI::PCIDevice *__device = (PCI::PCIDevice *)_Header;
PCI::PCIDeviceHeader *Header = (PCI::PCIDeviceHeader *)__device->Header;
switch (Header->HeaderType)
{
case 128:
warn("Unknown header type %d! Guessing PCI Header 0",
Header->HeaderType);
[[fallthrough]];
case 0: /* PCI Header 0 */
{
PCI::PCIHeader0 *hdr0 =
(PCI::PCIHeader0 *)Header;
switch (i)
{
case 0:
return hdr0->BAR0;
case 1:
return hdr0->BAR1;
case 2:
return hdr0->BAR2;
case 3:
return hdr0->BAR3;
case 4:
return hdr0->BAR4;
case 5:
return hdr0->BAR5;
default:
assert(!"Invalid BAR index");
}
}
case 1: /* PCI Header 1 (PCI-to-PCI Bridge) */
{
PCI::PCIHeader1 *hdr1 =
(PCI::PCIHeader1 *)Header;
switch (i)
{
case 0:
return hdr1->BAR0;
case 1:
return hdr1->BAR1;
default:
assert(!"Invalid BAR index");
}
}
case 2: /* PCI Header 2 (PCI-to-CardBus Bridge) */
{
assert(!"PCI-to-CardBus Bridge not supported");
}
default:
assert(!"Invalid PCI header type");
}
}
uint8_t iLine(dev_t DriverID, PCIDevice *Device)
{
dbg_api("%d, %#lx", DriverID, Device);
PCIHeader0 *Header = (PCIHeader0 *)Device->Header;
return Header->InterruptLine;
}
uint8_t iPin(dev_t DriverID, PCIDevice *Device)
{
dbg_api("%d, %#lx", DriverID, Device);
PCIHeader0 *Header = (PCIHeader0 *)Device->Header;
return Header->InterruptPin;
}
/* --------- */
dev_t RegisterDevice(dev_t DriverID, DeviceType Type, const InodeOperations *Operations)
{
dbg_api("%d, %d, %#lx", DriverID, Type, Operations);
return DriverManager->RegisterDevice(DriverID, Type, Operations);
}
int UnregisterDevice(dev_t DriverID, dev_t Device)
{
dbg_api("%d, %d", DriverID, Device);
return DriverManager->UnregisterDevice(DriverID, Device);
}
int ReportInputEvent(dev_t DriverID, InputReport *Report)
{
dbg_api("%d, %#lx", DriverID, Report);
return DriverManager->ReportInputEvent(DriverID, Report);
}
}
struct APISymbols
{
const char *Name;
void *Function;
};
static struct APISymbols APISymbols_v0[] = {
{"__KernelPrint", (void *)v0::KernelPrint},
{"__KernelLog", (void *)v0::KernelLog},
{"__EnterCriticalSection", (void *)v0::EnterCriticalSection},
{"__LeaveCriticalSection", (void *)v0::LeaveCriticalSection},
{"__RegisterInterruptHandler", (void *)v0::RegisterInterruptHandler},
{"__OverrideInterruptHandler", (void *)v0::OverrideInterruptHandler},
{"__UnregisterInterruptHandler", (void *)v0::UnregisterInterruptHandler},
{"__UnregisterAllInterruptHandlers", (void *)v0::UnregisterAllInterruptHandlers},
{"__RegisterFileSystem", (void *)v0::RegisterFileSystem},
{"__UnregisterFileSystem", (void *)v0::UnregisterFileSystem},
{"__CreateKernelProcess", (void *)v0::CreateKernelProcess},
{"__CreateKernelThread", (void *)v0::CreateKernelThread},
{"__GetCurrentProcess", (void *)v0::GetCurrentProcess},
{"__KillProcess", (void *)v0::KillProcess},
{"__KillThread", (void *)v0::KillThread},
{"__Yield", (void *)v0::Yield},
{"__Sleep", (void *)v0::Sleep},
{"__PIC_EOI", (void *)v0::PIC_EOI},
{"__IRQ_MASK", (void *)v0::IRQ_MASK},
{"__IRQ_UNMASK", (void *)v0::IRQ_UNMASK},
{"__PS2Wait", (void *)v0::PS2Wait},
{"__PS2WriteCommand", (void *)v0::PS2WriteCommand},
{"__PS2WriteData", (void *)v0::PS2WriteData},
{"__PS2ReadData", (void *)v0::PS2ReadData},
{"__PS2ReadStatus", (void *)v0::PS2ReadStatus},
{"__PS2ReadAfterACK", (void *)v0::PS2ReadAfterACK},
{"__PS2ClearOutputBuffer", (void *)v0::PS2ClearOutputBuffer},
{"__PS2ACKTimeout", (void *)v0::PS2ACKTimeout},
{"__AllocateMemory", (void *)v0::AllocateMemory},
{"__FreeMemory", (void *)v0::FreeMemory},
{"__MemoryCopy", (void *)v0::MemoryCopy},
{"__MemorySet", (void *)v0::MemorySet},
{"__MemoryMove", (void *)v0::MemoryMove},
{"__StringLength", (void *)v0::StringLength},
{"__strstr", (void *)v0::_strstr},
{"__MapPages", (void *)v0::MapPages},
{"__UnmapPages", (void *)v0::UnmapPages},
{"__AppendMapFlag", (void *)v0::AppendMapFlag},
{"__RemoveMapFlag", (void *)v0::RemoveMapFlag},
{"_Znwm", (void *)v0::Znwm},
{"_ZdlPvm", (void *)v0::ZdlPvm},
{"__GetPCIDevices", (void *)v0::GetPCIDevices},
{"__InitializePCI", (void *)v0::InitializePCI},
{"__GetBAR", (void *)v0::GetBAR},
{"__iLine", (void *)v0::iLine},
{"__iPin", (void *)v0::iPin},
{"__RegisterDevice", (void *)v0::RegisterDevice},
{"__UnregisterDevice", (void *)v0::UnregisterDevice},
{"__ReportInputEvent", (void *)v0::ReportInputEvent},
};
long __KernelUndefinedFunction(long arg0, long arg1, long arg2, long arg3,
long arg4, long arg5, long arg6, long arg7)
{
debug("%#lx, %#lx, %#lx, %#lx, %#lx, %#lx, %#lx, %#lx",
arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
assert(!"Undefined kernel driver API function called!");
CPU::Stop();
}
void *GetSymbolByName(const char *Name, int Version)
{
switch (Version)
{
case 0:
{
for (auto sym : APISymbols_v0)
{
if (strcmp(Name, sym.Name) != 0)
continue;
debug("Symbol %s found in API version %d", Name, Version);
return sym.Function;
}
break;
}
default:
assert(!"Invalid API version");
}
error("Symbol %s not found in API version %d", Name, Version);
KPrint("Driver API symbol \"%s\" not found!", Name);
return (void *)__KernelUndefinedFunction;
}

View File

@ -0,0 +1,890 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <driver.hpp>
#include <interface/driver.h>
#include <interface/input.h>
#include <memory.hpp>
#include <stropts.h>
#include <ints.hpp>
#include <task.hpp>
#include <printf.h>
#include <exec.hpp>
#include <rand.hpp>
#include <cwalk.h>
#include <md5.h>
#include "../../kernel.h"
using namespace vfs;
namespace Driver
{
/**
* maj = 0
* min:
* 0 - <ROOT>
* 1 - /proc/self
* 2 - /dev/null
* 3 - /dev/zero
* 4 - /dev/random
* 5 - /dev/mem
* 6 - /dev/kcon
* 7 - /dev/tty
* 8 - /dev/ptmx
*
* maj = 1
* min:
* 0 - /dev/input/keyboard
* 1 - /dev/input/mouse
* ..- /dev/input/eventX
*/
TTY::PTMXDevice *ptmx = nullptr;
int __fs_Lookup(struct Inode *_Parent, const char *Name, struct Inode **Result)
{
func("%#lx %s %#lx", _Parent, Name, Result);
assert(_Parent != nullptr);
const char *basename;
size_t length;
cwk_path_get_basename(Name, &basename, &length);
if (basename == NULL)
{
error("Invalid name %s", Name);
return -EINVAL;
}
auto Parent = (Manager::DeviceInode *)_Parent;
for (const auto &child : Parent->Children)
{
debug("Comparing %s with %s", basename, child->Name.c_str());
if (strcmp(child->Name.c_str(), basename) != 0)
continue;
*Result = &child->Node;
return 0;
}
debug("Not found %s", Name);
return -ENOENT;
}
int __fs_Create(struct Inode *_Parent, const char *Name, mode_t Mode, struct Inode **Result)
{
func("%#lx %s %d", _Parent, Name, Mode);
assert(_Parent != nullptr);
/* We expect to be /dev or children of it */
auto Parent = (Manager::DeviceInode *)_Parent;
auto _dev = new Manager::DeviceInode;
_dev->Parent = nullptr;
_dev->ParentInode = _Parent;
_dev->Name = Name;
_dev->Node.Mode = Mode;
_dev->Node.Index = Parent->Node.Index + Parent->Children.size();
Parent->Children.push_back(_dev);
*Result = &_dev->Node;
return 0;
}
ssize_t __fs_Read(struct Inode *Node, void *Buffer, size_t Size, off_t Offset)
{
func("%#lx %d %d", Node, Size, Offset);
switch (Node->GetMajor())
{
case 0:
{
switch (Node->GetMinor())
{
case 2: /* /dev/null */
{
return 0;
}
case 3: /* /dev/zero */
{
if (Size <= 0)
return 0;
memset(Buffer, 0, Size);
return Size;
}
case 4: /* /dev/random */
{
if (Size <= 0)
return 0;
if (Size < sizeof(uint64_t))
{
uint8_t *buf = (uint8_t *)Buffer;
for (size_t i = 0; i < Size; i++)
buf[i] = (uint8_t)(Random::rand16() & 0xFF);
return Size;
}
uint64_t *buf = (uint64_t *)Buffer;
for (size_t i = 0; i < Size / sizeof(uint64_t); i++)
buf[i] = Random::rand64();
return Size;
}
case 5: /* /dev/mem */
{
stub;
return 0;
}
case 6: /* /dev/kcon */
return KernelConsole::CurrentTerminal.load()->Read(Buffer, Size, Offset);
case 7: /* /dev/tty */
{
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
if (tty == nullptr)
return -ENOTTY;
return tty->Read(Buffer, Size, Offset);
}
case 8: /* /dev/ptmx */
return -ENOSYS;
default:
return -ENOENT;
};
break;
}
case 1:
{
switch (Node->GetMinor())
{
case 0: /* /dev/input/keyboard */
{
if (Size < sizeof(KeyboardReport))
return -EINVAL;
size_t nReads = Size / sizeof(KeyboardReport);
KeyboardReport *report = (KeyboardReport *)Buffer;
while (DriverManager->GlobalKeyboardInputReports.Count() == 0)
TaskManager->Yield();
DriverManager->GlobalKeyboardInputReports.Read(report, nReads);
return sizeof(KeyboardReport) * nReads;
}
case 1: /* /dev/input/mouse */
{
if (Size < sizeof(MouseReport))
return -EINVAL;
size_t nReads = Size / sizeof(MouseReport);
MouseReport *report = (MouseReport *)Buffer;
while (DriverManager->GlobalMouseInputReports.Count() == 0)
TaskManager->Yield();
DriverManager->GlobalMouseInputReports.Read(report, nReads);
return sizeof(MouseReport) * nReads;
}
default:
return -ENOENT;
};
}
default:
{
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
DriverManager->GetDrivers();
const auto it = drivers.find(Node->GetMajor());
if (it == drivers.end())
ReturnLogError(-EINVAL, "Driver %d not found", Node->GetMajor());
const Driver::DriverObject *drv = &it->second;
auto dop = drv->DeviceOperations;
auto dOps = dop->find(Node->GetMinor());
if (dOps == dop->end())
ReturnLogError(-EINVAL, "Device %d not found", Node->GetMinor());
AssertReturnError(dOps->second.Ops, -ENOTSUP);
AssertReturnError(dOps->second.Ops->Read, -ENOTSUP);
return dOps->second.Ops->Read(Node, Buffer, Size, Offset);
}
}
}
ssize_t __fs_Write(struct Inode *Node, const void *Buffer, size_t Size, off_t Offset)
{
func("%#lx %p %d %d", Node, Buffer, Size, Offset);
switch (Node->GetMajor())
{
case 0:
{
switch (Node->GetMinor())
{
case 2: /* /dev/null */
{
return Size;
}
case 3: /* /dev/zero */
{
return Size;
}
case 4: /* /dev/random */
{
return Size;
}
case 5: /* /dev/mem */
{
stub;
return 0;
}
case 6: /* /dev/kcon */
return KernelConsole::CurrentTerminal.load()->Write(Buffer, Size, Offset);
case 7: /* /dev/tty */
{
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
if (tty == nullptr)
return -ENOTTY;
return tty->Write(Buffer, Size, Offset);
}
case 8: /* /dev/ptmx */
return -ENOSYS;
default:
return -ENOENT;
};
}
case 1:
{
switch (Node->GetMinor())
{
case 0: /* /dev/input/keyboard */
{
return -ENOTSUP;
}
case 1: /* /dev/input/mouse */
{
return -ENOTSUP;
}
default:
return -ENOENT;
};
}
default:
{
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
DriverManager->GetDrivers();
const auto it = drivers.find(Node->GetMajor());
if (it == drivers.end())
ReturnLogError(-EINVAL, "Driver %d not found", Node->GetMajor());
const Driver::DriverObject *drv = &it->second;
auto dop = drv->DeviceOperations;
auto dOps = dop->find(Node->GetMinor());
if (dOps == dop->end())
ReturnLogError(-EINVAL, "Device %d not found", Node->GetMinor());
AssertReturnError(dOps->second.Ops, -ENOTSUP);
AssertReturnError(dOps->second.Ops->Write, -ENOTSUP);
return dOps->second.Ops->Write(Node, Buffer, Size, Offset);
}
}
}
int __fs_Open(struct Inode *Node, int Flags, mode_t Mode)
{
func("%#lx %d %d", Node, Flags, Mode);
switch (Node->GetMajor())
{
case 0:
{
switch (Node->GetMinor())
{
case 2: /* /dev/null */
case 3: /* /dev/zero */
case 4: /* /dev/random */
case 5: /* /dev/mem */
return -ENOSYS;
case 6: /* /dev/kcon */
return KernelConsole::CurrentTerminal.load()->Open(Flags, Mode);
case 7: /* /dev/tty */
{
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
if (tty == nullptr)
return -ENOTTY;
return tty->Open(Flags, Mode);
}
case 8: /* /dev/ptmx */
return ptmx->Open();
default:
return -ENOENT;
};
}
case 1:
{
switch (Node->GetMinor())
{
case 0: /* /dev/input/keyboard */
case 1: /* /dev/input/mouse */
return -ENOTSUP;
default:
return -ENOENT;
};
}
default:
{
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
DriverManager->GetDrivers();
const auto it = drivers.find(Node->GetMajor());
if (it == drivers.end())
ReturnLogError(-EINVAL, "Driver %d not found", Node->GetMajor());
const Driver::DriverObject *drv = &it->second;
auto dop = drv->DeviceOperations;
auto dOps = dop->find(Node->GetMinor());
if (dOps == dop->end())
ReturnLogError(-EINVAL, "Device %d not found", Node->GetMinor());
AssertReturnError(dOps->second.Ops, -ENOTSUP);
AssertReturnError(dOps->second.Ops->Open, -ENOTSUP);
return dOps->second.Ops->Open(Node, Flags, Mode);
}
}
}
int __fs_Close(struct Inode *Node)
{
func("%#lx", Node);
switch (Node->GetMajor())
{
case 0:
{
switch (Node->GetMinor())
{
case 2: /* /dev/null */
case 3: /* /dev/zero */
case 4: /* /dev/random */
case 5: /* /dev/mem */
return -ENOSYS;
case 6: /* /dev/kcon */
return KernelConsole::CurrentTerminal.load()->Close();
case 7: /* /dev/tty */
{
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
if (tty == nullptr)
return -ENOTTY;
return tty->Close();
}
case 8: /* /dev/ptmx */
return ptmx->Close();
default:
return -ENOENT;
};
}
case 1:
{
switch (Node->GetMinor())
{
case 0: /* /dev/input/keyboard */
case 1: /* /dev/input/mouse */
return -ENOTSUP;
default:
return -ENOENT;
};
}
default:
{
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
DriverManager->GetDrivers();
const auto it = drivers.find(Node->GetMajor());
if (it == drivers.end())
ReturnLogError(-EINVAL, "Driver %d not found", Node->GetMajor());
const Driver::DriverObject *drv = &it->second;
auto dop = drv->DeviceOperations;
auto dOps = dop->find(Node->GetMinor());
if (dOps == dop->end())
ReturnLogError(-EINVAL, "Device %d not found", Node->GetMinor());
AssertReturnError(dOps->second.Ops, -ENOTSUP);
AssertReturnError(dOps->second.Ops->Close, -ENOTSUP);
return dOps->second.Ops->Close(Node);
}
}
}
int __fs_Ioctl(struct Inode *Node, unsigned long Request, void *Argp)
{
func("%#lx %lu %#lx", Node, Request, Argp);
switch (Node->GetMajor())
{
case 0:
{
switch (Node->GetMinor())
{
case 2: /* /dev/null */
case 3: /* /dev/zero */
case 4: /* /dev/random */
case 5: /* /dev/mem */
return -ENOSYS;
case 6: /* /dev/kcon */
return KernelConsole::CurrentTerminal.load()->Ioctl(Request, Argp);
case 7: /* /dev/tty */
{
TTY::TeletypeDriver *tty = (TTY::TeletypeDriver *)thisProcess->tty;
if (tty == nullptr)
return -ENOTTY;
return tty->Ioctl(Request, Argp);
}
case 8: /* /dev/ptmx */
return -ENOSYS;
default:
return -ENOENT;
};
break;
}
case 1:
{
switch (Node->GetMinor())
{
case 0: /* /dev/input/keyboard */
case 1: /* /dev/input/mouse */
return -ENOSYS;
default:
return -ENOENT;
};
}
default:
{
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
DriverManager->GetDrivers();
const auto it = drivers.find(Node->GetMajor());
if (it == drivers.end())
ReturnLogError(-EINVAL, "Driver %d not found", Node->GetMajor());
const Driver::DriverObject *drv = &it->second;
auto dop = drv->DeviceOperations;
auto dOps = dop->find(Node->GetMinor());
if (dOps == dop->end())
ReturnLogError(-EINVAL, "Device %d not found", Node->GetMinor());
AssertReturnError(dOps->second.Ops, -ENOTSUP);
AssertReturnError(dOps->second.Ops->Ioctl, -ENOTSUP);
return dOps->second.Ops->Ioctl(Node, Request, Argp);
}
}
}
__no_sanitize("alignment")
ssize_t __fs_Readdir(struct Inode *_Node, struct kdirent *Buffer, size_t Size, off_t Offset, off_t Entries)
{
func("%#lx %#lx %d %d %d", _Node, Buffer, Size, Offset, Entries);
auto Node = (Manager::DeviceInode *)_Node;
off_t realOffset = Offset;
size_t totalSize = 0;
uint16_t reclen = 0;
struct kdirent *ent = nullptr;
if (Offset == 0)
{
reclen = (uint16_t)(offsetof(struct kdirent, d_name) + strlen(".") + 1);
if (totalSize + reclen >= Size)
return -EINVAL;
ent = (struct kdirent *)((uintptr_t)Buffer + totalSize);
ent->d_ino = Node->Node.Index;
ent->d_off = Offset++;
ent->d_reclen = reclen;
ent->d_type = DT_DIR;
strcpy(ent->d_name, ".");
totalSize += reclen;
}
if (Offset <= 1)
{
reclen = (uint16_t)(offsetof(struct kdirent, d_name) + strlen("..") + 1);
if (totalSize + reclen >= Size)
{
if (realOffset == 1)
return -EINVAL;
return totalSize;
}
ent = (struct kdirent *)((uintptr_t)Buffer + totalSize);
if (Node->Parent)
ent->d_ino = Node->Parent->Node->Index;
else if (Node->ParentInode)
ent->d_ino = Node->ParentInode->Index;
else
{
warn("Parent is null for %s", Node->Name.c_str());
ent->d_ino = Node->Node.Index;
}
ent->d_off = Offset++;
ent->d_reclen = reclen;
ent->d_type = DT_DIR;
strcpy(ent->d_name, "..");
totalSize += reclen;
}
if (!S_ISDIR(Node->Node.Mode))
return -ENOTDIR;
if ((Offset >= 2 ? (Offset - 2) : Offset) > (off_t)Node->Children.size())
return -EINVAL;
off_t entries = 0;
for (const auto &var : Node->Children)
{
debug("iterating \"%s\" inside \"%s\"", var->Name.c_str(), Node->Name.c_str());
if (var->Node.Offset < realOffset)
{
debug("skipping \"%s\" (%d < %d)", var->Name.c_str(), var->Node.Offset, Offset);
continue;
}
if (entries >= Entries)
break;
reclen = (uint16_t)(offsetof(struct kdirent, d_name) + strlen(var->Name.c_str()) + 1);
if (totalSize + reclen >= Size)
break;
ent = (struct kdirent *)((uintptr_t)Buffer + totalSize);
ent->d_ino = var->Node.Index;
ent->d_off = var->Node.Offset;
ent->d_reclen = reclen;
ent->d_type = IFTODT(var->Node.Mode);
strncpy(ent->d_name, var->Name.c_str(), strlen(var->Name.c_str()));
totalSize += reclen;
entries++;
}
if (totalSize + sizeof(struct kdirent) >= Size)
return totalSize;
ent = (struct kdirent *)((uintptr_t)Buffer + totalSize);
ent->d_ino = 0;
ent->d_off = 0;
ent->d_reclen = 0;
ent->d_type = DT_UNKNOWN;
ent->d_name[0] = '\0';
return totalSize;
}
void ManagerDaemonWrapper() { DriverManager->Daemon(); }
void Manager::Daemon()
{
while (true)
{
TaskManager->Sleep(1000);
}
}
dev_t Manager::RegisterInputDevice(std::unordered_map<dev_t, DriverHandlers> *dop,
dev_t DriverID, size_t i, const InodeOperations *Operations)
{
std::string prefix = "event";
for (size_t j = 0; j < 128; j++)
{
std::string deviceName = prefix + std::to_string(j);
FileNode *node = fs->GetByPath(deviceName.c_str(), devInputNode);
if (node)
continue;
/* c rwx r-- r-- */
mode_t mode = S_IRWXU |
S_IRGRP |
S_IROTH |
S_IFCHR;
node = fs->ForceCreate(devInputNode, deviceName.c_str(), mode);
node->Node->SetDevice(DriverID, i);
DriverHandlers dh{};
dh.Ops = Operations;
dh.Node = node->Node;
dh.InputReports = new RingBuffer<InputReport>(16);
dop->insert({i, std::move(dh)});
return i;
}
ReturnLogError(-1, "No available slots for device %d", DriverID);
return -1; /* -Werror=return-type */
}
dev_t Manager::RegisterBlockDevice(std::unordered_map<dev_t, DriverHandlers> *dop,
dev_t DriverID, size_t i, const InodeOperations *Operations)
{
std::string prefix = "event";
for (size_t j = 0; j < 128; j++)
{
std::string deviceName = prefix + std::to_string(j);
FileNode *node = fs->GetByPath(deviceName.c_str(), devInputNode);
if (node)
continue;
/* c rwx r-- r-- */
mode_t mode = S_IRWXU |
S_IRGRP |
S_IROTH |
S_IFBLK;
node = fs->ForceCreate(devInputNode, deviceName.c_str(), mode);
node->Node->SetDevice(DriverID, i);
DriverHandlers dh{};
dh.Ops = Operations;
dh.Node = node->Node;
dh.InputReports = new RingBuffer<InputReport>(16);
dop->insert({i, std::move(dh)});
return i;
}
ReturnLogError(-1, "No available slots for device %d", DriverID);
return -1; /* -Werror=return-type */
}
dev_t Manager::RegisterDevice(dev_t DriverID, DeviceType Type, const InodeOperations *Operations)
{
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
DriverManager->GetDrivers();
const auto it = drivers.find(DriverID);
if (it == drivers.end())
ReturnLogError(-EINVAL, "Driver %d not found", DriverID);
const Driver::DriverObject *drv = &it->second;
auto dop = drv->DeviceOperations;
for (size_t i = 0; i < 128; i++)
{
const auto dOps = dop->find(i);
const auto dOpsEnd = dop->end();
if (dOps != dOpsEnd)
continue;
DeviceType devType = (DeviceType)(Type & DEVICE_TYPE_MASK);
switch (devType)
{
case DEVICE_TYPE_INPUT:
return RegisterInputDevice(dop, DriverID, i, Operations);
case DEVICE_TYPE_BLOCK:
return RegisterBlockDevice(dop, DriverID, i, Operations);
default:
ReturnLogError(-1, "Invalid device type %d", Type);
}
}
ReturnLogError(-1, "No available slots for device %d", DriverID);
}
int Manager::UnregisterDevice(dev_t DriverID, dev_t Device)
{
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
DriverManager->GetDrivers();
const auto it = drivers.find(DriverID);
if (it == drivers.end())
ReturnLogError(-EINVAL, "Driver %d not found", DriverID);
const Driver::DriverObject *drv = &it->second;
auto dop = drv->DeviceOperations;
const auto dOps = dop->find(Device);
if (dOps == dop->end())
ReturnLogError(-EINVAL, "Device %d not found", Device);
dop->erase(dOps);
fixme("remove eventX from /dev/input");
fixme("delete InputReports");
return 0;
}
int Manager::ReportInputEvent(dev_t DriverID, InputReport *Report)
{
std::unordered_map<dev_t, Driver::DriverObject> &drivers =
DriverManager->GetDrivers();
const auto it = drivers.find(DriverID);
if (it == drivers.end())
ReturnLogError(-EINVAL, "Driver %d not found", DriverID);
const Driver::DriverObject *drv = &it->second;
auto dop = drv->DeviceOperations;
auto dOps = dop->find(Report->Device);
if (dOps == dop->end())
ReturnLogError(-EINVAL, "Device %d not found", Report->Device);
dOps->second.InputReports->Write(Report, 1);
switch (Report->Type)
{
case INPUT_TYPE_KEYBOARD:
{
KeyboardReport *kReport = &Report->Keyboard;
GlobalKeyboardInputReports.Write(kReport, 1);
break;
}
case INPUT_TYPE_MOUSE:
{
MouseReport *mReport = &Report->Mouse;
GlobalMouseInputReports.Write(mReport, 1);
break;
}
default:
assert(!"Invalid input type");
}
return 0;
}
void Manager::InitializeDaemonFS()
{
ptmx = new TTY::PTMXDevice;
dev_t MinorID = 0;
DeviceInode *_dev = new DeviceInode;
_dev->Name = "dev";
/* d rwx r-- r-- */
mode_t mode = S_IRWXU |
S_IRGRP |
S_IROTH |
S_IFDIR;
Inode *dev = (Inode *)_dev;
dev->Mode = mode;
dev->Flags = I_FLAG_MOUNTPOINT | I_FLAG_CACHE_KEEP;
FileSystemInfo *fsi = new FileSystemInfo;
fsi->Name = "Device Virtual File System";
fsi->RootName = "dev";
fsi->Flags = I_FLAG_ROOT | I_FLAG_MOUNTPOINT | I_FLAG_CACHE_KEEP;
fsi->SuperOps = {};
fsi->Ops.Lookup = __fs_Lookup;
fsi->Ops.Create = __fs_Create;
fsi->Ops.Read = __fs_Read;
fsi->Ops.Write = __fs_Write;
fsi->Ops.Open = __fs_Open;
fsi->Ops.Close = __fs_Close;
fsi->Ops.Ioctl = __fs_Ioctl;
fsi->Ops.ReadDir = __fs_Readdir;
dev->Device = fs->RegisterFileSystem(fsi, dev);
dev->SetDevice(0, MinorID++);
MinorID++; /* 1 = /proc/self */
devNode = fs->Mount(fs->GetRoot(0), dev, "/dev");
_dev->Parent = devNode->Parent;
_dev->ParentInode = devNode->Parent->Node;
/* d rwx r-- r-- */
mode = S_IRWXU |
S_IRGRP |
S_IROTH |
S_IFDIR;
DeviceInode *input = new DeviceInode;
input->Parent = devNode;
input->ParentInode = devNode->Node;
input->Name = "input";
input->Node.Device = dev->Device;
input->Node.Mode = mode;
input->Node.Flags = I_FLAG_CACHE_KEEP;
input->Node.Offset = _dev->Children.size();
_dev->Children.push_back(input);
devInputNode = fs->GetByPath("input", devNode);
auto createDevice = [](DeviceInode *p1, FileNode *p2, const std::string &name, dev_t maj, dev_t min, mode_t mode)
{
DeviceInode *device = new DeviceInode;
device->Parent = p2;
device->ParentInode = p2->Node;
device->Name = name;
device->Node.Device = p2->Node->Device;
device->Node.Mode = mode;
device->Node.SetDevice(maj, min);
device->Node.Flags = I_FLAG_CACHE_KEEP;
device->Node.Offset = p1->Children.size();
p1->Children.push_back(device);
};
/* c rw- rw- rw- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP |
S_IROTH | S_IWOTH |
S_IFCHR;
createDevice(_dev, devNode, "null", 0, MinorID++, mode);
/* c rw- rw- rw- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP |
S_IROTH | S_IWOTH |
S_IFCHR;
createDevice(_dev, devNode, "zero", 0, MinorID++, mode);
/* c rw- rw- rw- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP |
S_IROTH | S_IWOTH |
S_IFCHR;
createDevice(_dev, devNode, "random", 0, MinorID++, mode);
/* c rw- r-- --- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP |
S_IFCHR;
createDevice(_dev, devNode, "mem", 0, MinorID++, mode);
/* c rw- r-- --- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP |
S_IFCHR;
createDevice(_dev, devNode, "kcon", 0, MinorID++, mode);
/* c rw- rw- rw- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP |
S_IRUSR | S_IWUSR |
S_IFCHR;
createDevice(_dev, devNode, "tty", 0, MinorID++, mode);
/* c rw- rw- rw- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP |
S_IRUSR | S_IWUSR |
S_IFCHR;
createDevice(_dev, devNode, "ptmx", 0, MinorID++, mode);
/* ------------------------------------------------------ */
MinorID = 0;
/* c rw- r-- --- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP |
S_IFCHR;
createDevice(input, devInputNode, "keyboard", 1, MinorID++, mode);
/* c rw- r-- --- */
mode = S_IRUSR | S_IWUSR |
S_IRGRP |
S_IFCHR;
createDevice(input, devInputNode, "mouse", 1, MinorID++, mode);
}
}

View File

@ -0,0 +1,524 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <driver.hpp>
#include <interface/driver.h>
#include <interface/input.h>
#include <memory.hpp>
#include <ints.hpp>
#include <task.hpp>
#include <printf.h>
#include <exec.hpp>
#include <rand.hpp>
#include <cwalk.h>
#include <md5.h>
#include "../../kernel.h"
using namespace vfs;
namespace Driver
{
void Manager::PreloadDrivers()
{
debug("Initializing driver manager");
const char *DriverDirectory = Config.DriverDirectory;
FileNode *drvDirNode = fs->GetByPath(DriverDirectory, nullptr);
if (!drvDirNode)
{
error("Failed to open driver directory %s", DriverDirectory);
KPrint("Failed to open driver directory %s", DriverDirectory);
return;
}
foreach (const auto &drvNode in drvDirNode->Children)
{
debug("Checking driver %s", drvNode->Path.c_str());
if (!drvNode->IsRegularFile())
continue;
if (Execute::GetBinaryType(drvNode->Path) != Execute::BinTypeELF)
{
error("Driver %s is not an ELF binary", drvNode->Path.c_str());
continue;
}
DriverObject drvObj = {.BaseAddress = 0,
.EntryPoint = 0,
.vma = new Memory::VirtualMemoryArea(thisProcess->PageTable),
.Path = drvNode->Path,
.InterruptHandlers = new std::unordered_map<uint8_t, void *>(),
.DeviceOperations = new std::unordered_map<dev_t, DriverHandlers>(),
.ID = DriverIDCounter};
int err = this->LoadDriverFile(drvObj, drvNode);
debug("err = %d (%s)", err, strerror(err));
if (err != 0)
{
error("Failed to load driver %s: %s",
drvNode->Path.c_str(), strerror(err));
delete drvObj.vma;
delete drvObj.InterruptHandlers;
delete drvObj.DeviceOperations;
continue;
}
debug("gdb: \"0x%lX\" %s", drvObj.BaseAddress, drvObj.Name);
Drivers.insert({DriverIDCounter++, drvObj});
}
}
void Manager::LoadAllDrivers()
{
if (Drivers.empty())
{
KPrint("\x1b[1;31;41mNo drivers to load");
return;
}
foreach (auto &var in Drivers)
{
DriverObject &Drv = var.second;
debug("Calling driver %s at %#lx", Drv.Path.c_str(), Drv.EntryPoint);
int (*DrvInit)(dev_t) = (int (*)(dev_t))Drv.EntryPoint;
Drv.ErrorCode = DrvInit(Drv.ID);
if (Drv.ErrorCode < 0)
{
KPrint("FATAL: _start() failed for %s: %s",
Drv.Name, strerror(Drv.ErrorCode));
error("Failed to load driver %s: %s",
Drv.Path.c_str(), strerror(Drv.ErrorCode));
Drv.vma->FreeAllPages();
continue;
}
KPrint("Loading driver %s", Drv.Name);
debug("Calling Probe()=%#lx on driver %s",
Drv.Probe, Drv.Path.c_str());
Drv.ErrorCode = Drv.Probe();
if (Drv.ErrorCode < 0)
{
KPrint("Probe() failed for %s: %s",
Drv.Name, strerror(Drv.ErrorCode));
error("Failed to probe driver %s: %s",
Drv.Path.c_str(), strerror(Drv.ErrorCode));
Drv.vma->FreeAllPages();
continue;
}
debug("Calling driver Entry()=%#lx function on driver %s",
Drv.Entry, Drv.Path.c_str());
Drv.ErrorCode = Drv.Entry();
if (Drv.ErrorCode < 0)
{
KPrint("Entry() failed for %s: %s",
Drv.Name, strerror(Drv.ErrorCode));
error("Failed to initialize driver %s: %s",
Drv.Path.c_str(), strerror(Drv.ErrorCode));
Drv.vma->FreeAllPages();
continue;
}
debug("Loaded driver %s", Drv.Path.c_str());
Drv.Initialized = true;
}
}
void Manager::UnloadAllDrivers()
{
foreach (auto &var in Drivers)
{
DriverObject *Drv = &var.second;
if (!Drv->Initialized)
continue;
debug("Unloading driver %s", Drv->Name);
int err = Drv->Final();
if (err < 0)
{
warn("Failed to unload driver %s: %s",
Drv->Name, strerror(err));
}
if (!Drv->InterruptHandlers->empty())
{
foreach (auto &rInt in * Drv->InterruptHandlers)
{
Interrupts::RemoveHandler((void (*)(CPU::TrapFrame *))rInt.second);
}
Drv->InterruptHandlers->clear();
}
}
Drivers.clear();
}
void Manager::Panic()
{
Memory::Virtual vmm;
if (Drivers.size() == 0)
return;
foreach (auto Driver in Drivers)
{
if (!Driver.second.Initialized)
continue;
trace("Panic on driver %s", Driver.second.Name);
debug("%#lx", Driver.second.Panic);
/* Crash while probing? */
if (Driver.second.Panic && vmm.Check((void *)Driver.second.Panic))
Driver.second.Panic();
else
error("No panic function for driver %s",
Driver.second.Name);
}
}
int Manager::LoadDriverFile(DriverObject &Drv, FileNode *File)
{
trace("Loading driver %s in memory", File->Name.c_str());
Elf_Ehdr ELFHeader{};
File->Read(&ELFHeader, sizeof(Elf_Ehdr), 0);
AssertReturnError(ELFHeader.e_ident[EI_CLASS] == ELFCLASS64, -ENOEXEC);
AssertReturnError(ELFHeader.e_ident[EI_DATA] == ELFDATA2LSB, -ENOEXEC);
AssertReturnError(ELFHeader.e_ident[EI_OSABI] == ELFOSABI_SYSV, -ENOEXEC);
AssertReturnError(ELFHeader.e_ident[EI_ABIVERSION] == 0, -ENOEXEC);
AssertReturnError(ELFHeader.e_type == ET_DYN, -ENOEXEC);
AssertReturnError(ELFHeader.e_machine == EM_X86_64, -ENOEXEC);
AssertReturnError(ELFHeader.e_version == EV_CURRENT, -ENOEXEC);
AssertReturnError(ELFHeader.e_entry != 0x0, -ENOEXEC);
AssertReturnError(ELFHeader.e_shstrndx != SHN_UNDEF, -ENOEXEC);
Drv.EntryPoint = ELFHeader.e_entry;
size_t segSize = 0;
Elf_Phdr phdr{};
for (Elf_Half i = 0; i < ELFHeader.e_phnum; i++)
{
File->Read(&phdr, sizeof(Elf_Phdr), ELFHeader.e_phoff + (i * sizeof(Elf_Phdr)));
if (phdr.p_type == PT_LOAD || phdr.p_type == PT_DYNAMIC)
{
if (segSize < phdr.p_vaddr + phdr.p_memsz)
segSize = phdr.p_vaddr + phdr.p_memsz;
continue;
}
if (phdr.p_type == PT_INTERP)
{
char interp[17];
File->Read(interp, sizeof(interp), phdr.p_offset);
if (strncmp(interp, "/boot/fennix.elf", sizeof(interp)) != 0)
{
error("Interpreter is not /boot/fennix.elf");
return -ENOEXEC;
}
}
}
debug("segSize: %ld", segSize);
Drv.BaseAddress = (uintptr_t)Drv.vma->RequestPages(TO_PAGES(segSize) + 1);
Drv.EntryPoint += Drv.BaseAddress;
debug("Driver %s has entry point %#lx and base %#lx",
File->Name.c_str(), Drv.EntryPoint, Drv.BaseAddress);
Elf64_Shdr sht_strtab{};
Elf64_Shdr sht_symtab{};
Elf_Shdr shstrtab{};
Elf_Shdr shdr{};
__DriverInfo driverInfo{};
File->Read(&shstrtab, sizeof(Elf_Shdr), ELFHeader.e_shoff + (ELFHeader.e_shstrndx * ELFHeader.e_shentsize));
for (Elf_Half i = 0; i < ELFHeader.e_shnum; i++)
{
if (i == ELFHeader.e_shstrndx)
continue;
File->Read(&shdr, ELFHeader.e_shentsize, ELFHeader.e_shoff + (i * ELFHeader.e_shentsize));
switch (shdr.sh_type)
{
case SHT_PROGBITS:
break;
case SHT_SYMTAB:
sht_symtab = shdr;
continue;
case SHT_STRTAB:
sht_strtab = shdr;
continue;
case SHT_NULL:
default:
continue;
}
char symName[16];
File->Read(symName, sizeof(symName), shstrtab.sh_offset + shdr.sh_name);
if (strcmp(symName, ".driver.info") != 0)
continue;
File->Read(&driverInfo, sizeof(__DriverInfo), shdr.sh_offset);
/* Perform relocations */
driverInfo.Name = (const char *)(Drv.BaseAddress + (uintptr_t)driverInfo.Name);
driverInfo.Description = (const char *)(Drv.BaseAddress + (uintptr_t)driverInfo.Description);
driverInfo.Author = (const char *)(Drv.BaseAddress + (uintptr_t)driverInfo.Author);
driverInfo.License = (const char *)(Drv.BaseAddress + (uintptr_t)driverInfo.License);
}
for (size_t h = 0; h < (sht_symtab.sh_size / sizeof(Elf64_Sym)); h++)
{
Elf64_Sym symEntry{};
uintptr_t symOffset = sht_symtab.sh_offset + (h * sizeof(Elf64_Sym));
File->Read(&symEntry, sizeof(Elf64_Sym), symOffset);
if (symEntry.st_name == 0)
continue;
char symName[16];
File->Read(symName, sizeof(symName), sht_strtab.sh_offset + symEntry.st_name);
switch (symEntry.st_shndx)
{
case SHN_UNDEF:
case SHN_ABS:
case SHN_LOPROC /* , SHN_LORESERVE and SHN_BEFORE */:
case SHN_AFTER:
case SHN_HIPROC:
case SHN_COMMON:
case SHN_HIRESERVE:
break;
default:
{
debug("shndx: %d", symEntry.st_shndx);
if (strcmp(symName, "DriverEntry") == 0)
Drv.Entry = (int (*)())(Drv.BaseAddress + symEntry.st_value);
else if (strcmp(symName, "DriverFinal") == 0)
Drv.Final = (int (*)())(Drv.BaseAddress + symEntry.st_value);
else if (strcmp(symName, "DriverPanic") == 0)
Drv.Panic = (int (*)())(Drv.BaseAddress + symEntry.st_value);
else if (strcmp(symName, "DriverProbe") == 0)
Drv.Probe = (int (*)())(Drv.BaseAddress + symEntry.st_value);
debug("Found %s at %#lx", symName, symEntry.st_value);
break;
}
}
}
for (Elf_Half i = 0; i < ELFHeader.e_phnum; i++)
{
File->Read(&phdr, sizeof(Elf_Phdr), ELFHeader.e_phoff + (i * sizeof(Elf_Phdr)));
switch (phdr.p_type)
{
case PT_LOAD:
case PT_DYNAMIC:
{
if (phdr.p_memsz == 0)
continue;
uintptr_t dest = Drv.BaseAddress + phdr.p_vaddr;
debug("Copying PHDR %#lx to %#lx-%#lx (%ld file bytes, %ld mem bytes)",
phdr.p_type, dest, dest + phdr.p_memsz,
phdr.p_filesz, phdr.p_memsz);
if (phdr.p_filesz > 0)
File->Read(dest, phdr.p_filesz, phdr.p_offset);
if (phdr.p_memsz - phdr.p_filesz > 0)
{
void *zero = (void *)(dest + phdr.p_filesz);
memset(zero, 0, phdr.p_memsz - phdr.p_filesz);
}
if (phdr.p_type != PT_DYNAMIC)
break;
Elf64_Dyn *dyn = (Elf64_Dyn *)(Drv.BaseAddress + phdr.p_vaddr);
Elf64_Dyn *relaSize = nullptr;
Elf64_Dyn *pltrelSize = nullptr;
while (dyn->d_tag != DT_NULL)
{
switch (dyn->d_tag)
{
case DT_PLTRELSZ:
{
pltrelSize = dyn;
break;
}
case DT_PLTGOT:
{
Elf_Addr *got = (Elf_Addr *)(Drv.BaseAddress + dyn->d_un.d_ptr);
got[1] = 0;
got[2] = 0;
break;
}
case DT_RELASZ:
{
relaSize = dyn;
break;
}
case DT_PLTREL:
{
AssertReturnError(dyn->d_un.d_val == DT_RELA, -ENOEXEC);
break;
}
default:
break;
}
dyn++;
}
dyn = (Elf64_Dyn *)(Drv.BaseAddress + phdr.p_vaddr);
while (dyn->d_tag != DT_NULL)
{
switch (dyn->d_tag)
{
case DT_RELA: /* .rela.dyn */
{
AssertReturnError(relaSize != nullptr, -ENOEXEC);
Elf64_Rela *rela = (Elf64_Rela *)(Drv.BaseAddress + dyn->d_un.d_ptr);
for (size_t i = 0; i < (relaSize->d_un.d_val / sizeof(Elf64_Rela)); i++)
{
Elf64_Rela *r = &rela[i];
uintptr_t *reloc = (uintptr_t *)(Drv.BaseAddress + r->r_offset);
uintptr_t relocTarget = 0;
switch (ELF64_R_TYPE(r->r_info))
{
case R_X86_64_GLOB_DAT:
case R_X86_64_JUMP_SLOT:
{
relocTarget = Drv.BaseAddress;
break;
}
case R_X86_64_RELATIVE:
case R_X86_64_64:
{
relocTarget = Drv.BaseAddress + r->r_addend;
break;
}
default:
{
fixme("Unhandled relocation type: %#lx",
ELF64_R_TYPE(r->r_info));
break;
}
}
*reloc = relocTarget;
debug("Relocated %#lx to %#lx",
r->r_offset, *reloc);
}
break;
}
case DT_JMPREL: /* .rela.plt */
{
AssertReturnError(pltrelSize != nullptr, -ENOEXEC);
std::vector<Elf64_Dyn> symtab = Execute::ELFGetDynamicTag_x86_64(File, DT_SYMTAB);
Elf64_Sym *symbols = (Elf64_Sym *)((uintptr_t)Drv.BaseAddress + symtab[0].d_un.d_ptr);
std::vector<Elf64_Dyn> StrTab = Execute::ELFGetDynamicTag_x86_64(File, DT_STRTAB);
char *DynStr = (char *)((uintptr_t)Drv.BaseAddress + StrTab[0].d_un.d_ptr);
Elf64_Rela *rela = (Elf64_Rela *)(Drv.BaseAddress + dyn->d_un.d_ptr);
for (size_t i = 0; i < (pltrelSize->d_un.d_val / sizeof(Elf64_Rela)); i++)
{
Elf64_Rela *r = &rela[i];
uintptr_t *reloc = (uintptr_t *)(Drv.BaseAddress + r->r_offset);
switch (ELF64_R_TYPE(r->r_info))
{
case R_X86_64_JUMP_SLOT:
{
Elf64_Xword symIndex = ELF64_R_SYM(r->r_info);
Elf64_Sym *sym = symbols + symIndex;
const char *symName = DynStr + sym->st_name;
debug("Resolving symbol %s", symName);
*reloc = (uintptr_t)GetSymbolByName(symName, driverInfo.Version.APIVersion);
break;
}
default:
{
fixme("Unhandled relocation type: %#lx",
ELF64_R_TYPE(r->r_info));
break;
}
}
}
break;
}
case DT_PLTGOT:
case DT_PLTRELSZ:
case DT_RELASZ:
case DT_PLTREL:
break;
default:
{
fixme("Unhandled dynamic tag: %#lx", dyn->d_tag);
break;
}
}
dyn++;
}
break;
}
case PT_PHDR:
case PT_INTERP:
break;
default:
{
fixme("Unhandled program header type: %#lx", phdr.p_type);
break;
}
}
}
AssertReturnError(driverInfo.Name != nullptr, -EFAULT);
strncpy(Drv.Name, driverInfo.Name, sizeof(Drv.Name));
strncpy(Drv.Description, driverInfo.Description, sizeof(Drv.Description));
strncpy(Drv.Author, driverInfo.Author, sizeof(Drv.Author));
Drv.Version.Major = driverInfo.Version.Major;
Drv.Version.Minor = driverInfo.Version.Minor;
Drv.Version.Patch = driverInfo.Version.Patch;
strncpy(Drv.License, driverInfo.License, sizeof(Drv.License));
return 0;
}
Manager::Manager() { this->InitializeDaemonFS(); }
Manager::~Manager()
{
debug("Unloading drivers");
UnloadAllDrivers();
}
}

View File

@ -0,0 +1,368 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <interface/driver.h>
#include <driver.hpp>
static char ScanCodeConversionTableLower[] = {
[KEY_1] = '1',
[KEY_2] = '2',
[KEY_3] = '3',
[KEY_4] = '4',
[KEY_5] = '5',
[KEY_6] = '6',
[KEY_7] = '7',
[KEY_8] = '8',
[KEY_9] = '9',
[KEY_0] = '0',
[KEY_Q] = 'q',
[KEY_W] = 'w',
[KEY_E] = 'e',
[KEY_R] = 'r',
[KEY_T] = 't',
[KEY_Y] = 'y',
[KEY_U] = 'u',
[KEY_I] = 'i',
[KEY_O] = 'o',
[KEY_P] = 'p',
[KEY_A] = 'a',
[KEY_S] = 's',
[KEY_D] = 'd',
[KEY_F] = 'f',
[KEY_G] = 'g',
[KEY_H] = 'h',
[KEY_J] = 'j',
[KEY_K] = 'k',
[KEY_L] = 'l',
[KEY_Z] = 'z',
[KEY_X] = 'x',
[KEY_C] = 'c',
[KEY_V] = 'v',
[KEY_B] = 'b',
[KEY_N] = 'n',
[KEY_M] = 'm',
[KEY_F1] = 0x00,
[KEY_F2] = 0x00,
[KEY_F3] = 0x00,
[KEY_F4] = 0x00,
[KEY_F5] = 0x00,
[KEY_F6] = 0x00,
[KEY_F7] = 0x00,
[KEY_F8] = 0x00,
[KEY_F9] = 0x00,
[KEY_F10] = 0x00,
[KEY_F11] = 0x00,
[KEY_F12] = 0x00,
[KEYPAD_7] = '7',
[KEYPAD_8] = '8',
[KEYPAD_9] = '9',
[KEYPAD_MINUS] = '-',
[KEYPAD_4] = '4',
[KEYPAD_5] = '5',
[KEYPAD_6] = '6',
[KEYPAD_PLUS] = '+',
[KEYPAD_1] = '1',
[KEYPAD_2] = '2',
[KEYPAD_3] = '3',
[KEYPAD_0] = '0',
[KEYPAD_PERIOD] = '.',
[KEYPAD_RETURN] = '\n',
[KEYPAD_ASTERISK] = '*',
[KEYPAD_SLASH] = '/',
[KEY_LEFT_CTRL] = 0x00,
[KEY_RIGHT_CTRL] = 0x00,
[KEY_LEFT_SHIFT] = 0x00,
[KEY_RIGHT_SHIFT] = 0x00,
[KEY_LEFT_ALT] = 0x00,
[KEY_RIGHT_ALT] = 0x00,
[KEY_ESCAPE] = '\e',
[KEY_MINUS] = '-',
[KEY_EQUAL] = '=',
[KEY_BACKSPACE] = '\b',
[KEY_TAB] = '\t',
[KEY_LEFT_BRACKET] = '[',
[KEY_RIGHT_BRACKET] = ']',
[KEY_RETURN] = '\n',
[KEY_SEMICOLON] = ';',
[KEY_APOSTROPHE] = '\'',
[KEY_BACK_TICK] = '`',
[KEY_BACKSLASH] = '\\',
[KEY_COMMA] = ',',
[KEY_PERIOD] = '.',
[KEY_SLASH] = '/',
[KEY_SPACE] = ' ',
[KEY_CAPS_LOCK] = 0x00,
[KEY_NUM_LOCK] = 0x00,
[KEY_SCROLL_LOCK] = 0x00,
[KEY_PRINT_SCREEN] = 0x00,
[KEY_HOME] = 0x00,
[KEY_UP_ARROW] = 0x00,
[KEY_LEFT_ARROW] = 0x00,
[KEY_RIGHT_ARROW] = 0x00,
[KEY_DOWN_ARROW] = 0x00,
[KEY_PAGE_UP] = 0x00,
[KEY_PAGE_DOWN] = 0x00,
[KEY_END] = 0x00,
[KEY_INSERT] = 0x00,
[KEY_DELETE] = 0x00,
[KEY_LEFT_GUI] = 0x00,
[KEY_RIGHT_GUI] = 0x00,
[KEY_APPS] = 0x00,
[KEY_MULTIMEDIA_PREV_TRACK] = 0x00,
[KEY_MULTIMEDIA_NEXT_TRACK] = 0x00,
[KEY_MULTIMEDIA_MUTE] = 0x00,
[KEY_MULTIMEDIA_CALCULATOR] = 0x00,
[KEY_MULTIMEDIA_PLAY] = 0x00,
[KEY_MULTIMEDIA_STOP] = 0x00,
[KEY_MULTIMEDIA_VOL_DOWN] = 0x00,
[KEY_MULTIMEDIA_VOL_UP] = 0x00,
[KEY_MULTIMEDIA_WWW_HOME] = 0x00,
[KEY_MULTIMEDIA_WWW_SEARCH] = 0x00,
[KEY_MULTIMEDIA_WWW_FAVORITES] = 0x00,
[KEY_MULTIMEDIA_WWW_REFRESH] = 0x00,
[KEY_MULTIMEDIA_WWW_STOP] = 0x00,
[KEY_MULTIMEDIA_WWW_FORWARD] = 0x00,
[KEY_MULTIMEDIA_WWW_BACK] = 0x00,
[KEY_MULTIMEDIA_MY_COMPUTER] = 0x00,
[KEY_MULTIMEDIA_EMAIL] = 0x00,
[KEY_MULTIMEDIA_MEDIA_SELECT] = 0x00,
[KEY_ACPI_POWER] = 0x00,
[KEY_ACPI_SLEEP] = 0x00,
[KEY_ACPI_WAKE] = 0x00};
static char ScanCodeConversionTableUpper[] = {
[KEY_1] = '!',
[KEY_2] = '@',
[KEY_3] = '#',
[KEY_4] = '$',
[KEY_5] = '%',
[KEY_6] = '^',
[KEY_7] = '&',
[KEY_8] = '*',
[KEY_9] = '(',
[KEY_0] = ')',
[KEY_Q] = 'Q',
[KEY_W] = 'W',
[KEY_E] = 'E',
[KEY_R] = 'R',
[KEY_T] = 'T',
[KEY_Y] = 'Y',
[KEY_U] = 'U',
[KEY_I] = 'I',
[KEY_O] = 'O',
[KEY_P] = 'P',
[KEY_A] = 'A',
[KEY_S] = 'S',
[KEY_D] = 'D',
[KEY_F] = 'F',
[KEY_G] = 'G',
[KEY_H] = 'H',
[KEY_J] = 'J',
[KEY_K] = 'K',
[KEY_L] = 'L',
[KEY_Z] = 'Z',
[KEY_X] = 'X',
[KEY_C] = 'C',
[KEY_V] = 'V',
[KEY_B] = 'B',
[KEY_N] = 'N',
[KEY_M] = 'M',
[KEY_F1] = 0x00,
[KEY_F2] = 0x00,
[KEY_F3] = 0x00,
[KEY_F4] = 0x00,
[KEY_F5] = 0x00,
[KEY_F6] = 0x00,
[KEY_F7] = 0x00,
[KEY_F8] = 0x00,
[KEY_F9] = 0x00,
[KEY_F10] = 0x00,
[KEY_F11] = 0x00,
[KEY_F12] = 0x00,
[KEYPAD_7] = '7',
[KEYPAD_8] = '8',
[KEYPAD_9] = '9',
[KEYPAD_MINUS] = '-',
[KEYPAD_4] = '4',
[KEYPAD_5] = '5',
[KEYPAD_6] = '6',
[KEYPAD_PLUS] = '+',
[KEYPAD_1] = '1',
[KEYPAD_2] = '2',
[KEYPAD_3] = '3',
[KEYPAD_0] = '0',
[KEYPAD_PERIOD] = '.',
[KEYPAD_RETURN] = '\n',
[KEYPAD_ASTERISK] = '*',
[KEYPAD_SLASH] = '/',
[KEY_LEFT_CTRL] = 0x00,
[KEY_RIGHT_CTRL] = 0x00,
[KEY_LEFT_SHIFT] = 0x00,
[KEY_RIGHT_SHIFT] = 0x00,
[KEY_LEFT_ALT] = 0x00,
[KEY_RIGHT_ALT] = 0x00,
[KEY_ESCAPE] = '\e',
[KEY_MINUS] = '_',
[KEY_EQUAL] = '+',
[KEY_BACKSPACE] = '\b',
[KEY_TAB] = '\t',
[KEY_LEFT_BRACKET] = '{',
[KEY_RIGHT_BRACKET] = '}',
[KEY_RETURN] = '\n',
[KEY_SEMICOLON] = ':',
[KEY_APOSTROPHE] = '\"',
[KEY_BACK_TICK] = '~',
[KEY_BACKSLASH] = '|',
[KEY_COMMA] = '<',
[KEY_PERIOD] = '>',
[KEY_SLASH] = '?',
[KEY_SPACE] = ' ',
[KEY_CAPS_LOCK] = 0x00,
[KEY_NUM_LOCK] = 0x00,
[KEY_SCROLL_LOCK] = 0x00,
[KEY_PRINT_SCREEN] = 0x00,
[KEY_HOME] = 0x00,
[KEY_UP_ARROW] = 0x00,
[KEY_LEFT_ARROW] = 0x00,
[KEY_RIGHT_ARROW] = 0x00,
[KEY_DOWN_ARROW] = 0x00,
[KEY_PAGE_UP] = 0x00,
[KEY_PAGE_DOWN] = 0x00,
[KEY_END] = 0x00,
[KEY_INSERT] = 0x00,
[KEY_DELETE] = 0x00,
[KEY_LEFT_GUI] = 0x00,
[KEY_RIGHT_GUI] = 0x00,
[KEY_APPS] = 0x00,
[KEY_MULTIMEDIA_PREV_TRACK] = 0x00,
[KEY_MULTIMEDIA_NEXT_TRACK] = 0x00,
[KEY_MULTIMEDIA_MUTE] = 0x00,
[KEY_MULTIMEDIA_CALCULATOR] = 0x00,
[KEY_MULTIMEDIA_PLAY] = 0x00,
[KEY_MULTIMEDIA_STOP] = 0x00,
[KEY_MULTIMEDIA_VOL_DOWN] = 0x00,
[KEY_MULTIMEDIA_VOL_UP] = 0x00,
[KEY_MULTIMEDIA_WWW_HOME] = 0x00,
[KEY_MULTIMEDIA_WWW_SEARCH] = 0x00,
[KEY_MULTIMEDIA_WWW_FAVORITES] = 0x00,
[KEY_MULTIMEDIA_WWW_REFRESH] = 0x00,
[KEY_MULTIMEDIA_WWW_STOP] = 0x00,
[KEY_MULTIMEDIA_WWW_FORWARD] = 0x00,
[KEY_MULTIMEDIA_WWW_BACK] = 0x00,
[KEY_MULTIMEDIA_MY_COMPUTER] = 0x00,
[KEY_MULTIMEDIA_EMAIL] = 0x00,
[KEY_MULTIMEDIA_MEDIA_SELECT] = 0x00,
[KEY_ACPI_POWER] = 0x00,
[KEY_ACPI_SLEEP] = 0x00,
[KEY_ACPI_WAKE] = 0x00};
#ifdef DEBUG
static const char *ScanCodeDebugNames[] = {
"KEY_1", "KEY_2", "KEY_3", "KEY_4", "KEY_5", "KEY_6", "KEY_7", "KEY_8",
"KEY_9", "KEY_0", "KEY_Q", "KEY_W", "KEY_E", "KEY_R", "KEY_T", "KEY_Y",
"KEY_U", "KEY_I", "KEY_O", "KEY_P", "KEY_A", "KEY_S", "KEY_D", "KEY_F",
"KEY_G", "KEY_H", "KEY_J", "KEY_K", "KEY_L", "KEY_Z", "KEY_X", "KEY_C",
"KEY_V", "KEY_B", "KEY_N", "KEY_M", "KEY_F1", "KEY_F2", "KEY_F3", "KEY_F4",
"KEY_F5", "KEY_F6", "KEY_F7", "KEY_F8", "KEY_F9", "KEY_F10", "KEY_F11",
"KEY_F12", "KEYPAD_7", "KEYPAD_8", "KEYPAD_9", "KEYPAD_MINUS", "KEYPAD_4",
"KEYPAD_5", "KEYPAD_6", "KEYPAD_PLUS", "KEYPAD_1", "KEYPAD_2", "KEYPAD_3",
"KEYPAD_0", "KEYPAD_PERIOD", "KEYPAD_RETURN", "KEYPAD_ASTERISK", "KEYPAD_SLASH",
"KEY_LEFT_CTRL", "KEY_RIGHT_CTRL", "KEY_LEFT_SHIFT", "KEY_RIGHT_SHIFT",
"KEY_LEFT_ALT", "KEY_RIGHT_ALT", "KEY_ESCAPE", "KEY_MINUS", "KEY_EQUAL",
"KEY_BACKSPACE", "KEY_TAB", "KEY_LEFT_BRACKET", "KEY_RIGHT_BRACKET",
"KEY_RETURN", "KEY_SEMICOLON", "KEY_APOSTROPHE", "KEY_BACK_TICK",
"KEY_BACKSLASH", "KEY_COMMA", "KEY_PERIOD", "KEY_SLASH", "KEY_SPACE",
"KEY_CAPS_LOCK", "KEY_NUM_LOCK", "KEY_SCROLL_LOCK", "KEY_PRINT_SCREEN",
"KEY_HOME", "KEY_UP_ARROW", "KEY_LEFT_ARROW", "KEY_RIGHT_ARROW",
"KEY_DOWN_ARROW", "KEY_PAGE_UP", "KEY_PAGE_DOWN", "KEY_END", "KEY_INSERT",
"KEY_DELETE", "KEY_LEFT_GUI", "KEY_RIGHT_GUI", "KEY_APPS",
"KEY_MULTIMEDIA_PREV_TRACK", "KEY_MULTIMEDIA_NEXT_TRACK", "KEY_MULTIMEDIA_MUTE",
"KEY_MULTIMEDIA_CALCULATOR", "KEY_MULTIMEDIA_PLAY", "KEY_MULTIMEDIA_STOP",
"KEY_MULTIMEDIA_VOL_DOWN", "KEY_MULTIMEDIA_VOL_UP", "KEY_MULTIMEDIA_WWW_HOME",
"KEY_MULTIMEDIA_WWW_SEARCH", "KEY_MULTIMEDIA_WWW_FAVORITES",
"KEY_MULTIMEDIA_WWW_REFRESH", "KEY_MULTIMEDIA_WWW_STOP",
"KEY_MULTIMEDIA_WWW_FORWARD", "KEY_MULTIMEDIA_WWW_BACK",
"KEY_MULTIMEDIA_MY_COMPUTER", "KEY_MULTIMEDIA_EMAIL",
"KEY_MULTIMEDIA_MEDIA_SELECT", "KEY_ACPI_POWER", "KEY_ACPI_SLEEP", "KEY_ACPI_WAKE"};
#endif
namespace Driver
{
char GetScanCode(uint8_t ScanCode, bool Upper)
{
ScanCode &= 0x7F; /* Remove KEY_PRESSED bit */
if (ScanCode >= sizeof(ScanCodeConversionTableLower))
{
warn("Unknown scancode %x", ScanCode);
return 0x00;
}
// debug("Scancode %x (%s)", ScanCode, ScanCodeDebugNames[ScanCode]);
return Upper
? ScanCodeConversionTableUpper[ScanCode]
: ScanCodeConversionTableLower[ScanCode];
}
bool IsValidChar(uint8_t ScanCode)
{
ScanCode &= 0x7F; /* Remove KEY_PRESSED bit */
if (ScanCode >= sizeof(ScanCodeConversionTableLower))
return false;
if (ScanCode > KEY_M)
{
if (ScanCode < KEYPAD_7)
return false; /* F1 - F12 */
switch (ScanCode)
{
case KEY_MINUS:
case KEY_EQUAL:
case KEY_LEFT_BRACKET:
case KEY_RIGHT_BRACKET:
case KEY_RETURN:
case KEY_SEMICOLON:
case KEY_APOSTROPHE:
case KEY_BACK_TICK:
case KEY_BACKSLASH:
case KEY_COMMA:
case KEY_PERIOD:
case KEY_SLASH:
case KEY_SPACE:
return true;
default:
return false;
}
}
return true;
}
}

334
Kernel/core/dsdt.cpp Normal file
View File

@ -0,0 +1,334 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <acpi.hpp>
#include <time.hpp>
#include <debug.h>
#include <smp.hpp>
#include <io.h>
#if defined(a64)
#include "../arch/amd64/cpu/apic.hpp"
#elif defined(a32)
#include "../arch/i386/cpu/apic.hpp"
#endif
#include "../kernel.h"
#define ACPI_TIMER 0x0001
#define ACPI_BUSMASTER 0x0010
#define ACPI_GLOBAL 0x0020
#define ACPI_POWER_BUTTON 0x0100
#define ACPI_SLEEP_BUTTON 0x0200
#define ACPI_RTC_ALARM 0x0400
#define ACPI_PCIE_WAKE 0x4000
#define ACPI_WAKE 0x8000
extern std::atomic<bool> ExceptionLock;
namespace ACPI
{
__always_inline inline bool IsCanonical(uint64_t Address)
{
return ((Address <= 0x00007FFFFFFFFFFF) ||
((Address >= 0xFFFF800000000000) &&
(Address <= 0xFFFFFFFFFFFFFFFF)));
}
#define ACPI_ENABLED 0x0001
#define ACPI_SLEEP 0x2000
#define ACPI_GAS_MMIO 0
#define ACPI_GAS_IO 1
#define ACPI_GAS_PCI 2
void DSDT::OnInterruptReceived(CPU::TrapFrame *)
{
debug("SCI Handle Triggered");
uint16_t Event = 0;
{
uint16_t a = 0, b = 0;
if (acpi->FADT->PM1aEventBlock)
{
a = inw(s_cst(uint16_t, acpi->FADT->PM1aEventBlock));
outw(s_cst(uint16_t, acpi->FADT->PM1aEventBlock), a);
}
if (acpi->FADT->PM1bEventBlock)
{
b = inw(s_cst(uint16_t, acpi->FADT->PM1bEventBlock));
outw(s_cst(uint16_t, acpi->FADT->PM1bEventBlock), b);
}
Event = a | b;
}
#ifdef DEBUG
char dbgEvStr[128];
dbgEvStr[0] = '\0';
if (Event & ACPI_BUSMASTER)
strcat(dbgEvStr, "BUSMASTER ");
if (Event & ACPI_GLOBAL)
strcat(dbgEvStr, "GLOBAL ");
if (Event & ACPI_POWER_BUTTON)
strcat(dbgEvStr, "POWER_BUTTON ");
if (Event & ACPI_SLEEP_BUTTON)
strcat(dbgEvStr, "SLEEP_BUTTON ");
if (Event & ACPI_RTC_ALARM)
strcat(dbgEvStr, "RTC_ALARM ");
if (Event & ACPI_PCIE_WAKE)
strcat(dbgEvStr, "PCIE_WAKE ");
if (Event & ACPI_WAKE)
strcat(dbgEvStr, "WAKE ");
if (Event & ACPI_TIMER)
strcat(dbgEvStr, "ACPI_TIMER ");
KPrint("SCI Event: %s", dbgEvStr);
#endif
if (Event & ACPI_BUSMASTER)
{
fixme("ACPI Busmaster");
}
else if (Event & ACPI_GLOBAL)
{
fixme("ACPI Global");
}
else if (Event & ACPI_POWER_BUTTON)
{
if (ExceptionLock.load())
{
this->Shutdown();
CPU::Stop();
}
Tasking::PCB *pcb = thisProcess;
if (pcb && !pcb->GetContext()->IsPanic())
{
Tasking::Task *ctx = pcb->GetContext();
ctx->CreateThread(ctx->GetKernelProcess(),
Tasking::IP(KST_Shutdown))
->Rename("Shutdown");
}
else
KernelShutdownThread(false);
}
else if (Event & ACPI_SLEEP_BUTTON)
{
fixme("ACPI Sleep Button");
}
else if (Event & ACPI_RTC_ALARM)
{
fixme("ACPI RTC Alarm");
}
else if (Event & ACPI_PCIE_WAKE)
{
fixme("ACPI PCIe Wake");
}
else if (Event & ACPI_WAKE)
{
fixme("ACPI Wake");
}
else if (Event & ACPI_TIMER)
{
fixme("ACPI Timer");
}
else
{
error("ACPI unknown event %#lx on CPU %d",
Event, GetCurrentCPU()->ID);
KPrint("ACPI unknown event %#lx on CPU %d",
Event, GetCurrentCPU()->ID);
}
}
void DSDT::Shutdown()
{
trace("Shutting down...");
if (SCI_EN != 1)
{
error("ACPI Shutdown not supported");
return;
}
if (inw(s_cst(uint16_t, PM1a_CNT) & SCI_EN) == 0)
{
KPrint("ACPI was disabled, enabling...");
if (SMI_CMD == 0 || ACPI_ENABLE == 0)
{
error("ACPI Shutdown not supported");
KPrint("ACPI Shutdown not supported");
return;
}
outb(s_cst(uint16_t, SMI_CMD), ACPI_ENABLE);
uint16_t Timeout = 3000;
while ((inw(s_cst(uint16_t, PM1a_CNT)) & SCI_EN) == 0 && Timeout-- > 0)
;
if (Timeout == 0)
{
error("ACPI Shutdown not supported");
KPrint("ACPI Shutdown not supported");
return;
}
if (PM1b_CNT)
{
Timeout = 3000;
while ((inw(s_cst(uint16_t, PM1b_CNT)) & SCI_EN) == 0 && Timeout-- > 0)
;
}
}
outw(s_cst(uint16_t, PM1a_CNT), SLP_TYPa | SLP_EN);
if (PM1b_CNT)
outw(s_cst(uint16_t, PM1b_CNT), SLP_TYPb | SLP_EN);
}
void DSDT::Reboot()
{
trace("Rebooting...");
switch (acpi->FADT->ResetReg.AddressSpace)
{
case ACPI_GAS_MMIO:
{
*(uint8_t *)(acpi->FADT->ResetReg.Address) = acpi->FADT->ResetValue;
break;
}
case ACPI_GAS_IO:
{
outb(s_cst(uint16_t, acpi->FADT->ResetReg.Address), acpi->FADT->ResetValue);
break;
}
case ACPI_GAS_PCI:
{
fixme("ACPI_GAS_PCI not supported.");
/*
seg - 0
bus - 0
dev - (FADT->ResetReg.Address >> 32) & 0xFFFF
function - (FADT->ResetReg.Address >> 16) & 0xFFFF
offset - FADT->ResetReg.Address & 0xFFFF
value - FADT->ResetValue
*/
break;
}
default:
{
error("Unknown reset register address space: %d", acpi->FADT->ResetReg.AddressSpace);
break;
}
}
}
DSDT::DSDT(ACPI *acpi) : Interrupts::Handler(acpi->FADT->SCI_Interrupt, true)
{
/* TODO: AML Interpreter */
this->acpi = acpi;
uint64_t Address = ((IsCanonical(acpi->FADT->X_Dsdt) && acpi->XSDTSupported) ? acpi->FADT->X_Dsdt : acpi->FADT->Dsdt);
uint8_t *S5Address = (uint8_t *)(Address) + 36;
ACPI::ACPI::ACPIHeader *Header = (ACPI::ACPI::ACPIHeader *)Address;
Memory::Virtual vmm;
if (!vmm.Check(Header))
{
warn("DSDT is not mapped");
debug("DSDT: %#lx", Address);
vmm.Map(Header, Header, Memory::RW);
}
size_t Length = Header->Length;
vmm.Map(Header, Header, Length, Memory::RW);
while (Length-- > 0)
{
if (!memcmp(S5Address, "_S5_", 4))
break;
S5Address++;
}
if (Length <= 0)
{
warn("_S5_ not present in ACPI");
return;
}
if ((*(S5Address - 1) == 0x08 ||
(*(S5Address - 2) == 0x08 &&
*(S5Address - 1) == '\\')) &&
*(S5Address + 4) == 0x12)
{
S5Address += 5;
S5Address += ((*S5Address & 0xC0) >> 6) + 2;
if (*S5Address == 0x0A)
S5Address++;
SLP_TYPa = s_cst(uint16_t, *(S5Address) << 10);
S5Address++;
if (*S5Address == 0x0A)
S5Address++;
SLP_TYPb = s_cst(uint16_t, *(S5Address) << 10);
SMI_CMD = acpi->FADT->SMI_CommandPort;
ACPI_ENABLE = acpi->FADT->AcpiEnable;
ACPI_DISABLE = acpi->FADT->AcpiDisable;
PM1a_CNT = acpi->FADT->PM1aControlBlock;
PM1b_CNT = acpi->FADT->PM1bControlBlock;
PM1_CNT_LEN = acpi->FADT->PM1ControlLength;
SLP_EN = 1 << 13;
SCI_EN = 1;
KPrint("ACPI Shutdown is supported");
ACPIShutdownSupported = true;
{
const uint16_t value = /*ACPI_TIMER |*/ ACPI_BUSMASTER | ACPI_GLOBAL |
ACPI_POWER_BUTTON | ACPI_SLEEP_BUTTON | ACPI_RTC_ALARM |
ACPI_PCIE_WAKE | ACPI_WAKE;
uint16_t a = s_cst(uint16_t, acpi->FADT->PM1aEventBlock + (acpi->FADT->PM1EventLength / 2));
uint16_t b = s_cst(uint16_t, acpi->FADT->PM1bEventBlock + (acpi->FADT->PM1EventLength / 2));
debug("SCI Event: %#x [a:%#x b:%#x]", value, a, b);
if (acpi->FADT->PM1aEventBlock)
outw(a, value);
if (acpi->FADT->PM1bEventBlock)
outw(b, value);
}
{
uint16_t a = 0, b = 0;
if (acpi->FADT->PM1aEventBlock)
{
a = inw(s_cst(uint16_t, acpi->FADT->PM1aEventBlock));
outw(s_cst(uint16_t, acpi->FADT->PM1aEventBlock), a);
}
if (acpi->FADT->PM1bEventBlock)
{
b = inw(s_cst(uint16_t, acpi->FADT->PM1bEventBlock));
outw(s_cst(uint16_t, acpi->FADT->PM1bEventBlock), b);
}
}
((APIC::APIC *)Interrupts::apic[0])->RedirectIRQ(0, uint8_t(acpi->FADT->SCI_Interrupt), 1);
return;
}
warn("Failed to parse _S5_ in ACPI");
SCI_EN = 0;
}
DSDT::~DSDT()
{
}
}

View File

@ -0,0 +1,497 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <ints.hpp>
#include <syscalls.hpp>
#include <acpi.hpp>
#include <smp.hpp>
#include <vector>
#include <io.h>
#if defined(a64)
#include "../arch/amd64/cpu/apic.hpp"
#include "../arch/amd64/cpu/gdt.hpp"
#include "../arch/amd64/cpu/idt.hpp"
#elif defined(a32)
#include "../arch/i386/cpu/apic.hpp"
#include "../arch/i386/cpu/gdt.hpp"
#include "../arch/i386/cpu/idt.hpp"
#elif defined(aa64)
#endif
#include "../kernel.h"
void HandleException(CPU::ExceptionFrame *Frame);
extern "C" nsa void ExceptionHandler(void *Frame)
{
HandleException((CPU::ExceptionFrame *)Frame);
}
namespace Interrupts
{
struct Event
{
/** Interrupt number */
int IRQ;
/** Raw pointer to the Handler */
void *Data;
/** Is this a handler? */
bool IsHandler;
/**
* Function to call if this is not a Handler
*
* Used for C-style callbacks.
*/
void (*Callback)(CPU::TrapFrame *);
/**
* Context for the callback
*
* Used for C-style callbacks if the callback needs a context.
* (e.g. a driver)
*/
void *Context;
/**
* Priority of the event
*
* Used for sorting the events.
*
* This is incremented every time the event is called.
*
* This will improve performance by reducing the time
* spent on searching for the event.
*/
unsigned long Priority;
/**
* If this is true, the event is critical.
*
* This will make sure that the event will not be
* removed by the kernel.
*
* This is used to prevent the kernel from removing
* ACPI related handlers. (SCI interrupts)
*/
bool Critical;
};
std::list<Event> RegisteredEvents;
#ifdef DEBUG
#define SORT_DIVIDER 10
#else
#define SORT_DIVIDER 1
#endif
#define SORT_START 10000
std::atomic_uint SortEvents = SORT_START / SORT_DIVIDER;
constexpr uint32_t SORT_ITR = (SORT_START * 100) / SORT_DIVIDER;
#if defined(a86)
/* APIC::APIC */ void *apic[MAX_CPU] = {nullptr};
/* APIC::Timer */ void *apicTimer[MAX_CPU] = {nullptr};
#elif defined(aa64)
#endif
void Initialize(int Core)
{
#if defined(a64)
GlobalDescriptorTable::Init(Core);
InterruptDescriptorTable::Init(Core);
CPUData *CoreData = GetCPU(Core);
CoreData->Checksum = CPU_DATA_CHECKSUM;
CPU::x64::wrmsr(CPU::x64::MSR_GS_BASE, (uint64_t)CoreData);
CPU::x64::wrmsr(CPU::x64::MSR_SHADOW_GS_BASE, (uint64_t)CoreData);
CoreData->ID = Core;
CoreData->IsActive = true;
CoreData->Stack = (uintptr_t)StackManager.Allocate(STACK_SIZE) + STACK_SIZE;
if (CoreData->Checksum != CPU_DATA_CHECKSUM)
{
KPrint("CPU %d checksum mismatch! %x != %x",
Core, CoreData->Checksum, CPU_DATA_CHECKSUM);
CPU::Stop();
}
debug("Stack for core %d is %#lx (Address: %#lx)",
Core, CoreData->Stack, CoreData->Stack - STACK_SIZE);
InitializeSystemCalls();
#elif defined(a32)
GlobalDescriptorTable::Init(Core);
InterruptDescriptorTable::Init(Core);
CPUData *CoreData = GetCPU(Core);
CoreData->Checksum = CPU_DATA_CHECKSUM;
CPU::x32::wrmsr(CPU::x32::MSR_GS_BASE, (uint64_t)CoreData);
CPU::x32::wrmsr(CPU::x32::MSR_SHADOW_GS_BASE, (uint64_t)CoreData);
CoreData->ID = Core;
CoreData->IsActive = true;
CoreData->Stack = (uintptr_t)StackManager.Allocate(STACK_SIZE) + STACK_SIZE;
if (CoreData->Checksum != CPU_DATA_CHECKSUM)
{
KPrint("CPU %d checksum mismatch! %x != %x",
Core, CoreData->Checksum, CPU_DATA_CHECKSUM);
CPU::Stop();
}
debug("Stack for core %d is %#lx (Address: %#lx)",
Core, CoreData->Stack, CoreData->Stack - STACK_SIZE);
#elif defined(aa64)
warn("aarch64 is not supported yet");
#endif
}
void Enable(int Core)
{
#if defined(a86)
if (((ACPI::MADT *)PowerManager->GetMADT())->LAPICAddress != nullptr)
{
// TODO: This function is called by SMP too. Do not initialize timers that doesn't support multiple cores.
apic[Core] = new APIC::APIC(Core);
if (Core == Config.IOAPICInterruptCore) // Redirect IRQs to the specified core.
((APIC::APIC *)apic[Core])->RedirectIRQs(uint8_t(Core));
}
else
{
error("LAPIC not found");
// TODO: PIC
}
#elif defined(aa64)
warn("aarch64 is not supported yet");
#endif
CPU::Interrupts(CPU::Enable);
}
void InitializeTimer(int Core)
{
// TODO: This function is called by SMP too. Do not initialize timers that doesn't support multiple cores.
#if defined(a86)
if (apic[Core] != nullptr)
apicTimer[Core] = new APIC::Timer((APIC::APIC *)apic[Core]);
else
{
fixme("apic not found");
}
#elif defined(aa64)
warn("aarch64 is not supported yet");
#endif
}
nsa void RemoveAll()
{
forItr(itr, RegisteredEvents)
{
if (itr->Critical)
continue;
RegisteredEvents.erase(itr);
}
}
void AddHandler(void (*Callback)(CPU::TrapFrame *),
int InterruptNumber,
void *ctx, bool Critical)
{
/* Just log a warning if the interrupt is already registered. */
foreach (auto ev in RegisteredEvents)
{
if (ev.IRQ == InterruptNumber &&
ev.Callback == Callback)
{
warn("IRQ%d is already registered.",
InterruptNumber);
}
}
Event newEvent =
{InterruptNumber, /* IRQ */
nullptr, /* Data */
false, /* IsHandler */
Callback, /* Callback */
ctx, /* Context */
0, /* Priority */
Critical}; /* Critical */
RegisteredEvents.push_back(newEvent);
debug("Registered interrupt handler for IRQ%d to %#lx",
InterruptNumber, Callback);
}
void RemoveHandler(void (*Callback)(CPU::TrapFrame *), int InterruptNumber)
{
forItr(itr, RegisteredEvents)
{
if (itr->IRQ == InterruptNumber &&
itr->Callback == Callback)
{
RegisteredEvents.erase(itr);
debug("Unregistered interrupt handler for IRQ%d to %#lx",
InterruptNumber, Callback);
return;
}
}
warn("Event %d not found.", InterruptNumber);
}
void RemoveHandler(void (*Callback)(CPU::TrapFrame *))
{
forItr(itr, RegisteredEvents)
{
if (itr->Callback == Callback)
{
debug("Removing handle %d %#lx", itr->IRQ,
itr->IsHandler
? itr->Data
: (void *)itr->Callback);
RegisteredEvents.erase(itr);
}
}
warn("Handle not found.");
}
void RemoveHandler(int InterruptNumber)
{
forItr(itr, RegisteredEvents)
{
if (itr->IRQ == InterruptNumber)
{
debug("Removing handle %d %#lx", itr->IRQ,
itr->IsHandler
? itr->Data
: (void *)itr->Callback);
RegisteredEvents.erase(itr);
}
}
warn("IRQ%d not found.", InterruptNumber);
}
nsa inline void ReturnFromInterrupt()
{
CPUData *CoreData = GetCurrentCPU();
int Core = CoreData->ID;
/* TODO: This should be done when the os is idle */
if (SortEvents++ > SORT_ITR)
{
debug("Sorting events");
SortEvents = 0;
RegisteredEvents.sort([](const Event &a, const Event &b)
{ return a.Priority < b.Priority; });
#ifdef DEBUG
foreach (auto ev in RegisteredEvents)
{
void *fct = ev.IsHandler
? ev.Data
: (void *)ev.Callback;
const char *symbol = ev.IsHandler
? "class"
: KernelSymbolTable->GetSymbol((uintptr_t)fct);
debug("Event IRQ%d [%#lx %s] has priority %ld",
ev.IRQ, fct, symbol, ev.Priority);
}
#endif
}
if (likely(apic[Core]))
{
APIC::APIC *this_apic = (APIC::APIC *)apic[Core];
this_apic->EOI();
return;
}
else
fixme("APIC not found for core %d", Core);
// TODO: Handle PIC too
assert(!"EOI not handled.");
CPU::Stop();
}
extern "C" nsa void MainInterruptHandler(void *Data)
{
class AutoSwitchPageTable
{
private:
void *Original;
public:
AutoSwitchPageTable()
{
#if defined(a86)
asmv("mov %%cr3, %0" : "=r"(Original));
#endif
if (likely(Original == KernelPageTable))
return;
#if defined(a86)
asmv("mov %0, %%cr3" : : "r"(KernelPageTable));
#endif
}
~AutoSwitchPageTable()
{
if (likely(Original == KernelPageTable))
return;
#if defined(a86)
asmv("mov %0, %%cr3" : : "r"(Original));
#endif
}
} SwitchPageTable;
CPU::TrapFrame *Frame = (CPU::TrapFrame *)Data;
// debug("IRQ%ld", Frame->InterruptNumber - 32);
/* Halt core interrupt */
if (unlikely(Frame->InterruptNumber == CPU::x86::IRQ31))
CPU::Stop();
assert(Frame->InterruptNumber <= CPU::x86::IRQ223);
auto it = RegisteredEvents.begin();
while (it != RegisteredEvents.end())
{
int iEvNum = it->IRQ;
#if defined(a86)
iEvNum += CPU::x86::IRQ0;
#endif
if (iEvNum == s_cst(int, Frame->InterruptNumber))
break;
++it;
}
if (it == RegisteredEvents.end())
{
warn("IRQ%d is not registered.", Frame->InterruptNumber - 32);
ReturnFromInterrupt();
return;
}
it->Priority++;
if (it->IsHandler)
{
Handler *hnd = (Handler *)it->Data;
hnd->OnInterruptReceived(Frame);
}
else
{
if (it->Context != nullptr)
it->Callback((CPU::TrapFrame *)it->Context);
else
it->Callback(Frame);
}
ReturnFromInterrupt();
}
extern "C" nsa void SchedulerInterruptHandler(void *Data)
{
KernelPageTable->Update();
CPU::SchedulerFrame *Frame = (CPU::SchedulerFrame *)Data;
#if defined(a86)
assert(Frame->InterruptNumber == CPU::x86::IRQ16);
#else
assert(Frame->InterruptNumber == 16);
#endif
auto it = std::find_if(RegisteredEvents.begin(), RegisteredEvents.end(),
[](const Event &ev)
{
return ev.IRQ == 16;
});
if (it == RegisteredEvents.end())
{
warn("Scheduler interrupt is not registered.");
ReturnFromInterrupt();
Frame->ppt = Frame->opt;
debug("opt = %#lx", Frame->opt);
return;
}
assert(it->IsHandler);
it->Priority++;
Handler *hnd = (Handler *)it->Data;
hnd->OnInterruptReceived(Frame);
ReturnFromInterrupt();
}
Handler::Handler(int InterruptNumber, bool Critical)
{
foreach (auto ev in RegisteredEvents)
{
if (ev.IRQ == InterruptNumber)
{
warn("IRQ%d is already registered.",
InterruptNumber);
}
}
this->InterruptNumber = InterruptNumber;
Event newEvent =
{InterruptNumber, /* IRQ */
this, /* Data */
true, /* IsHandler */
nullptr, /* Callback */
nullptr, /* Context */
0, /* Priority */
Critical}; /* Critical */
RegisteredEvents.push_back(newEvent);
debug("Registered interrupt handler for IRQ%d.",
InterruptNumber);
}
Handler::Handler(PCI::PCIDevice Device, bool Critical)
{
PCI::PCIHeader0 *hdr0 =
(PCI::PCIHeader0 *)Device.Header;
Handler(hdr0->InterruptLine, Critical);
}
Handler::Handler()
{
debug("Empty interrupt handler.");
}
Handler::~Handler()
{
debug("Unregistering interrupt handler for IRQ%d.",
this->InterruptNumber);
forItr(itr, RegisteredEvents)
{
if (itr->IRQ == this->InterruptNumber)
{
RegisteredEvents.erase(itr);
return;
}
}
warn("Event %d not found.", this->InterruptNumber);
}
void Handler::OnInterruptReceived(CPU::TrapFrame *Frame)
{
debug("Unhandled interrupt %d", Frame->InterruptNumber);
}
void Handler::OnInterruptReceived(CPU::SchedulerFrame *Frame)
{
debug("Unhandled scheduler interrupt %d", Frame->InterruptNumber);
}
}

256
Kernel/core/lock.cpp Normal file
View File

@ -0,0 +1,256 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <lock.hpp>
#include <debug.h>
#include <smp.hpp>
#include "../kernel.h"
/* This might end up in a deadlock in the deadlock handler.
Nobody can escape the deadlock, not even the
deadlock handler itself. */
// #define PRINT_BACKTRACE 1
#ifdef PRINT_BACKTRACE
#pragma GCC diagnostic ignored "-Wframe-address"
void PrintStacktrace(LockClass::SpinLockData *Lock)
{
if (!KernelSymbolTable)
{
warn("Symbol table not available.");
return;
}
struct StackFrame
{
uintptr_t BasePointer;
uintptr_t ReturnAddress;
};
// char DbgAttempt[1024] = "\0";
// char DbgHolder[1024] = "\0";
std::string DbgAttempt = "\0";
std::string DbgHolder = "\0";
StackFrame *FrameAttempt = (StackFrame *)Lock->StackPointerAttempt.load();
StackFrame *FrameHolder = (StackFrame *)Lock->StackPointerHolder.load();
while (Memory::Virtual().Check(FrameAttempt))
{
DbgAttempt.concat(KernelSymbolTable->GetSymbol(FrameAttempt->ReturnAddress));
DbgAttempt.concat("<-");
FrameAttempt = (StackFrame *)FrameAttempt->BasePointer;
}
warn("Attempt: %s", DbgAttempt.c_str());
while (Memory::Virtual().Check(FrameHolder))
{
DbgHolder.concat(KernelSymbolTable->GetSymbol(FrameHolder->ReturnAddress));
DbgHolder.concat("<-");
FrameHolder = (StackFrame *)FrameHolder->BasePointer;
}
warn("Holder: %s", DbgHolder.c_str());
// warn("\t\t%s<-%s<-%s<-%s<-%s<-%s<-%s<-%s<-%s<-%s",
// KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(0)),
// KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(1)),
// KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(2)),
// KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(3)),
// KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(4)),
// KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(5)),
// KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(6)),
// KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(7)),
// KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(8)),
// KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(9)));
}
#endif
#ifdef DEBUG
#define DEADLOCK_TIMEOUT 0x1000
#else
#define DEADLOCK_TIMEOUT 0x10000000
#endif
bool ForceUnlock = false;
std::atomic_size_t LocksCount = 0;
size_t GetLocksCount() { return LocksCount.load(); }
void LockClass::Yield()
{
if (CPU::Interrupts(CPU::Check) && TaskManager &&
!TaskManager->IsPanic())
{
TaskManager->Yield();
}
CPU::Pause();
}
void LockClass::DeadLock(SpinLockData &Lock)
{
if (ForceUnlock)
{
warn("Unlocking lock '%s' which it was held by '%s'...",
Lock.AttemptingToGet, Lock.CurrentHolder);
this->DeadLocks = 0;
this->Unlock();
return;
}
CPUData *CoreData = GetCurrentCPU();
long CCore = 0xdead;
if (CoreData != nullptr)
CCore = CoreData->ID;
warn("Potential deadlock in lock '%s' held by '%s'! %ld %s in queue. Interrupts are %s. Core %ld held by %ld. (%ld times happened)",
Lock.AttemptingToGet, Lock.CurrentHolder, Lock.Count, Lock.Count > 1 ? "locks" : "lock",
CPU::Interrupts(CPU::Check) ? "enabled" : "disabled", CCore, Lock.Core, this->DeadLocks);
#ifdef PRINT_BACKTRACE
PrintStacktrace(&Lock);
#endif
// TODO: Print on screen too.
this->DeadLocks++;
if (Config.UnlockDeadLock && this->DeadLocks.load() > 10)
{
warn("Unlocking lock '%s' to prevent deadlock. (this is enabled in the kernel config)", Lock.AttemptingToGet);
this->DeadLocks = 0;
this->Unlock();
}
this->Yield();
}
int LockClass::Lock(const char *FunctionName)
{
LockData.AttemptingToGet = FunctionName;
LockData.StackPointerAttempt = (uintptr_t)__builtin_frame_address(0);
Retry:
int i = 0;
while (IsLocked.exchange(true, std::memory_order_acquire) &&
++i < DEADLOCK_TIMEOUT)
{
this->Yield();
}
if (i >= DEADLOCK_TIMEOUT)
{
DeadLock(LockData);
goto Retry;
}
LockData.Count.fetch_add(1);
LockData.CurrentHolder.store(FunctionName);
LockData.StackPointerHolder.store((uintptr_t)__builtin_frame_address(0));
CPUData *CoreData = GetCurrentCPU();
if (CoreData != nullptr)
LockData.Core.store(CoreData->ID);
LocksCount.fetch_add(1);
__sync;
return 0;
}
int LockClass::Unlock()
{
__sync;
IsLocked.store(false, std::memory_order_release);
LockData.Count.fetch_sub(1);
LocksCount.fetch_sub(1);
return 0;
}
void LockClass::TimeoutDeadLock(SpinLockData &Lock, uint64_t Timeout)
{
CPUData *CoreData = GetCurrentCPU();
long CCore = 0xdead;
if (CoreData != nullptr)
CCore = CoreData->ID;
uint64_t Counter = TimeManager->GetCounter();
warn("Potential deadlock in lock '%s' held by '%s'! %ld %s in queue. Interrupts are %s. Core %ld held by %ld. Timeout in %ld (%ld ticks remaining).",
Lock.AttemptingToGet, Lock.CurrentHolder, Lock.Count, Lock.Count > 1 ? "locks" : "lock",
CPU::Interrupts(CPU::Check) ? "enabled" : "disabled", CCore, Lock.Core, Timeout, Timeout - Counter);
#ifdef PRINT_BACKTRACE
PrintStacktrace(&Lock);
#endif
if (Timeout < Counter)
{
warn("Unlocking lock '%s' because of timeout. (%ld < %ld)",
Lock.AttemptingToGet, Timeout, Counter);
this->Unlock();
}
this->Yield();
}
int LockClass::TimeoutLock(const char *FunctionName, uint64_t Timeout)
{
if (!TimeManager)
return Lock(FunctionName);
LockData.AttemptingToGet.store(FunctionName);
LockData.StackPointerAttempt.store((uintptr_t)__builtin_frame_address(0));
std::atomic_uint64_t Target = 0;
Retry:
int i = 0;
while (IsLocked.exchange(true, std::memory_order_acquire) &&
++i < DEADLOCK_TIMEOUT)
{
this->Yield();
}
if (i >= DEADLOCK_TIMEOUT)
{
if (Target.load() == 0)
Target.store(TimeManager->CalculateTarget(Timeout,
Time::Units::Milliseconds));
TimeoutDeadLock(LockData, Target.load());
goto Retry;
}
LockData.Count.fetch_add(1);
LockData.CurrentHolder.store(FunctionName);
LockData.StackPointerHolder.store((uintptr_t)__builtin_frame_address(0));
CPUData *CoreData = GetCurrentCPU();
if (CoreData != nullptr)
LockData.Core.store(CoreData->ID);
LocksCount.fetch_add(1);
__sync;
return 0;
}

104
Kernel/core/memory/brk.cpp Normal file
View File

@ -0,0 +1,104 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <memory/brk.hpp>
#include <memory/virtual.hpp>
#include <memory/vma.hpp>
#include <assert.h>
#include <errno.h>
#include <debug.h>
namespace Memory
{
void *ProgramBreak::brk(void *Address)
{
if (HeapStart == 0x0 || Break == 0x0)
{
error("HeapStart or Break is 0x0");
return (void *)-EAGAIN;
}
/* Get the current program break. */
if (Address == nullptr)
return (void *)Break;
/* Check if the address is valid. */
if ((uintptr_t)Address < HeapStart)
{
debug("Address %#lx is less than HeapStart %#lx", Address, HeapStart);
return (void *)-ENOMEM;
}
Virtual vmm(this->Table);
if ((uintptr_t)Address > Break)
{
/* Allocate more memory. */
ssize_t Pages = TO_PAGES(uintptr_t(Address) - Break);
void *Allocated = vma->RequestPages(Pages);
if (Allocated == nullptr)
return (void *)-ENOMEM;
/* Map the allocated pages. */
for (ssize_t i = 0; i < Pages; i++)
{
void *VirtAddr = (void *)(Break + (i * PAGE_SIZE));
void *PhysAddr = (void *)(uintptr_t(Allocated) + (i * PAGE_SIZE));
debug("Mapping %#lx to %#lx", VirtAddr, PhysAddr);
vmm.Map(VirtAddr, PhysAddr, RW | US);
}
Break = ROUND_UP(uintptr_t(Address), PAGE_SIZE);
debug("Round up %#lx to %#lx", Address, Break);
return Address;
}
/* Free memory. */
ssize_t Pages = TO_PAGES(Break - uintptr_t(Address));
vma->FreePages((void *)Break, Pages);
/* Unmap the freed pages. */
for (ssize_t i = 0; i < Pages; i++)
{
uint64_t Page = Break - (i * 0x1000);
vmm.Remap((void *)Page, (void *)Page, RW);
debug("Unmapping %#lx", Page);
}
Break = (uint64_t)Address;
return (void *)Break;
}
ProgramBreak::ProgramBreak(PageTable *Table, VirtualMemoryArea *vma)
{
assert(Table != nullptr);
assert(vma != nullptr);
debug("+ %#lx", this);
this->Table = Table;
this->vma = vma;
}
ProgramBreak::~ProgramBreak()
{
debug("- %#lx", this);
/* Do nothing because VirtualMemoryArea
will be destroyed later. */
}
}

View File

@ -0,0 +1,212 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <memory.hpp>
#include <acpi.hpp>
#include <debug.h>
#include <elf.h>
#ifdef DEBUG
#include <uart.hpp>
#endif
#include "../../kernel.h"
namespace Memory
{
__no_sanitize("alignment") void Physical::FindBitmapRegion(uintptr_t &BitmapAddress,
size_t &BitmapAddressSize)
{
size_t BitmapSize = (size_t)(bInfo.Memory.Size / PAGE_SIZE) / 8 + 1;
uintptr_t KernelStart = (uintptr_t)bInfo.Kernel.PhysicalBase;
uintptr_t KernelEnd = (uintptr_t)bInfo.Kernel.PhysicalBase + bInfo.Kernel.Size;
uintptr_t SectionsStart = 0x0;
uintptr_t SectionsEnd = 0x0;
uintptr_t Symbols = 0x0;
uintptr_t StringAddress = 0x0;
size_t SymbolSize = 0;
size_t StringSize = 0;
uintptr_t RSDPStart = 0x0;
uintptr_t RSDPEnd = 0x0;
if (bInfo.Kernel.Symbols.Num &&
bInfo.Kernel.Symbols.EntSize &&
bInfo.Kernel.Symbols.Shndx)
{
char *sections = r_cst(char *, bInfo.Kernel.Symbols.Sections);
SectionsStart = (uintptr_t)sections;
SectionsEnd = (uintptr_t)sections + bInfo.Kernel.Symbols.EntSize *
bInfo.Kernel.Symbols.Num;
for (size_t i = 0; i < bInfo.Kernel.Symbols.Num; ++i)
{
Elf_Shdr *sym = (Elf_Shdr *)&sections[bInfo.Kernel.Symbols.EntSize * i];
Elf_Shdr *str = (Elf_Shdr *)&sections[bInfo.Kernel.Symbols.EntSize *
sym->sh_link];
if (sym->sh_type == SHT_SYMTAB &&
str->sh_type == SHT_STRTAB)
{
Symbols = (uintptr_t)sym->sh_addr;
StringAddress = (uintptr_t)str->sh_addr;
SymbolSize = (size_t)sym->sh_size;
StringSize = (size_t)str->sh_size;
break;
}
}
}
#if defined(a86)
if (bInfo.RSDP)
{
RSDPStart = (uintptr_t)bInfo.RSDP;
RSDPEnd = (uintptr_t)bInfo.RSDP + sizeof(BootInfo::RSDPInfo);
#ifdef DEBUG
ACPI::ACPI::ACPIHeader *ACPIPtr;
bool XSDT = false;
if (bInfo.RSDP->Revision >= 2 && bInfo.RSDP->XSDTAddress)
{
ACPIPtr = (ACPI::ACPI::ACPIHeader *)bInfo.RSDP->XSDTAddress;
XSDT = true;
}
else
ACPIPtr = (ACPI::ACPI::ACPIHeader *)(uintptr_t)bInfo.RSDP->RSDTAddress;
if (Memory::Virtual().Check(ACPIPtr))
{
size_t TableSize = ((ACPIPtr->Length - sizeof(ACPI::ACPI::ACPIHeader)) /
(XSDT ? 8 : 4));
debug("There are %d ACPI tables", TableSize);
}
#endif
}
#elif defined(aa64)
#endif
for (uint64_t i = 0; i < bInfo.Memory.Entries; i++)
{
if (bInfo.Memory.Entry[i].Type == Usable)
{
uintptr_t RegionAddress = (uintptr_t)bInfo.Memory.Entry[i].BaseAddress;
uintptr_t RegionSize = bInfo.Memory.Entry[i].Length;
/* We don't want to use the first 1MB of memory. */
if (RegionAddress <= 0xFFFFF)
continue;
if ((BitmapSize + 0x100) > RegionSize)
{
debug("Region %p-%p (%d MiB) is too small for bitmap.",
(void *)RegionAddress,
(void *)(RegionAddress + RegionSize),
TO_MiB(RegionSize));
continue;
}
BitmapAddress = RegionAddress;
BitmapAddressSize = RegionSize;
struct AddrRange
{
uintptr_t Start;
uintptr_t End;
};
auto SortAddresses = [](AddrRange *Array, size_t n)
{
size_t MinimumIndex;
for (size_t i = 0; i < n - 1; i++)
{
MinimumIndex = i;
for (size_t j = i + 1; j < n; j++)
if (Array[j].Start < Array[MinimumIndex].Start)
MinimumIndex = j;
AddrRange tmp = Array[MinimumIndex];
Array[MinimumIndex] = Array[i];
Array[i] = tmp;
}
};
AddrRange PtrArray[] =
{
{KernelStart,
KernelEnd},
{SectionsStart,
SectionsEnd},
{Symbols,
Symbols + SymbolSize},
{StringAddress,
StringAddress + StringSize},
{RSDPStart,
RSDPEnd},
{(uintptr_t)bInfo.Kernel.FileBase,
(uintptr_t)bInfo.Kernel.FileBase + bInfo.Kernel.Size},
{(uintptr_t)bInfo.Modules[0].Address,
(uintptr_t)bInfo.Modules[0].Address + bInfo.Modules[0].Size},
{(uintptr_t)bInfo.Modules[1].Address,
(uintptr_t)bInfo.Modules[1].Address + bInfo.Modules[1].Size},
{(uintptr_t)bInfo.Modules[2].Address,
(uintptr_t)bInfo.Modules[2].Address + bInfo.Modules[2].Size},
{(uintptr_t)bInfo.Modules[3].Address,
(uintptr_t)bInfo.Modules[3].Address + bInfo.Modules[3].Size},
/* MAX_MODULES == 4 */
};
SortAddresses(PtrArray, sizeof(PtrArray) / sizeof(PtrArray[0]));
for (size_t i = 0; i < sizeof(PtrArray) / sizeof(PtrArray[0]); i++)
{
if (PtrArray[i].Start == 0x0)
continue;
uintptr_t Start = PtrArray[i].Start;
uintptr_t End = PtrArray[i].End;
debug("%#lx - %#lx", Start, End);
if (RegionAddress >= Start &&
End <= (RegionAddress + RegionSize))
{
BitmapAddress = End;
BitmapAddressSize = RegionSize - (End - RegionAddress);
}
}
if ((BitmapSize + 0x100) > BitmapAddressSize)
{
debug("Region %p-%p (%d MiB) is too small for bitmap.",
(void *)BitmapAddress,
(void *)(BitmapAddress + BitmapAddressSize),
TO_MiB(BitmapAddressSize));
continue;
}
debug("Found free memory for bitmap: %p (%d MiB)",
(void *)BitmapAddress,
TO_MiB(BitmapAddressSize));
break;
}
}
}
}

View File

@ -0,0 +1,154 @@
# Xalloc
Xalloc is a custom memory allocator designed for hobby operating systems.
Written in C++ and provides a simple and efficient way to manage memory in your hobby OS.
#### ❗ This project is still in development and is not ready for use in production environments. ❗
---
## Features
- **Simple API** - Simple API for allocating and freeing memory.
- **Efficient** - Uses a free-list to manage memory and is designed to be fast.
- **No dependencies** - No dependencies and is designed to be easy to integrate into your OS.
---
## Getting Started
### Implementing missing functions
You will need to implement the following functions in your OS:
##### Wrapper.cpp
```cpp
extern "C" void *Xalloc_REQUEST_PAGES(Xsize_t Pages)
{
// ...
}
extern "C" void Xalloc_FREE_PAGES(void *Address, Xsize_t Pages)
{
// ...
}
/* Mandatory only if Xalloc_MapPages is set to true */
extern "C" void Xalloc_MAP_MEMORY(void *VirtualAddress, void *PhysicalAddress, Xsize_t Flags)
{
// ...
}
/* Mandatory only if Xalloc_MapPages is set to true */
extern "C" void Xalloc_UNMAP_MEMORY(void *VirtualAddress)
{
// ...
}
```
##### Xalloc.hpp
```cpp
#define Xalloc_StopOnFail <bool> /* Infinite loop on failure */
#define Xalloc_MapPages <bool> /* Map pages on allocation */
#define Xalloc_PAGE_SIZE <page size> /* <-- Replace with your page size */
#define Xalloc_trace(m, ...) <trace function>
#define Xalloc_warn(m, ...) <warning function>
#define Xalloc_err(m, ...) <error function>
#define XallocV1_def <define a lock> /* eg. std::mutex Xalloc_lock; */
#define XallocV1_lock <lock function>
#define XallocV1_unlock <unlock function>
/* Same as above */
#define XallocV2_def <define a lock>
#define XallocV2_lock <lock function>
#define XallocV2_unlock <unlock function>
```
### Typical usage
```cpp
#include "Xalloc.hpp"
Xalloc::V1 *XallocV1Allocator = nullptr;
int main()
{
/* Virtual Base User SMAP */
XallocV1Allocator = new Xalloc::V1((void *)0xFFFFA00000000000, false, false);
void *p = XallocV1Allocator->malloc(1234);
/* ... */
XallocV1Allocator->free(p);
delete XallocV1Allocator;
return 0;
}
```
or
```cpp
#include "Xalloc.hpp"
int main()
{
/* Virtual Base User SMAP */
Xalloc::V1 XallocV1Allocator((void *)0xFFFFA00000000000, false, false);
void *p = XallocV1Allocator.malloc(1234);
/* ... */
XallocV1Allocator.free(p);
return 0;
}
```
---
## API
### Xalloc::V1
```cpp
void *malloc(Xsize_t Size);
```
Allocates a block of memory of size `Size` bytes.
If `Size` is 0, then `nullptr` is returned.
- `Size` - The size of the block to allocate in bytes.
<br><br>
```cpp
void free(void *Address);
```
Frees the memory block pointed to by `Address`.
If `Address` is `nullptr`, then no operation is performed.
- `Address` - The address of the memory block to free.
<br><br>
```cpp
void *calloc(Xsize_t NumberOfBlocks, Xsize_t Size);
```
Allocates a block of memory for an array of `NumberOfBlocks` elements, each of them `Size` bytes long.
If `NumberOfBlocks` or `Size` is 0, then `nullptr` is returned.
- `NumberOfBlocks` - The number of elements to allocate.
- `Size` - The size of each element in bytes.
<br><br>
```cpp
void *realloc(void *Address, Xsize_t Size);
```
Changes the size of the memory block pointed to by `Address` to `Size` bytes.
If `Address` is `nullptr`, then the call is equivalent to `malloc(Size)`.
If `Size` is equal to zero, and `Address` is not `nullptr`, then the call is equivalent to `free(Address)`.
- `Address` - The address of the memory block to resize.
- `Size` - The new size of the memory block in bytes.
---
## To-do
- [ ] Multiple free-lists for different block sizes

View File

@ -0,0 +1,40 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include "Xalloc.hpp"
#include <memory.hpp>
extern "C" void *Xalloc_REQUEST_PAGES(Xsize_t Pages)
{
return KernelAllocator.RequestPages(Pages);
}
extern "C" void Xalloc_FREE_PAGES(void *Address, Xsize_t Pages)
{
KernelAllocator.FreePages(Address, Pages);
}
extern "C" void Xalloc_MAP_MEMORY(void *VirtualAddress, void *PhysicalAddress, Xsize_t Flags)
{
Memory::Virtual(KernelPageTable).Map(VirtualAddress, PhysicalAddress, Flags);
}
extern "C" void Xalloc_UNMAP_MEMORY(void *VirtualAddress)
{
Memory::Virtual(KernelPageTable).Unmap(VirtualAddress);
}

View File

@ -0,0 +1,236 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __FENNIX_KERNEL_Xalloc_H__
#define __FENNIX_KERNEL_Xalloc_H__
#include <memory.hpp>
#include <lock.hpp>
#include <debug.h>
typedef __UINT8_TYPE__ Xuint8_t;
typedef __SIZE_TYPE__ Xsize_t;
typedef __UINTPTR_TYPE__ Xuintptr_t;
#define Xalloc_StopOnFail true
#define Xalloc_MapPages false
#define Xalloc_PAGE_SIZE PAGE_SIZE
#define Xalloc_trace(m, ...) trace(m, ##__VA_ARGS__)
#define Xalloc_warn(m, ...) warn(m, ##__VA_ARGS__)
#define Xalloc_err(m, ...) error(m, ##__VA_ARGS__)
#define XallocV1_def NewLock(XallocV1Lock)
#define XallocV1_lock XallocV1Lock.Lock(__FUNCTION__)
#define XallocV1_unlock XallocV1Lock.Unlock()
#define XallocV2_def NewLock(XallocV2Lock)
#define XallocV2_lock XallocV2Lock.Lock(__FUNCTION__)
#define XallocV2_unlock XallocV2Lock.Unlock()
namespace Xalloc
{
class V1
{
private:
void *BaseVirtualAddress = nullptr;
void *FirstBlock = nullptr;
void *LastBlock = nullptr;
bool UserMapping = false;
bool SMAPUsed = false;
public:
/** @brief Execute "stac" instruction if the kernel has SMAP enabled */
void Xstac();
/** @brief Execute "clac" instruction if the kernel has SMAP enabled */
void Xclac();
/**
* @brief Arrange the blocks to optimize the memory usage
* The allocator is not arranged by default
* to avoid performance issues.
* This function will defragment the memory
* and free the unused blocks.
*
* You should call this function when the
* kernel is idle or when is not using
* the allocator.
*/
void Arrange();
/**
* @brief Allocate a new memory block
*
* @param Size Size of the block to allocate.
* @return void* Pointer to the allocated block.
*/
void *malloc(Xsize_t Size);
/**
* @brief Free a previously allocated block
*
* @param Address Address of the block to free.
*/
void free(void *Address);
/**
* @brief Allocate a new memory block
*
* @param NumberOfBlocks Number of blocks to allocate.
* @param Size Size of the block to allocate.
* @return void* Pointer to the allocated block.
*/
void *calloc(Xsize_t NumberOfBlocks, Xsize_t Size);
/**
* @brief Reallocate a previously allocated block
*
* @param Address Address of the block to reallocate.
* @param Size New size of the block.
* @return void* Pointer to the reallocated block.
*/
void *realloc(void *Address, Xsize_t Size);
/**
* @brief Construct a new Allocator object
*
* @param BaseVirtualAddress Virtual address to map the pages.
* @param UserMode Map the new pages with USER flag?
* @param SMAPEnabled Does the kernel has Supervisor Mode Access Prevention enabled?
*/
V1(void *BaseVirtualAddress, bool UserMode, bool SMAPEnabled);
/**
* @brief Destroy the Allocator object
*
*/
~V1();
};
class V2
{
private:
class Block
{
public:
int Sanity = 0xA110C;
Block *Next = nullptr;
bool IsFree = true;
V2 *ctx = nullptr;
Xuint8_t *Data = nullptr;
Xsize_t DataSize = 0;
void Check();
Block(Xsize_t Size, V2 *ctx);
~Block();
void *operator new(Xsize_t);
void operator delete(void *Address);
} __attribute__((packed, aligned((16))));
/* The base address of the virtual memory */
Xuintptr_t BaseVirtualAddress = 0x0;
/* The size of the heap */
Xsize_t HeapSize = 0x0;
/* The used size of the heap */
Xsize_t HeapUsed = 0x0;
Block *FirstBlock = nullptr;
Xuint8_t *AllocateHeap(Xsize_t Size);
void FreeHeap(Xuint8_t *At, Xsize_t Size);
Xsize_t Align(Xsize_t Size);
void *FindFreeBlock(Xsize_t Size,
Block *&CurrentBlock);
public:
/**
* Arrange the blocks to optimize the memory
* usage.
* The allocator is not arranged by default
* to avoid performance issues.
* This function will defragment the memory
* and free the unused blocks.
*
* You should call this function when the
* kernel is idle or when is not using the
* allocator.
*/
void Arrange();
/**
* Allocate a new memory block
*
* @param Size Size of the block to allocate.
* @return void* Pointer to the allocated
* block.
*/
void *malloc(Xsize_t Size);
/**
* Free a previously allocated block
*
* @param Address Address of the block to
* free.
*/
void free(void *Address);
/**
* Allocate a new memory block
*
* @param NumberOfBlocks Number of blocks
* to allocate.
* @param Size Size of the block to allocate.
* @return void* Pointer to the allocated
* block.
*/
void *calloc(Xsize_t NumberOfBlocks,
Xsize_t Size);
/**
* Reallocate a previously allocated block
*
* @param Address Address of the block
* to reallocate.
* @param Size New size of the block.
* @return void* Pointer to the reallocated
* block.
*/
void *realloc(void *Address, Xsize_t Size);
/**
* Construct a new Allocator object
*
* @param VirtualBase Virtual address
* to map the pages.
*/
V2(void *VirtualBase);
/**
* Destroy the Allocator object
*/
~V2();
friend class Block;
};
}
#endif // !__FENNIX_KERNEL_Xalloc_H__

View File

@ -0,0 +1,290 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include "Xalloc.hpp"
XallocV1_def;
#define XALLOC_CONCAT(x, y) x##y
#define XStoP(d) (((d) + PAGE_SIZE - 1) / PAGE_SIZE)
#define XPtoS(d) ((d)*PAGE_SIZE)
#define Xalloc_BlockSanityKey 0xA110C
extern "C" void *Xalloc_REQUEST_PAGES(Xsize_t Pages);
extern "C" void Xalloc_FREE_PAGES(void *Address, Xsize_t Pages);
extern "C" void Xalloc_MAP_MEMORY(void *VirtualAddress, void *PhysicalAddress, Xsize_t Flags);
extern "C" void Xalloc_UNMAP_MEMORY(void *VirtualAddress);
// TODO: Change memcpy with an optimized version
void *Xmemcpy(void *__restrict__ Destination, const void *__restrict__ Source, Xsize_t Length)
{
unsigned char *dst = (unsigned char *)Destination;
const unsigned char *src = (const unsigned char *)Source;
for (Xsize_t i = 0; i < Length; i++)
dst[i] = src[i];
return Destination;
}
// TODO: Change memset with an optimized version
void *Xmemset(void *__restrict__ Destination, int Data, Xsize_t Length)
{
unsigned char *Buffer = (unsigned char *)Destination;
for (Xsize_t i = 0; i < Length; i++)
Buffer[i] = (unsigned char)Data;
return Destination;
}
namespace Xalloc
{
class Block
{
public:
void *Address = nullptr;
int Sanity = Xalloc_BlockSanityKey;
Xsize_t Size = 0;
Block *Next = nullptr;
Block *Last = nullptr;
bool IsFree = true;
bool Check()
{
if (this->Sanity != Xalloc_BlockSanityKey)
return false;
return true;
}
Block(Xsize_t Size)
{
this->Address = Xalloc_REQUEST_PAGES(XStoP(Size + 1));
this->Size = Size;
Xmemset(this->Address, 0, Size);
}
~Block()
{
Xalloc_FREE_PAGES(this->Address, XStoP(this->Size + 1));
}
/**
* @brief Overload new operator to allocate memory from the heap
* @param Size Unused
* @return void* Pointer to the allocated memory
*/
void *operator new(Xsize_t Size)
{
void *ptr = Xalloc_REQUEST_PAGES(XStoP(sizeof(Block)));
return ptr;
(void)(Size);
}
/**
* @brief Overload delete operator to free memory from the heap
* @param Address Pointer to the memory to free
*/
void operator delete(void *Address)
{
Xalloc_FREE_PAGES(Address, XStoP(sizeof(Block)));
}
} __attribute__((packed, aligned((16))));
class SmartSMAPClass
{
private:
V1 *allocator = nullptr;
public:
SmartSMAPClass(V1 *allocator)
{
this->allocator = allocator;
this->allocator->Xstac();
}
~SmartSMAPClass() { this->allocator->Xclac(); }
};
#define SmartSMAP SmartSMAPClass XALLOC_CONCAT(SmartSMAP##_, __COUNTER__)(this)
void V1::Xstac()
{
if (this->SMAPUsed)
{
#if defined(a86)
asm volatile("stac" ::
: "cc");
#endif
}
}
void V1::Xclac()
{
if (this->SMAPUsed)
{
#if defined(a86)
asm volatile("clac" ::
: "cc");
#endif
}
}
void V1::Arrange()
{
Xalloc_err("Arrange() is not implemented yet!");
}
void *V1::malloc(Xsize_t Size)
{
if (Size == 0)
{
Xalloc_warn("Attempted to allocate 0 bytes!");
return nullptr;
}
SmartSMAP;
XallocV1_lock;
if (this->FirstBlock == nullptr)
{
this->FirstBlock = new Block(Size);
((Block *)this->FirstBlock)->IsFree = false;
XallocV1_unlock;
return ((Block *)this->FirstBlock)->Address;
}
Block *CurrentBlock = ((Block *)this->FirstBlock);
while (CurrentBlock != nullptr)
{
if (!CurrentBlock->Check())
{
Xalloc_err("Block %#lx has an invalid sanity key! (%#x != %#x)",
(Xsize_t)CurrentBlock, CurrentBlock->Sanity, Xalloc_BlockSanityKey);
while (Xalloc_StopOnFail)
;
}
else if (CurrentBlock->IsFree && CurrentBlock->Size >= Size)
{
CurrentBlock->IsFree = false;
Xmemset(CurrentBlock->Address, 0, Size);
XallocV1_unlock;
return CurrentBlock->Address;
}
CurrentBlock = CurrentBlock->Next;
}
CurrentBlock = ((Block *)this->FirstBlock);
while (CurrentBlock->Next != nullptr)
CurrentBlock = CurrentBlock->Next;
CurrentBlock->Next = new Block(Size);
((Block *)CurrentBlock->Next)->Last = CurrentBlock;
((Block *)CurrentBlock->Next)->IsFree = false;
XallocV1_unlock;
return ((Block *)CurrentBlock->Next)->Address;
}
void V1::free(void *Address)
{
if (Address == nullptr)
{
Xalloc_warn("Attempted to free a null pointer!");
return;
}
SmartSMAP;
XallocV1_lock;
Block *CurrentBlock = ((Block *)this->FirstBlock);
while (CurrentBlock != nullptr)
{
if (!CurrentBlock->Check())
{
Xalloc_err("Block %#lx has an invalid sanity key! (%#x != %#x)",
(Xsize_t)CurrentBlock, CurrentBlock->Sanity, Xalloc_BlockSanityKey);
while (Xalloc_StopOnFail)
;
}
else if (CurrentBlock->Address == Address)
{
if (CurrentBlock->IsFree)
{
Xalloc_warn("Attempted to free an already freed pointer!");
XallocV1_unlock;
return;
}
CurrentBlock->IsFree = true;
XallocV1_unlock;
return;
}
CurrentBlock = CurrentBlock->Next;
}
Xalloc_err("Invalid address %#lx.", Address);
XallocV1_unlock;
}
void *V1::calloc(Xsize_t NumberOfBlocks, Xsize_t Size)
{
if (NumberOfBlocks == 0 || Size == 0)
{
Xalloc_warn("The %s%s%s is 0!",
NumberOfBlocks == 0 ? "NumberOfBlocks" : "",
NumberOfBlocks == 0 && Size == 0 ? " and " : "",
Size == 0 ? "Size" : "");
return nullptr;
}
return this->malloc(NumberOfBlocks * Size);
}
void *V1::realloc(void *Address, Xsize_t Size)
{
if (Address == nullptr)
return this->malloc(Size);
if (Size == 0)
{
this->free(Address);
return nullptr;
}
// SmartSMAP;
// XallocV1_lock;
// ...
// XallocV1_unlock;
// TODO: Implement realloc
this->free(Address);
return this->malloc(Size);
}
V1::V1(void *BaseVirtualAddress, bool UserMode, bool SMAPEnabled)
{
SmartSMAP;
XallocV1_lock;
this->SMAPUsed = SMAPEnabled;
this->UserMapping = UserMode;
this->BaseVirtualAddress = BaseVirtualAddress;
XallocV1_unlock;
}
V1::~V1()
{
SmartSMAP;
XallocV1_lock;
Xalloc_trace("Destructor not implemented yet.");
XallocV1_unlock;
}
}

View File

@ -0,0 +1,281 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include "Xalloc.hpp"
XallocV2_def;
#define XALLOC_CONCAT(x, y) x##y
#define XStoP(d) (((d) + PAGE_SIZE - 1) / PAGE_SIZE)
#define XPtoS(d) ((d)*PAGE_SIZE)
extern "C" void *Xalloc_REQUEST_PAGES(Xsize_t Pages);
extern "C" void Xalloc_FREE_PAGES(void *Address, Xsize_t Pages);
extern "C" void Xalloc_MAP_MEMORY(void *VirtualAddress,
void *PhysicalAddress,
Xsize_t Flags);
extern "C" void Xalloc_UNMAP_MEMORY(void *VirtualAddress);
#define Xalloc_BlockSanityKey 0xA110C
/*
[ IN DEVELOPMENT ]
*/
namespace Xalloc
{
void V2::Block::Check()
{
if (unlikely(this->Sanity != Xalloc_BlockSanityKey))
{
Xalloc_err("Block %#lx has an invalid sanity key! (%#x != %#x)",
this, this->Sanity, Xalloc_BlockSanityKey);
while (Xalloc_StopOnFail)
;
}
}
V2::Block::Block(Xsize_t Size, V2 *ctx)
{
this->ctx = ctx;
this->Data = ctx->AllocateHeap(Size);
this->DataSize = Size;
}
V2::Block::~Block()
{
}
void *V2::Block::operator new(Xsize_t)
{
constexpr Xsize_t bPgs = XStoP(sizeof(Block));
void *ptr = Xalloc_REQUEST_PAGES(bPgs);
/* TODO: Do something with the rest of
the allocated memory */
return ptr;
}
void V2::Block::operator delete(void *Address)
{
constexpr Xsize_t bPgs = XStoP(sizeof(Block));
Xalloc_FREE_PAGES(Address, bPgs);
}
/* ========================================= */
Xuint8_t *V2::AllocateHeap(Xsize_t Size)
{
Size = this->Align(Size);
Xsize_t Pages = XStoP(Size);
Xuint8_t *FinalAddress = 0x0;
if (this->HeapUsed + Size >= this->HeapSize)
{
void *Address = Xalloc_REQUEST_PAGES(Pages);
void *VirtualAddress = (void *)(this->BaseVirtualAddress + this->HeapSize);
if (Xalloc_MapPages)
{
for (Xsize_t i = 0; i < Pages; i++)
{
Xuintptr_t Page = i * Xalloc_PAGE_SIZE;
void *vAddress = (void *)((Xuintptr_t)VirtualAddress + Page);
Xalloc_MAP_MEMORY(vAddress, (void *)((Xuintptr_t)Address + Page), 0x3);
}
}
this->HeapSize += XPtoS(Pages);
FinalAddress = (Xuint8_t *)VirtualAddress;
}
else
FinalAddress = (Xuint8_t *)(this->BaseVirtualAddress + this->HeapUsed);
this->HeapUsed += Size;
return (uint8_t *)FinalAddress;
}
void V2::FreeHeap(Xuint8_t *At, Xsize_t Size)
{
Xsize_t Pages = XStoP(Size);
if (Xalloc_MapPages)
{
for (Xsize_t i = 0; i < Pages; i++)
{
Xuintptr_t Page = i * Xalloc_PAGE_SIZE;
void *VirtualAddress = (void *)((Xuintptr_t)At + Page);
Xalloc_UNMAP_MEMORY(VirtualAddress);
}
}
Xalloc_FREE_PAGES(At, Pages);
this->HeapUsed -= Size;
}
Xsize_t V2::Align(Xsize_t Size)
{
return (Size + 0xF) & ~0xF;
}
void *V2::FindFreeBlock(Xsize_t Size, Block *&CurrentBlock)
{
if (this->FirstBlock == nullptr)
{
this->FirstBlock = new Block(Size, this);
this->FirstBlock->IsFree = false;
return this->FirstBlock->Data;
}
while (true)
{
CurrentBlock->Check();
/* FIXME: This will waste a lot of space
need better algorithm */
if (CurrentBlock->IsFree &&
CurrentBlock->DataSize >= Size)
{
CurrentBlock->IsFree = false;
return CurrentBlock->Data;
}
if (CurrentBlock->Next == nullptr)
break;
CurrentBlock = CurrentBlock->Next;
}
return nullptr;
}
void V2::Arrange()
{
Xalloc_err("Arrange() is not implemented yet!");
}
void *V2::malloc(Xsize_t Size)
{
if (Size == 0)
{
Xalloc_warn("Attempted to allocate 0 bytes!");
return nullptr;
}
XallocV2_lock;
Block *CurrentBlock = this->FirstBlock;
void *ret = this->FindFreeBlock(Size, CurrentBlock);
if (ret)
{
XallocV2_unlock;
return ret;
}
CurrentBlock->Next = new Block(Size, this);
CurrentBlock->Next->IsFree = false;
XallocV2_unlock;
return CurrentBlock->Next->Data;
}
void V2::free(void *Address)
{
if (Address == nullptr)
{
Xalloc_warn("Attempted to free a null pointer!");
return;
}
XallocV2_lock;
Block *CurrentBlock = ((Block *)this->FirstBlock);
while (CurrentBlock != nullptr)
{
CurrentBlock->Check();
if (CurrentBlock->Data == Address)
{
if (CurrentBlock->IsFree)
Xalloc_warn("Attempted to free an already freed block! %#lx", Address);
CurrentBlock->IsFree = true;
XallocV2_unlock;
return;
}
CurrentBlock = CurrentBlock->Next;
}
Xalloc_err("Invalid address %#lx.", Address);
XallocV2_unlock;
}
void *V2::calloc(Xsize_t NumberOfBlocks, Xsize_t Size)
{
if (NumberOfBlocks == 0 || Size == 0)
{
Xalloc_warn("The %s%s%s is 0!",
NumberOfBlocks == 0 ? "NumberOfBlocks" : "",
NumberOfBlocks == 0 && Size == 0 ? " and " : "",
Size == 0 ? "Size" : "");
return nullptr;
}
return this->malloc(NumberOfBlocks * Size);
}
void *V2::realloc(void *Address, Xsize_t Size)
{
if (Address == nullptr && Size != 0)
return this->malloc(Size);
if (Size == 0)
{
this->free(Address);
return nullptr;
}
// XallocV2_lock;
// ...
// XallocV2_unlock;
// TODO: Implement realloc
static int once = 0;
if (!once++)
Xalloc_trace("realloc is stub!");
this->free(Address);
return this->malloc(Size);
}
V2::V2(void *VirtualBase)
{
if (VirtualBase == 0x0 && Xalloc_MapPages)
{
Xalloc_err("VirtualBase is 0x0 and Xalloc_MapPages is true!");
while (true)
;
}
XallocV2_lock;
this->BaseVirtualAddress = Xuintptr_t(VirtualBase);
XallocV2_unlock;
}
V2::~V2()
{
XallocV2_lock;
Xalloc_trace("Destructor not implemented yet.");
XallocV2_unlock;
}
}

View File

@ -0,0 +1,796 @@
#include "liballoc_1_1.h"
#include <convert.h>
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic ignored "-Wsign-conversion"
/** Durand's Amazing Super Duper Memory functions. */
#define VERSION "1.1"
#define ALIGNMENT 16ul // 4ul ///< This is the byte alignment that memory must be allocated on. IMPORTANT for GTK and other stuff.
#define ALIGN_TYPE char /// unsigned char[16] /// unsigned short
#define ALIGN_INFO sizeof(ALIGN_TYPE) * 16 ///< Alignment information is stored right before the pointer. This is the number of bytes of information stored there.
#define USE_CASE1
#define USE_CASE2
#define USE_CASE3
#define USE_CASE4
#define USE_CASE5
/** This macro will conveniently align our pointer upwards */
#define ALIGN(ptr) \
if (ALIGNMENT > 1) \
{ \
uintptr_t diff; \
ptr = (void *)((uintptr_t)ptr + ALIGN_INFO); \
diff = (uintptr_t)ptr & (ALIGNMENT - 1); \
if (diff != 0) \
{ \
diff = ALIGNMENT - diff; \
ptr = (void *)((uintptr_t)ptr + diff); \
} \
*((ALIGN_TYPE *)((uintptr_t)ptr - ALIGN_INFO)) = \
diff + ALIGN_INFO; \
}
#define UNALIGN(ptr) \
if (ALIGNMENT > 1) \
{ \
uintptr_t diff = *((ALIGN_TYPE *)((uintptr_t)ptr - ALIGN_INFO)); \
if (diff < (ALIGNMENT + ALIGN_INFO)) \
{ \
ptr = (void *)((uintptr_t)ptr - diff); \
} \
}
#define LIBALLOC_MAGIC 0xc001c0de
#define LIBALLOC_DEAD 0xdeaddead
// #define LIBALLOCDEBUG 1
#define LIBALLOCINFO 1
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
// #include <stdio.h>
// #include <stdlib.h>
#include <debug.h>
// #define FLUSH() fflush(stdout)
#define FLUSH()
#define atexit(x)
#define printf(m, ...) trace(m, ##__VA_ARGS__)
#endif
/** A structure found at the top of all system allocated
* memory blocks. It details the usage of the memory block.
*/
struct liballoc_major
{
struct liballoc_major *prev; ///< Linked list information.
struct liballoc_major *next; ///< Linked list information.
unsigned int pages; ///< The number of pages in the block.
unsigned int size; ///< The number of pages in the block.
unsigned int usage; ///< The number of bytes used in the block.
struct liballoc_minor *first; ///< A pointer to the first allocated memory in the block.
};
/** This is a structure found at the beginning of all
* sections in a major block which were allocated by a
* malloc, calloc, realloc call.
*/
struct liballoc_minor
{
struct liballoc_minor *prev; ///< Linked list information.
struct liballoc_minor *next; ///< Linked list information.
struct liballoc_major *block; ///< The owning block. A pointer to the major structure.
unsigned int magic; ///< A magic number to idenfity correctness.
unsigned int size; ///< The size of the memory allocated. Could be 1 byte or more.
unsigned int req_size; ///< The size of memory requested.
};
static struct liballoc_major *l_memRoot = NULL; ///< The root memory block acquired from the system.
static struct liballoc_major *l_bestBet = NULL; ///< The major with the most free memory.
static unsigned int l_pageSize = 4096; ///< The size of an individual page. Set up in liballoc_init.
static unsigned int l_pageCount = 16; ///< The number of pages to request per chunk. Set up in liballoc_init.
static unsigned long long l_allocated = 0; ///< Running total of allocated memory.
static unsigned long long l_inuse = 0; ///< Running total of used memory.
static long long l_warningCount = 0; ///< Number of warnings encountered
static long long l_errorCount = 0; ///< Number of actual errors
static long long l_possibleOverruns = 0; ///< Number of possible overruns
// *********** HELPER FUNCTIONS *******************************
__no_sanitize("undefined") static void *liballoc_memset(void *s, int c, size_t n)
{
return memset(s, c, n);
unsigned int i;
for (i = 0; i < n; i++)
((char *)s)[i] = c;
return s;
}
__no_sanitize("undefined") static void *liballoc_memcpy(void *s1, const void *s2, size_t n)
{
return memcpy(s1, s2, n);
char *cdest;
char *csrc;
unsigned int *ldest = (unsigned int *)s1;
unsigned int *lsrc = (unsigned int *)s2;
while (n >= sizeof(unsigned int))
{
*ldest++ = *lsrc++;
n -= sizeof(unsigned int);
}
cdest = (char *)ldest;
csrc = (char *)lsrc;
while (n > 0)
{
*cdest++ = *csrc++;
n -= 1;
}
return s1;
}
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
__no_sanitize("undefined") static void liballoc_dump()
{
#ifdef LIBALLOCDEBUG
struct liballoc_major *maj = l_memRoot;
struct liballoc_minor *min = NULL;
#endif
printf("liballoc: ------ Memory data ---------------\n");
printf("liballoc: System memory allocated: %i bytes\n", l_allocated);
printf("liballoc: Memory in used (malloc'ed): %i bytes\n", l_inuse);
printf("liballoc: Warning count: %i\n", l_warningCount);
printf("liballoc: Error count: %i\n", l_errorCount);
printf("liballoc: Possible overruns: %i\n", l_possibleOverruns);
#ifdef LIBALLOCDEBUG
while (maj != NULL)
{
printf("liballoc: %lx: total = %i, used = %i\n",
maj,
maj->size,
maj->usage);
min = maj->first;
while (min != NULL)
{
printf("liballoc: %lx: %i bytes\n",
min,
min->size);
min = min->next;
}
maj = maj->next;
}
#endif
FLUSH();
}
#endif
// ***************************************************************
__no_sanitize("undefined") static struct liballoc_major *allocate_new_page(unsigned int size)
{
unsigned int st;
struct liballoc_major *maj;
// This is how much space is required.
st = size + sizeof(struct liballoc_major);
st += sizeof(struct liballoc_minor);
// Perfect amount of space?
if ((st % l_pageSize) == 0)
st = st / (l_pageSize);
else
st = st / (l_pageSize) + 1;
// No, add the buffer.
// Make sure it's >= the minimum size.
if (st < l_pageCount)
st = l_pageCount;
maj = (struct liballoc_major *)liballoc_alloc(st);
if (maj == NULL)
{
l_warningCount += 1;
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
printf("liballoc: WARNING: liballoc_alloc( %i ) return NULL\n", st);
FLUSH();
#endif
return NULL; // uh oh, we ran out of memory.
}
maj->prev = NULL;
maj->next = NULL;
maj->pages = st;
maj->size = st * l_pageSize;
maj->usage = sizeof(struct liballoc_major);
maj->first = NULL;
l_allocated += maj->size;
#ifdef LIBALLOCDEBUG
printf("liballoc: Resource allocated %lx of %i pages (%i bytes) for %i size.\n", maj, st, maj->size, size);
printf("liballoc: Total memory usage = %i KB\n", (int)((l_allocated / (1024))));
FLUSH();
#endif
return maj;
}
__no_sanitize("undefined") void *PREFIX(malloc)(size_t req_size)
{
int startedBet = 0;
unsigned long long bestSize = 0;
void *p = NULL;
uintptr_t diff;
struct liballoc_major *maj;
struct liballoc_minor *min;
struct liballoc_minor *new_min;
unsigned long size = req_size;
// For alignment, we adjust size so there's enough space to align.
if (ALIGNMENT > 1)
{
size += ALIGNMENT + ALIGN_INFO;
}
// So, ideally, we really want an alignment of 0 or 1 in order
// to save space.
liballoc_lock();
if (size == 0)
{
l_warningCount += 1;
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
printf("liballoc: WARNING: alloc( 0 ) called from %lx\n",
__builtin_return_address(0));
FLUSH();
#endif
liballoc_unlock();
return PREFIX(malloc)(1);
}
if (l_memRoot == NULL)
{
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
#ifdef LIBALLOCDEBUG
printf("liballoc: initialization of liballoc " VERSION "\n");
#endif
atexit(liballoc_dump);
FLUSH();
#endif
// This is the first time we are being used.
l_memRoot = allocate_new_page(size);
if (l_memRoot == NULL)
{
liballoc_unlock();
#ifdef LIBALLOCDEBUG
printf("liballoc: initial l_memRoot initialization failed\n", p);
FLUSH();
#endif
return NULL;
}
#ifdef LIBALLOCDEBUG
printf("liballoc: set up first memory major %lx\n", l_memRoot);
FLUSH();
#endif
}
#ifdef LIBALLOCDEBUG
printf("liballoc: %lx PREFIX(malloc)( %i ): ",
__builtin_return_address(0),
size);
FLUSH();
#endif
// Now we need to bounce through every major and find enough space....
maj = l_memRoot;
startedBet = 0;
// Start at the best bet....
if (l_bestBet != NULL)
{
bestSize = l_bestBet->size - l_bestBet->usage;
if (bestSize > (size + sizeof(struct liballoc_minor)))
{
maj = l_bestBet;
startedBet = 1;
}
}
while (maj != NULL)
{
diff = maj->size - maj->usage;
// free memory in the block
if (bestSize < diff)
{
// Hmm.. this one has more memory then our bestBet. Remember!
l_bestBet = maj;
bestSize = diff;
}
#ifdef USE_CASE1
// CASE 1: There is not enough space in this major block.
if (diff < (size + sizeof(struct liballoc_minor)))
{
#ifdef LIBALLOCDEBUG
printf("CASE 1: Insufficient space in block %lx\n", maj);
FLUSH();
#endif
// Another major block next to this one?
if (maj->next != NULL)
{
maj = maj->next; // Hop to that one.
continue;
}
if (startedBet == 1) // If we started at the best bet,
{ // let's start all over again.
maj = l_memRoot;
startedBet = 0;
continue;
}
// Create a new major block next to this one and...
maj->next = allocate_new_page(size); // next one will be okay.
if (maj->next == NULL)
break; // no more memory.
maj->next->prev = maj;
maj = maj->next;
// .. fall through to CASE 2 ..
}
#endif
#ifdef USE_CASE2
// CASE 2: It's a brand new block.
if (maj->first == NULL)
{
maj->first = (struct liballoc_minor *)((uintptr_t)maj + sizeof(struct liballoc_major));
maj->first->magic = LIBALLOC_MAGIC;
maj->first->prev = NULL;
maj->first->next = NULL;
maj->first->block = maj;
maj->first->size = size;
maj->first->req_size = req_size;
maj->usage += size + sizeof(struct liballoc_minor);
l_inuse += size;
p = (void *)((uintptr_t)(maj->first) + sizeof(struct liballoc_minor));
ALIGN(p);
#ifdef LIBALLOCDEBUG
printf("CASE 2: returning %lx\n", p);
FLUSH();
#endif
liballoc_unlock(); // release the lock
return p;
}
#endif
#ifdef USE_CASE3
// CASE 3: Block in use and enough space at the start of the block.
diff = (uintptr_t)(maj->first);
diff -= (uintptr_t)maj;
diff -= sizeof(struct liballoc_major);
if (diff >= (size + sizeof(struct liballoc_minor)))
{
// Yes, space in front. Squeeze in.
maj->first->prev = (struct liballoc_minor *)((uintptr_t)maj + sizeof(struct liballoc_major));
maj->first->prev->next = maj->first;
maj->first = maj->first->prev;
maj->first->magic = LIBALLOC_MAGIC;
maj->first->prev = NULL;
maj->first->block = maj;
maj->first->size = size;
maj->first->req_size = req_size;
maj->usage += size + sizeof(struct liballoc_minor);
l_inuse += size;
p = (void *)((uintptr_t)(maj->first) + sizeof(struct liballoc_minor));
ALIGN(p);
#ifdef LIBALLOCDEBUG
printf("CASE 3: returning %lx\n", p);
FLUSH();
#endif
liballoc_unlock(); // release the lock
return p;
}
#endif
#ifdef USE_CASE4
// CASE 4: There is enough space in this block. But is it contiguous?
min = maj->first;
// Looping within the block now...
while (min != NULL)
{
// CASE 4.1: End of minors in a block. Space from last and end?
if (min->next == NULL)
{
// the rest of this block is free... is it big enough?
diff = (uintptr_t)(maj) + maj->size;
diff -= (uintptr_t)min;
diff -= sizeof(struct liballoc_minor);
diff -= min->size;
// minus already existing usage..
if (diff >= (size + sizeof(struct liballoc_minor)))
{
// yay....
min->next = (struct liballoc_minor *)((uintptr_t)min + sizeof(struct liballoc_minor) + min->size);
min->next->prev = min;
min = min->next;
min->next = NULL;
min->magic = LIBALLOC_MAGIC;
min->block = maj;
min->size = size;
min->req_size = req_size;
maj->usage += size + sizeof(struct liballoc_minor);
l_inuse += size;
p = (void *)((uintptr_t)min + sizeof(struct liballoc_minor));
ALIGN(p);
#ifdef LIBALLOCDEBUG
printf("CASE 4.1: returning %lx\n", p);
FLUSH();
#endif
liballoc_unlock(); // release the lock
return p;
}
}
// CASE 4.2: Is there space between two minors?
if (min->next != NULL)
{
// is the difference between here and next big enough?
diff = (uintptr_t)(min->next);
diff -= (uintptr_t)min;
diff -= sizeof(struct liballoc_minor);
diff -= min->size;
// minus our existing usage.
if (diff >= (size + sizeof(struct liballoc_minor)))
{
// yay......
new_min = (struct liballoc_minor *)((uintptr_t)min + sizeof(struct liballoc_minor) + min->size);
new_min->magic = LIBALLOC_MAGIC;
new_min->next = min->next;
new_min->prev = min;
new_min->size = size;
new_min->req_size = req_size;
new_min->block = maj;
min->next->prev = new_min;
min->next = new_min;
maj->usage += size + sizeof(struct liballoc_minor);
l_inuse += size;
p = (void *)((uintptr_t)new_min + sizeof(struct liballoc_minor));
ALIGN(p);
#ifdef LIBALLOCDEBUG
printf("CASE 4.2: returning %lx\n", p);
FLUSH();
#endif
liballoc_unlock(); // release the lock
return p;
}
} // min->next != NULL
min = min->next;
} // while min != NULL ...
#endif
#ifdef USE_CASE5
// CASE 5: Block full! Ensure next block and loop.
if (maj->next == NULL)
{
#ifdef LIBALLOCDEBUG
printf("CASE 5: block full\n");
FLUSH();
#endif
if (startedBet == 1)
{
maj = l_memRoot;
startedBet = 0;
continue;
}
// we've run out. we need more...
maj->next = allocate_new_page(size); // next one guaranteed to be okay
if (maj->next == NULL)
break; // uh oh, no more memory.....
maj->next->prev = maj;
}
#endif
maj = maj->next;
} // while (maj != NULL)
liballoc_unlock(); // release the lock
#ifdef LIBALLOCDEBUG
printf("All cases exhausted. No memory available.\n");
FLUSH();
#endif
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
printf("liballoc: WARNING: PREFIX(malloc)( %i ) returning NULL.\n", size);
liballoc_dump();
FLUSH();
#endif
return NULL;
}
__no_sanitize("undefined") void PREFIX(free)(void *ptr)
{
struct liballoc_minor *min;
struct liballoc_major *maj;
if (ptr == NULL)
{
l_warningCount += 1;
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
printf("liballoc: WARNING: PREFIX(free)( NULL ) called from %lx\n",
__builtin_return_address(0));
FLUSH();
#endif
return;
}
UNALIGN(ptr);
liballoc_lock(); // lockit
min = (struct liballoc_minor *)((uintptr_t)ptr - sizeof(struct liballoc_minor));
if (min->magic != LIBALLOC_MAGIC)
{
l_errorCount += 1;
// Check for overrun errors. For all bytes of LIBALLOC_MAGIC
if (
((min->magic & 0xFFFFFF) == (LIBALLOC_MAGIC & 0xFFFFFF)) ||
((min->magic & 0xFFFF) == (LIBALLOC_MAGIC & 0xFFFF)) ||
((min->magic & 0xFF) == (LIBALLOC_MAGIC & 0xFF)))
{
l_possibleOverruns += 1;
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
printf("liballoc: ERROR: Possible 1-3 byte overrun for magic %lx != %lx\n",
min->magic,
LIBALLOC_MAGIC);
FLUSH();
#endif
}
if (min->magic == LIBALLOC_DEAD)
{
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
printf("liballoc: ERROR: multiple PREFIX(free)() attempt on %lx from %lx.\n",
ptr,
__builtin_return_address(0));
FLUSH();
#endif
}
else
{
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
printf("liballoc: ERROR: Bad PREFIX(free)( %lx ) called from %lx\n",
ptr,
__builtin_return_address(0));
FLUSH();
#endif
}
// being lied to...
liballoc_unlock(); // release the lock
return;
}
#ifdef LIBALLOCDEBUG
printf("liballoc: %lx PREFIX(free)( %lx ): ",
__builtin_return_address(0),
ptr);
FLUSH();
#endif
maj = min->block;
l_inuse -= min->size;
maj->usage -= (min->size + sizeof(struct liballoc_minor));
min->magic = LIBALLOC_DEAD; // No mojo.
if (min->next != NULL)
min->next->prev = min->prev;
if (min->prev != NULL)
min->prev->next = min->next;
if (min->prev == NULL)
maj->first = min->next;
// Might empty the block. This was the first
// minor.
// We need to clean up after the majors now....
if (maj->first == NULL) // Block completely unused.
{
if (l_memRoot == maj)
l_memRoot = maj->next;
if (l_bestBet == maj)
l_bestBet = NULL;
if (maj->prev != NULL)
maj->prev->next = maj->next;
if (maj->next != NULL)
maj->next->prev = maj->prev;
l_allocated -= maj->size;
liballoc_free(maj, maj->pages);
}
else
{
if (l_bestBet != NULL)
{
int bestSize = l_bestBet->size - l_bestBet->usage;
int majSize = maj->size - maj->usage;
if (majSize > bestSize)
l_bestBet = maj;
}
}
#ifdef LIBALLOCDEBUG
printf("OK\n");
FLUSH();
#endif
liballoc_unlock(); // release the lock
}
__no_sanitize("undefined") void *PREFIX(calloc)(size_t nobj, size_t size)
{
int real_size;
void *p;
real_size = nobj * size;
p = PREFIX(malloc)(real_size);
liballoc_memset(p, 0, real_size);
return p;
}
__no_sanitize("undefined") void *PREFIX(realloc)(void *p, size_t size)
{
void *ptr;
struct liballoc_minor *min;
unsigned int real_size;
// Honour the case of size == 0 => free old and return NULL
if (size == 0)
{
PREFIX(free)
(p);
return NULL;
}
// In the case of a NULL pointer, return a simple malloc.
if (p == NULL)
return PREFIX(malloc)(size);
// Unalign the pointer if required.
ptr = p;
UNALIGN(ptr);
liballoc_lock(); // lockit
min = (struct liballoc_minor *)((uintptr_t)ptr - sizeof(struct liballoc_minor));
// Ensure it is a valid structure.
if (min->magic != LIBALLOC_MAGIC)
{
l_errorCount += 1;
// Check for overrun errors. For all bytes of LIBALLOC_MAGIC
if (
((min->magic & 0xFFFFFF) == (LIBALLOC_MAGIC & 0xFFFFFF)) ||
((min->magic & 0xFFFF) == (LIBALLOC_MAGIC & 0xFFFF)) ||
((min->magic & 0xFF) == (LIBALLOC_MAGIC & 0xFF)))
{
l_possibleOverruns += 1;
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
printf("liballoc: ERROR: Possible 1-3 byte overrun for magic %lx != %lx\n",
min->magic,
LIBALLOC_MAGIC);
FLUSH();
#endif
}
if (min->magic == LIBALLOC_DEAD)
{
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
printf("liballoc: ERROR: multiple PREFIX(free)() attempt on %lx from %lx.\n",
ptr,
__builtin_return_address(0));
FLUSH();
#endif
}
else
{
#if defined LIBALLOCDEBUG || defined LIBALLOCINFO
printf("liballoc: ERROR: Bad PREFIX(free)( %lx ) called from %lx\n",
ptr,
__builtin_return_address(0));
FLUSH();
#endif
}
// being lied to...
liballoc_unlock(); // release the lock
return NULL;
}
// Definitely a memory block.
real_size = min->req_size;
if (real_size >= size)
{
min->req_size = size;
liballoc_unlock();
return p;
}
liballoc_unlock();
// If we got here then we're reallocating to a block bigger than us.
ptr = PREFIX(malloc)(size); // We need to allocate new memory
liballoc_memcpy(ptr, p, real_size);
PREFIX(free)
(p);
return ptr;
}

View File

@ -0,0 +1,74 @@
#ifndef _LIBALLOC_H
#define _LIBALLOC_H
#include <types.h>
/** \defgroup ALLOCHOOKS liballoc hooks
*
* These are the OS specific functions which need to
* be implemented on any platform that the library
* is expected to work on.
*/
/** @{ */
// If we are told to not define our own size_t, then we skip the define.
// #define _HAVE_UINTPTR_T
// typedef unsigned long uintptr_t;
// This lets you prefix malloc and friends
#define PREFIX(func) kliballoc_##func
#ifdef __cplusplus
extern "C"
{
#endif
/** This function is supposed to lock the memory data structures. It
* could be as simple as disabling interrupts or acquiring a spinlock.
* It's up to you to decide.
*
* \return 0 if the lock was acquired successfully. Anything else is
* failure.
*/
extern int liballoc_lock();
/** This function unlocks what was previously locked by the liballoc_lock
* function. If it disabled interrupts, it enables interrupts. If it
* had acquiried a spinlock, it releases the spinlock. etc.
*
* \return 0 if the lock was successfully released.
*/
extern int liballoc_unlock();
/** This is the hook into the local system which allocates pages. It
* accepts an integer parameter which is the number of pages
* required. The page size was set up in the liballoc_init function.
*
* \return NULL if the pages were not allocated.
* \return A pointer to the allocated memory.
*/
extern void *liballoc_alloc(size_t);
/** This frees previously allocated memory. The void* parameter passed
* to the function is the exact same value returned from a previous
* liballoc_alloc call.
*
* The integer value is the number of pages to free.
*
* \return 0 if the memory was successfully freed.
*/
extern int liballoc_free(void *, size_t);
extern void *PREFIX(malloc)(size_t); ///< The standard function.
extern void *PREFIX(realloc)(void *, size_t); ///< The standard function.
extern void *PREFIX(calloc)(size_t, size_t); ///< The standard function.
extern void PREFIX(free)(void *); ///< The standard function.
#ifdef __cplusplus
}
#endif
/** @} */
#endif

View File

@ -0,0 +1,29 @@
#include <types.h>
#include <lock.hpp>
#include <memory.hpp>
NewLock(liballocLock);
EXTERNC int liballoc_lock()
{
return liballocLock.Lock(__FUNCTION__);
}
EXTERNC int liballoc_unlock()
{
return liballocLock.Unlock();
}
EXTERNC void *liballoc_alloc(size_t Pages)
{
void *ret = KernelAllocator.RequestPages(Pages);
debug("(%d) = %#lx", Pages, ret);
return ret;
}
EXTERNC int liballoc_free(void *Address, size_t Pages)
{
debug("(%#lx, %d)", Address, Pages);
KernelAllocator.FreePages(Address, Pages);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,371 @@
/* rpmalloc.h - Memory allocator - Public Domain - 2016 Mattias Jansson
*
* This library provides a cross-platform lock free thread caching malloc implementation in C11.
* The latest source code is always available at
*
* https://github.com/mjansson/rpmalloc
*
* This library is put in the public domain; you can redistribute it and/or modify it without any restrictions.
*
*/
#pragma once
#include <stddef.h>
#ifdef __cplusplus
extern "C"
{
#endif
#if defined(__clang__) || defined(__GNUC__)
#define RPMALLOC_EXPORT __attribute__((visibility("default")))
#define RPMALLOC_ALLOCATOR
#if (defined(__clang_major__) && (__clang_major__ < 4)) || (defined(__GNUC__) && defined(ENABLE_PRELOAD) && ENABLE_PRELOAD)
#define RPMALLOC_ATTRIB_MALLOC
#define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
#define RPMALLOC_ATTRIB_ALLOC_SIZE2(count, size)
#else
#define RPMALLOC_ATTRIB_MALLOC __attribute__((__malloc__))
#define RPMALLOC_ATTRIB_ALLOC_SIZE(size) __attribute__((alloc_size(size)))
#define RPMALLOC_ATTRIB_ALLOC_SIZE2(count, size) __attribute__((alloc_size(count, size)))
#endif
#define RPMALLOC_CDECL
#elif defined(_MSC_VER)
#define RPMALLOC_EXPORT
#define RPMALLOC_ALLOCATOR __declspec(allocator) __declspec(restrict)
#define RPMALLOC_ATTRIB_MALLOC
#define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
#define RPMALLOC_ATTRIB_ALLOC_SIZE2(count, size)
#define RPMALLOC_CDECL __cdecl
#else
#define RPMALLOC_EXPORT
#define RPMALLOC_ALLOCATOR
#define RPMALLOC_ATTRIB_MALLOC
#define RPMALLOC_ATTRIB_ALLOC_SIZE(size)
#define RPMALLOC_ATTRIB_ALLOC_SIZE2(count, size)
#define RPMALLOC_CDECL
#endif
//! Define RPMALLOC_CONFIGURABLE to enable configuring sizes. Will introduce
// a very small overhead due to some size calculations not being compile time constants
#ifndef RPMALLOC_CONFIGURABLE
#define RPMALLOC_CONFIGURABLE 1
#endif
//! Define RPMALLOC_FIRST_CLASS_HEAPS to enable heap based API (rpmalloc_heap_* functions).
// Will introduce a very small overhead to track fully allocated spans in heaps
#ifndef RPMALLOC_FIRST_CLASS_HEAPS
#define RPMALLOC_FIRST_CLASS_HEAPS 0
#endif
//! Flag to rpaligned_realloc to not preserve content in reallocation
#define RPMALLOC_NO_PRESERVE 1
//! Flag to rpaligned_realloc to fail and return null pointer if grow cannot be done in-place,
// in which case the original pointer is still valid (just like a call to realloc which failes to allocate
// a new block).
#define RPMALLOC_GROW_OR_FAIL 2
typedef struct rpmalloc_global_statistics_t
{
//! Current amount of virtual memory mapped, all of which might not have been committed (only if ENABLE_STATISTICS=1)
size_t mapped;
//! Peak amount of virtual memory mapped, all of which might not have been committed (only if ENABLE_STATISTICS=1)
size_t mapped_peak;
//! Current amount of memory in global caches for small and medium sizes (<32KiB)
size_t cached;
//! Current amount of memory allocated in huge allocations, i.e larger than LARGE_SIZE_LIMIT which is 2MiB by default (only if ENABLE_STATISTICS=1)
size_t huge_alloc;
//! Peak amount of memory allocated in huge allocations, i.e larger than LARGE_SIZE_LIMIT which is 2MiB by default (only if ENABLE_STATISTICS=1)
size_t huge_alloc_peak;
//! Total amount of memory mapped since initialization (only if ENABLE_STATISTICS=1)
size_t mapped_total;
//! Total amount of memory unmapped since initialization (only if ENABLE_STATISTICS=1)
size_t unmapped_total;
} rpmalloc_global_statistics_t;
typedef struct rpmalloc_thread_statistics_t
{
//! Current number of bytes available in thread size class caches for small and medium sizes (<32KiB)
size_t sizecache;
//! Current number of bytes available in thread span caches for small and medium sizes (<32KiB)
size_t spancache;
//! Total number of bytes transitioned from thread cache to global cache (only if ENABLE_STATISTICS=1)
size_t thread_to_global;
//! Total number of bytes transitioned from global cache to thread cache (only if ENABLE_STATISTICS=1)
size_t global_to_thread;
//! Per span count statistics (only if ENABLE_STATISTICS=1)
struct
{
//! Currently used number of spans
size_t current;
//! High water mark of spans used
size_t peak;
//! Number of spans transitioned to global cache
size_t to_global;
//! Number of spans transitioned from global cache
size_t from_global;
//! Number of spans transitioned to thread cache
size_t to_cache;
//! Number of spans transitioned from thread cache
size_t from_cache;
//! Number of spans transitioned to reserved state
size_t to_reserved;
//! Number of spans transitioned from reserved state
size_t from_reserved;
//! Number of raw memory map calls (not hitting the reserve spans but resulting in actual OS mmap calls)
size_t map_calls;
} span_use[64];
//! Per size class statistics (only if ENABLE_STATISTICS=1)
struct
{
//! Current number of allocations
size_t alloc_current;
//! Peak number of allocations
size_t alloc_peak;
//! Total number of allocations
size_t alloc_total;
//! Total number of frees
size_t free_total;
//! Number of spans transitioned to cache
size_t spans_to_cache;
//! Number of spans transitioned from cache
size_t spans_from_cache;
//! Number of spans transitioned from reserved state
size_t spans_from_reserved;
//! Number of raw memory map calls (not hitting the reserve spans but resulting in actual OS mmap calls)
size_t map_calls;
} size_use[128];
} rpmalloc_thread_statistics_t;
typedef struct rpmalloc_config_t
{
//! Map memory pages for the given number of bytes. The returned address MUST be
// aligned to the rpmalloc span size, which will always be a power of two.
// Optionally the function can store an alignment offset in the offset variable
// in case it performs alignment and the returned pointer is offset from the
// actual start of the memory region due to this alignment. The alignment offset
// will be passed to the memory unmap function. The alignment offset MUST NOT be
// larger than 65535 (storable in an uint16_t), if it is you must use natural
// alignment to shift it into 16 bits. If you set a memory_map function, you
// must also set a memory_unmap function or else the default implementation will
// be used for both. This function must be thread safe, it can be called by
// multiple threads simultaneously.
void *(*memory_map)(size_t size, size_t *offset);
//! Unmap the memory pages starting at address and spanning the given number of bytes.
// If release is set to non-zero, the unmap is for an entire span range as returned by
// a previous call to memory_map and that the entire range should be released. The
// release argument holds the size of the entire span range. If release is set to 0,
// the unmap is a partial decommit of a subset of the mapped memory range.
// If you set a memory_unmap function, you must also set a memory_map function or
// else the default implementation will be used for both. This function must be thread
// safe, it can be called by multiple threads simultaneously.
void (*memory_unmap)(void *address, size_t size, size_t offset, size_t release);
//! Called when an assert fails, if asserts are enabled. Will use the standard assert()
// if this is not set.
void (*error_callback)(const char *message);
//! Called when a call to map memory pages fails (out of memory). If this callback is
// not set or returns zero the library will return a null pointer in the allocation
// call. If this callback returns non-zero the map call will be retried. The argument
// passed is the number of bytes that was requested in the map call. Only used if
// the default system memory map function is used (memory_map callback is not set).
int (*map_fail_callback)(size_t size);
//! Size of memory pages. The page size MUST be a power of two. All memory mapping
// requests to memory_map will be made with size set to a multiple of the page size.
// Used if RPMALLOC_CONFIGURABLE is defined to 1, otherwise system page size is used.
size_t page_size;
//! Size of a span of memory blocks. MUST be a power of two, and in [4096,262144]
// range (unless 0 - set to 0 to use the default span size). Used if RPMALLOC_CONFIGURABLE
// is defined to 1.
size_t span_size;
//! Number of spans to map at each request to map new virtual memory blocks. This can
// be used to minimize the system call overhead at the cost of virtual memory address
// space. The extra mapped pages will not be written until actually used, so physical
// committed memory should not be affected in the default implementation. Will be
// aligned to a multiple of spans that match memory page size in case of huge pages.
size_t span_map_count;
//! Enable use of large/huge pages. If this flag is set to non-zero and page size is
// zero, the allocator will try to enable huge pages and auto detect the configuration.
// If this is set to non-zero and page_size is also non-zero, the allocator will
// assume huge pages have been configured and enabled prior to initializing the
// allocator.
// For Windows, see https://docs.microsoft.com/en-us/windows/desktop/memory/large-page-support
// For Linux, see https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt
int enable_huge_pages;
//! Respectively allocated pages and huge allocated pages names for systems
// supporting it to be able to distinguish among anonymous regions.
const char *page_name;
const char *huge_page_name;
} rpmalloc_config_t;
//! Initialize allocator with default configuration
RPMALLOC_EXPORT int
rpmalloc_initialize(void);
//! Initialize allocator with given configuration
RPMALLOC_EXPORT int
rpmalloc_initialize_config(const rpmalloc_config_t *config);
//! Get allocator configuration
RPMALLOC_EXPORT const rpmalloc_config_t *
rpmalloc_config(void);
//! Finalize allocator
RPMALLOC_EXPORT void
rpmalloc_finalize(void);
//! Initialize allocator for calling thread
RPMALLOC_EXPORT void
rpmalloc_thread_initialize(void);
//! Finalize allocator for calling thread
RPMALLOC_EXPORT void
rpmalloc_thread_finalize(int release_caches);
//! Perform deferred deallocations pending for the calling thread heap
RPMALLOC_EXPORT void
rpmalloc_thread_collect(void);
//! Query if allocator is initialized for calling thread
RPMALLOC_EXPORT int
rpmalloc_is_thread_initialized(void);
//! Get per-thread statistics
RPMALLOC_EXPORT void
rpmalloc_thread_statistics(rpmalloc_thread_statistics_t *stats);
//! Get global statistics
RPMALLOC_EXPORT void
rpmalloc_global_statistics(rpmalloc_global_statistics_t *stats);
//! Dump all statistics in human readable format to file (should be a FILE*)
RPMALLOC_EXPORT void
rpmalloc_dump_statistics(void *file);
//! Allocate a memory block of at least the given size
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void *
rpmalloc(size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(1);
//! Free the given memory block
RPMALLOC_EXPORT void
rpfree(void *ptr);
//! Allocate a memory block of at least the given size and zero initialize it
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void *
rpcalloc(size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(1, 2);
//! Reallocate the given block to at least the given size
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void *
rprealloc(void *ptr, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
//! Reallocate the given block to at least the given size and alignment,
// with optional control flags (see RPMALLOC_NO_PRESERVE).
// Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB)
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void *
rpaligned_realloc(void *ptr, size_t alignment, size_t size, size_t oldsize, unsigned int flags) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(3);
//! Allocate a memory block of at least the given size and alignment.
// Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB)
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void *
rpaligned_alloc(size_t alignment, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
//! Allocate a memory block of at least the given size and alignment, and zero initialize it.
// Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB)
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void *
rpaligned_calloc(size_t alignment, size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(2, 3);
//! Allocate a memory block of at least the given size and alignment.
// Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB)
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void *
rpmemalign(size_t alignment, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
//! Allocate a memory block of at least the given size and alignment.
// Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB)
RPMALLOC_EXPORT int
rpposix_memalign(void **memptr, size_t alignment, size_t size);
//! Query the usable size of the given memory block (from given pointer to the end of block)
RPMALLOC_EXPORT size_t
rpmalloc_usable_size(void *ptr);
#if RPMALLOC_FIRST_CLASS_HEAPS
//! Heap type
typedef struct heap_t rpmalloc_heap_t;
//! Acquire a new heap. Will reuse existing released heaps or allocate memory for a new heap
// if none available. Heap API is implemented with the strict assumption that only one single
// thread will call heap functions for a given heap at any given time, no functions are thread safe.
RPMALLOC_EXPORT rpmalloc_heap_t *
rpmalloc_heap_acquire(void);
//! Release a heap (does NOT free the memory allocated by the heap, use rpmalloc_heap_free_all before destroying the heap).
// Releasing a heap will enable it to be reused by other threads. Safe to pass a null pointer.
RPMALLOC_EXPORT void
rpmalloc_heap_release(rpmalloc_heap_t *heap);
//! Allocate a memory block of at least the given size using the given heap.
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void *
rpmalloc_heap_alloc(rpmalloc_heap_t *heap, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(2);
//! Allocate a memory block of at least the given size using the given heap. The returned
// block will have the requested alignment. Alignment must be a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB).
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void *
rpmalloc_heap_aligned_alloc(rpmalloc_heap_t *heap, size_t alignment, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(3);
//! Allocate a memory block of at least the given size using the given heap and zero initialize it.
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void *
rpmalloc_heap_calloc(rpmalloc_heap_t *heap, size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(2, 3);
//! Allocate a memory block of at least the given size using the given heap and zero initialize it. The returned
// block will have the requested alignment. Alignment must either be zero, or a power of two and a multiple of sizeof(void*),
// and should ideally be less than memory page size. A caveat of rpmalloc
// internals is that this must also be strictly less than the span size (default 64KiB).
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void *
rpmalloc_heap_aligned_calloc(rpmalloc_heap_t *heap, size_t alignment, size_t num, size_t size) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE2(2, 3);
//! Reallocate the given block to at least the given size. The memory block MUST be allocated
// by the same heap given to this function.
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void *
rpmalloc_heap_realloc(rpmalloc_heap_t *heap, void *ptr, size_t size, unsigned int flags) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(3);
//! Reallocate the given block to at least the given size. The memory block MUST be allocated
// by the same heap given to this function. The returned block will have the requested alignment.
// Alignment must be either zero, or a power of two and a multiple of sizeof(void*), and should ideally be
// less than memory page size. A caveat of rpmalloc internals is that this must also be strictly less than
// the span size (default 64KiB).
RPMALLOC_EXPORT RPMALLOC_ALLOCATOR void *
rpmalloc_heap_aligned_realloc(rpmalloc_heap_t *heap, void *ptr, size_t alignment, size_t size, unsigned int flags) RPMALLOC_ATTRIB_MALLOC RPMALLOC_ATTRIB_ALLOC_SIZE(4);
//! Free the given memory block from the given heap. The memory block MUST be allocated
// by the same heap given to this function.
RPMALLOC_EXPORT void
rpmalloc_heap_free(rpmalloc_heap_t *heap, void *ptr);
//! Free all memory allocated by the heap
RPMALLOC_EXPORT void
rpmalloc_heap_free_all(rpmalloc_heap_t *heap);
//! Set the given heap as the current heap for the calling thread. A heap MUST only be current heap
// for a single thread, a heap can never be shared between multiple threads. The previous
// current heap for the calling thread is released to be reused by other threads.
RPMALLOC_EXPORT void
rpmalloc_heap_thread_set_current(rpmalloc_heap_t *heap);
#endif
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,91 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <memory/macro.hpp>
#include <sys/mman.h>
#include <memory.hpp>
#include <assert.h>
#include <unistd.h>
// #include "rpmalloc.c"
#include "../../../../kernel.h"
struct heap_t
{
char pad[56408];
};
static heap_t *__rpmalloc_tls_heap = nullptr;
EXTERNC heap_t **__memory_thread_heap(void)
{
if (unlikely(!TaskManager || !thisThread))
{
if (unlikely(!__rpmalloc_tls_heap))
{
__rpmalloc_tls_heap = (heap_t *)KernelAllocator.RequestPages(TO_PAGES(sizeof(heap_t)));
debug("rpmalloc TLS heap: %#lx", __rpmalloc_tls_heap);
memset(__rpmalloc_tls_heap, 0, sizeof(heap_t));
assert(__rpmalloc_tls_heap);
}
return &__rpmalloc_tls_heap;
}
return &__rpmalloc_tls_heap;
heap_t *heap = (heap_t *)thisThread->TLS.pBase;
return (heap_t **)heap;
}
EXTERNC uintptr_t __get_tid(void)
{
if (unlikely(!TaskManager || !thisThread))
return (uintptr_t)-1;
return thisThread->ID;
}
EXTERNC long __rpmalloc_sysconf(int name)
{
switch (name)
{
case _SC_PAGESIZE:
return PAGE_SIZE;
default:
return -1;
}
}
EXTERNC void *__rpmalloc_mmap(void *addr, size_t length, int, int, int fd, off_t offset)
{
assert(addr == 0 && fd == -1 && offset == 0);
void *ptr = KernelAllocator.RequestPages(TO_PAGES(length));
debug("Requested %d pages, got %p", TO_PAGES(length), ptr);
if (ptr == nullptr)
return MAP_FAILED;
return ptr;
}
EXTERNC int __rpmalloc_munmap(void *addr, size_t length)
{
KernelAllocator.FreePages(addr, TO_PAGES(length));
debug("Freed %d pages at %p", TO_PAGES(length), addr);
return 0;
}
EXTERNC int __rpmalloc_posix_madvise(void *addr, size_t length, int advice)
{
func("%#lx %d %d", addr, length, advice);
return 0;
}

View File

@ -0,0 +1,75 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <memory.hpp>
#include "../../kernel.h"
namespace Memory
{
KernelStackManager::StackAllocation KernelStackManager::DetailedAllocate(size_t Size)
{
SmartLock(StackLock);
Size += 0x10;
size_t pagesNeeded = TO_PAGES(Size);
size_t stackSize = pagesNeeded * PAGE_SIZE;
assert((CurrentStackTop - stackSize) > KERNEL_STACK_BASE);
void *physicalMemory = KernelAllocator.RequestPages(pagesNeeded);
void *virtualAddress = (void *)(CurrentStackTop - stackSize);
Memory::Virtual vmm(KernelPageTable);
vmm.Map(virtualAddress, physicalMemory, stackSize, Memory::RW | Memory::G);
AllocatedStacks.push_back({physicalMemory, virtualAddress, stackSize});
CurrentStackTop -= stackSize;
TotalSize += stackSize;
return {physicalMemory, virtualAddress, stackSize};
}
void *KernelStackManager::Allocate(size_t Size)
{
return this->DetailedAllocate(Size).VirtualAddress;
}
void KernelStackManager::Free(void *Address)
{
SmartLock(StackLock);
auto it = std::find_if(AllocatedStacks.begin(), AllocatedStacks.end(),
[Address](const StackAllocation &stack)
{
return stack.VirtualAddress == Address;
});
if (it == AllocatedStacks.end())
return;
size_t pagesToFree = TO_PAGES(it->Size);
Memory::Virtual vmm(KernelPageTable);
vmm.Unmap(Address, it->Size);
KernelAllocator.FreePages(it->PhysicalAddress, pagesToFree);
TotalSize -= it->Size;
AllocatedStacks.erase(it);
}
KernelStackManager::KernelStackManager() {}
KernelStackManager::~KernelStackManager() {}
}

View File

@ -0,0 +1,616 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <memory.hpp>
#include <convert.h>
#include <lock.hpp>
#include <debug.h>
#ifdef DEBUG
#include <uart.hpp>
#endif
#include "heap_allocators/Xalloc/Xalloc.hpp"
#include "heap_allocators/liballoc_1_1/liballoc_1_1.h"
#include "heap_allocators/rpmalloc/rpmalloc.h"
#include "../../kernel.h"
// #define DEBUG_ALLOCATIONS 1
#ifdef DEBUG_ALLOCATIONS
#define memdbg(m, ...) \
debug(m, ##__VA_ARGS__); \
__sync
#else
#define memdbg(m, ...)
#endif
using namespace Memory;
Physical KernelAllocator;
Memory::KernelStackManager StackManager;
PageTable *KernelPageTable = nullptr;
bool Page1GBSupport = false;
bool PSESupport = false;
MemoryAllocatorType AllocatorType = MemoryAllocatorType::Pages;
Xalloc::V1 *XallocV1Allocator = nullptr;
Xalloc::V2 *XallocV2Allocator = nullptr;
#ifdef DEBUG
NIF void tracepagetable(PageTable *pt)
{
for (int i = 0; i < 512; i++)
{
#if defined(a64)
if (pt->Entries[i].Present)
debug("Entry %03d: %x %x %x %x %x %x %x %p-%#llx", i,
pt->Entries[i].Present, pt->Entries[i].ReadWrite,
pt->Entries[i].UserSupervisor, pt->Entries[i].WriteThrough,
pt->Entries[i].CacheDisable, pt->Entries[i].Accessed,
pt->Entries[i].ExecuteDisable, pt->Entries[i].Address << 12,
pt->Entries[i]);
#elif defined(a32)
#elif defined(aa64)
#endif
}
}
#endif
NIF void MapEntries(PageTable *PT)
{
debug("mapping %d memory entries", bInfo.Memory.Entries);
Virtual vmm = Virtual(PT);
for (uint64_t i = 0; i < bInfo.Memory.Entries; i++)
{
uintptr_t Base = r_cst(uintptr_t, bInfo.Memory.Entry[i].BaseAddress);
size_t Length = bInfo.Memory.Entry[i].Length;
debug("mapping %#lx-%#lx", Base, Base + Length);
vmm.Map((void *)Base, (void *)Base, Length, RW);
}
/* Make sure 0x0 is unmapped (so we PF when nullptr is accessed) */
vmm.Unmap((void *)0);
}
NIF void MapFramebuffer(PageTable *PT)
{
debug("Mapping Framebuffer");
Virtual vmm = Virtual(PT);
int itrfb = 0;
while (1)
{
if (!bInfo.Framebuffer[itrfb].BaseAddress)
break;
size_t fbSize = bInfo.Framebuffer[itrfb].Pitch * bInfo.Framebuffer[itrfb].Height;
fbSize = ALIGN_UP(fbSize, PAGE_SIZE);
#ifdef DEBUG
if (DebuggerIsAttached)
fbSize += 16 * PAGE_SIZE;
#endif
if (PSESupport && Page1GBSupport)
{
vmm.OptimizedMap(bInfo.Framebuffer[itrfb].BaseAddress,
bInfo.Framebuffer[itrfb].BaseAddress,
fbSize, RW | G | KRsv);
}
else
{
vmm.Map(bInfo.Framebuffer[itrfb].BaseAddress,
bInfo.Framebuffer[itrfb].BaseAddress,
fbSize, RW | G | KRsv);
}
itrfb++;
}
}
NIF void MapKernel(PageTable *PT)
{
debug("Mapping Kernel");
/* RWX */
uintptr_t BootstrapStart = (uintptr_t)&_bootstrap_start;
uintptr_t BootstrapEnd = (uintptr_t)&_bootstrap_end;
/* RX */
uintptr_t KernelTextStart = (uintptr_t)&_kernel_text_start;
uintptr_t KernelTextEnd = (uintptr_t)&_kernel_text_end;
/* RW */
uintptr_t KernelDataStart = (uintptr_t)&_kernel_data_start;
uintptr_t KernelDataEnd = (uintptr_t)&_kernel_data_end;
/* R */
uintptr_t KernelRoDataStart = (uintptr_t)&_kernel_rodata_start;
uintptr_t KernelRoDataEnd = (uintptr_t)&_kernel_rodata_end;
/* RW */
uintptr_t KernelBssStart = (uintptr_t)&_kernel_bss_start;
uintptr_t KernelBssEnd = (uintptr_t)&_kernel_bss_end;
#ifdef DEBUG
uintptr_t KernelStart = (uintptr_t)&_kernel_start;
uintptr_t KernelEnd = (uintptr_t)&_kernel_end;
#endif
uintptr_t KernelFileStart = (uintptr_t)bInfo.Kernel.FileBase;
uintptr_t KernelFileEnd = KernelFileStart + bInfo.Kernel.Size;
debug("Bootstrap: %#lx-%#lx", BootstrapStart, BootstrapEnd);
debug("Kernel text: %#lx-%#lx", KernelTextStart, KernelTextEnd);
debug("Kernel data: %#lx-%#lx", KernelDataStart, KernelDataEnd);
debug("Kernel rodata: %#lx-%#lx", KernelRoDataStart, KernelRoDataEnd);
debug("Kernel bss: %#lx-%#lx", KernelBssStart, KernelBssEnd);
debug("Kernel: %#lx-%#lx", KernelStart, KernelEnd);
debug("Kernel file: %#lx-%#lx", KernelFileStart, KernelFileEnd);
debug("File size: %ld KiB", TO_KiB(bInfo.Kernel.Size));
debug(".bootstrap size: %ld KiB", TO_KiB(BootstrapEnd - BootstrapStart));
debug(".text size: %ld KiB", TO_KiB(KernelTextEnd - KernelTextStart));
debug(".data size: %ld KiB", TO_KiB(KernelDataEnd - KernelDataStart));
debug(".rodata size: %ld KiB", TO_KiB(KernelRoDataEnd - KernelRoDataStart));
debug(".bss size: %ld KiB", TO_KiB(KernelBssEnd - KernelBssStart));
uintptr_t BaseKernelMapAddress = (uintptr_t)bInfo.Kernel.PhysicalBase;
debug("Base kernel map address: %#lx", BaseKernelMapAddress);
uintptr_t k;
Virtual vmm = Virtual(PT);
/* Bootstrap section */
if (BaseKernelMapAddress == BootstrapStart)
{
for (k = BootstrapStart; k < BootstrapEnd; k += PAGE_SIZE)
{
vmm.Map((void *)k, (void *)BaseKernelMapAddress, RW | G | KRsv);
KernelAllocator.ReservePage((void *)BaseKernelMapAddress);
BaseKernelMapAddress += PAGE_SIZE;
}
}
else
{
trace("Ignoring bootstrap section.");
/* Bootstrap section must be mapped at 0x100000. */
}
/* Text section */
for (k = KernelTextStart; k < KernelTextEnd; k += PAGE_SIZE)
{
vmm.Map((void *)k, (void *)BaseKernelMapAddress, RW | G | KRsv);
KernelAllocator.ReservePage((void *)BaseKernelMapAddress);
BaseKernelMapAddress += PAGE_SIZE;
}
/* Data section */
for (k = KernelDataStart; k < KernelDataEnd; k += PAGE_SIZE)
{
vmm.Map((void *)k, (void *)BaseKernelMapAddress, RW | G | KRsv);
KernelAllocator.ReservePage((void *)BaseKernelMapAddress);
BaseKernelMapAddress += PAGE_SIZE;
}
/* Read only data section */
for (k = KernelRoDataStart; k < KernelRoDataEnd; k += PAGE_SIZE)
{
vmm.Map((void *)k, (void *)BaseKernelMapAddress, G | KRsv);
KernelAllocator.ReservePage((void *)BaseKernelMapAddress);
BaseKernelMapAddress += PAGE_SIZE;
}
/* Block starting symbol section */
for (k = KernelBssStart; k < KernelBssEnd; k += PAGE_SIZE)
{
vmm.Map((void *)k, (void *)BaseKernelMapAddress, RW | G | KRsv);
KernelAllocator.ReservePage((void *)BaseKernelMapAddress);
BaseKernelMapAddress += PAGE_SIZE;
}
debug("Base kernel map address: %#lx", BaseKernelMapAddress);
/* Kernel file */
if (KernelFileStart != 0)
{
for (k = KernelFileStart; k < KernelFileEnd; k += PAGE_SIZE)
{
vmm.Map((void *)k, (void *)k, G | KRsv);
KernelAllocator.ReservePage((void *)k);
}
}
else
info("Cannot determine kernel file address. Ignoring.");
}
NIF void CreatePageTable(PageTable *pt)
{
static int check_cpuid = 0;
if (!check_cpuid++)
{
if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0)
{
CPU::x86::AMD::CPUID0x80000001 cpuid;
PSESupport = cpuid.EDX.PSE;
Page1GBSupport = cpuid.EDX.Page1GB;
}
else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0)
{
CPU::x86::Intel::CPUID0x00000001 cpuid;
PSESupport = cpuid.EDX.PSE;
}
if (PSESupport)
{
#if defined(a64)
CPU::x64::CR4 cr4 = CPU::x64::readcr4();
cr4.PSE = 1;
CPU::x64::writecr4(cr4);
#elif defined(a32)
CPU::x32::CR4 cr4 = CPU::x32::readcr4();
cr4.PSE = 1;
CPU::x32::writecr4(cr4);
#elif defined(aa64)
#endif
trace("PSE Support Enabled");
}
#ifdef DEBUG
if (Page1GBSupport)
debug("1GB Page Support Enabled");
#endif
}
/* TODO: Map faster */
MapEntries(pt);
MapFramebuffer(pt);
MapKernel(pt);
#ifdef DEBUG
tracepagetable(pt);
#endif
}
NIF void InitializeMemoryManagement()
{
#ifdef DEBUG
#ifndef a32
for (uint64_t i = 0; i < bInfo.Memory.Entries; i++)
{
uintptr_t Base = r_cst(uintptr_t, bInfo.Memory.Entry[i].BaseAddress);
size_t Length = bInfo.Memory.Entry[i].Length;
uintptr_t End = Base + Length;
const char *Type = "Unknown";
switch (bInfo.Memory.Entry[i].Type)
{
case likely(Usable):
Type = "Usable";
break;
case Reserved:
Type = "Reserved";
break;
case ACPIReclaimable:
Type = "ACPI Reclaimable";
break;
case ACPINVS:
Type = "ACPI NVS";
break;
case BadMemory:
Type = "Bad Memory";
break;
case BootloaderReclaimable:
Type = "Bootloader Reclaimable";
break;
case KernelAndModules:
Type = "Kernel and Modules";
break;
case Framebuffer:
Type = "Framebuffer";
break;
default:
break;
}
debug("%02ld: %p-%p %s",
i,
Base,
End,
Type);
}
#endif // a32
#endif // DEBUG
trace("Initializing Physical Memory Manager");
// KernelAllocator = Physical(); <- Already called in the constructor
KernelAllocator.Init();
debug("Memory Info:\n\n%lld MiB / %lld MiB (%lld MiB reserved)\n",
TO_MiB(KernelAllocator.GetUsedMemory()),
TO_MiB(KernelAllocator.GetTotalMemory()),
TO_MiB(KernelAllocator.GetReservedMemory()));
/* -- Debugging --
size_t bmap_size = KernelAllocator.GetPageBitmap().Size;
for (size_t i = 0; i < bmap_size; i++)
{
bool idx = KernelAllocator.GetPageBitmap().Get(i);
if (idx == true)
debug("Page %04d: %#lx", i, i * PAGE_SIZE);
}
inf_loop debug("Alloc.: %#lx", KernelAllocator.RequestPage());
*/
trace("Initializing Virtual Memory Manager");
KernelPageTable = (PageTable *)KernelAllocator.RequestPages(TO_PAGES(PAGE_SIZE + 1));
memset(KernelPageTable, 0, PAGE_SIZE);
CreatePageTable(KernelPageTable);
trace("Applying new page table from address %#lx",
KernelPageTable);
CPU::PageTable(KernelPageTable);
debug("Page table updated.");
/* FIXME: Read kernel params */
AllocatorType = Config.AllocatorType;
switch (AllocatorType)
{
case MemoryAllocatorType::Pages:
break;
case MemoryAllocatorType::XallocV1:
{
XallocV1Allocator = new Xalloc::V1((void *)nullptr, false, false);
trace("XallocV1 Allocator initialized at %#lx", XallocV1Allocator);
break;
}
case MemoryAllocatorType::XallocV2:
{
XallocV2Allocator = new Xalloc::V2((void *)nullptr);
trace("XallocV2 Allocator initialized at %#lx", XallocV2Allocator);
break;
}
case MemoryAllocatorType::liballoc11:
break;
case MemoryAllocatorType::rpmalloc_:
{
trace("Using rpmalloc allocator");
rpmalloc_initialize();
break;
rpmalloc_config_t config = {
.memory_map = nullptr,
.memory_unmap = nullptr,
.error_callback = nullptr,
.map_fail_callback = nullptr,
.page_size = PAGE_SIZE,
.span_size = 4 * 1024, /* 4 KiB */
.span_map_count = 1,
.enable_huge_pages = 0,
.page_name = nullptr,
.huge_page_name = nullptr};
rpmalloc_initialize_config(&config);
break;
}
default:
{
error("Unknown allocator type %d", AllocatorType);
CPU::Stop();
}
}
}
void *malloc(size_t Size)
{
if (Size == 0)
{
warn("Attempt to allocate 0 bytes");
Size = 16;
}
memdbg("malloc(%d)->[%s]", Size,
KernelSymbolTable ? KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(0))
: "Unknown");
void *ret = nullptr;
switch (AllocatorType)
{
case MemoryAllocatorType::Pages:
{
ret = KernelAllocator.RequestPages(TO_PAGES(Size + 1));
break;
}
case MemoryAllocatorType::XallocV1:
{
ret = XallocV1Allocator->malloc(Size);
break;
}
case MemoryAllocatorType::XallocV2:
{
ret = XallocV2Allocator->malloc(Size);
break;
}
case MemoryAllocatorType::liballoc11:
{
ret = PREFIX(malloc)(Size);
break;
}
case MemoryAllocatorType::rpmalloc_:
{
ret = rpmalloc(Size);
break;
}
default:
{
error("Unknown allocator type %d", AllocatorType);
CPU::Stop();
}
}
memset(ret, 0, Size);
return ret;
}
void *calloc(size_t n, size_t Size)
{
if (Size == 0)
{
warn("Attempt to allocate 0 bytes");
Size = 16;
}
memdbg("calloc(%d, %d)->[%s]", n, Size,
KernelSymbolTable ? KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(0))
: "Unknown");
void *ret = nullptr;
switch (AllocatorType)
{
case MemoryAllocatorType::Pages:
{
ret = KernelAllocator.RequestPages(TO_PAGES(n * Size + 1));
break;
}
case MemoryAllocatorType::XallocV1:
{
ret = XallocV1Allocator->calloc(n, Size);
break;
}
case MemoryAllocatorType::XallocV2:
{
ret = XallocV2Allocator->calloc(n, Size);
break;
}
case MemoryAllocatorType::liballoc11:
{
void *ret = PREFIX(calloc)(n, Size);
return ret;
}
case MemoryAllocatorType::rpmalloc_:
{
ret = rpcalloc(n, Size);
break;
}
default:
{
error("Unknown allocator type %d", AllocatorType);
CPU::Stop();
}
}
memset(ret, 0, n * Size);
return ret;
}
void *realloc(void *Address, size_t Size)
{
if (Size == 0)
{
warn("Attempt to allocate 0 bytes");
Size = 16;
}
memdbg("realloc(%#lx, %d)->[%s]", Address, Size,
KernelSymbolTable ? KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(0))
: "Unknown");
void *ret = nullptr;
switch (AllocatorType)
{
case unlikely(MemoryAllocatorType::Pages):
{
ret = KernelAllocator.RequestPages(TO_PAGES(Size + 1)); // WARNING: Potential memory leak
break;
}
case MemoryAllocatorType::XallocV1:
{
ret = XallocV1Allocator->realloc(Address, Size);
break;
}
case MemoryAllocatorType::XallocV2:
{
ret = XallocV2Allocator->realloc(Address, Size);
break;
}
case MemoryAllocatorType::liballoc11:
{
void *ret = PREFIX(realloc)(Address, Size);
return ret;
}
case MemoryAllocatorType::rpmalloc_:
{
ret = rprealloc(Address, Size);
break;
}
default:
{
error("Unknown allocator type %d", AllocatorType);
CPU::Stop();
}
}
memset(ret, 0, Size);
return ret;
}
void free(void *Address)
{
if (Address == nullptr)
{
warn("Attempt to free a null pointer");
return;
}
memdbg("free(%#lx)->[%s]", Address,
KernelSymbolTable ? KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(0))
: "Unknown");
switch (AllocatorType)
{
case unlikely(MemoryAllocatorType::Pages):
{
KernelAllocator.FreePage(Address); // WARNING: Potential memory leak
break;
}
case MemoryAllocatorType::XallocV1:
{
XallocV1Allocator->free(Address);
break;
}
case MemoryAllocatorType::XallocV2:
{
XallocV2Allocator->free(Address);
break;
}
case MemoryAllocatorType::liballoc11:
{
PREFIX(free)
(Address);
break;
}
case MemoryAllocatorType::rpmalloc_:
{
rpfree(Address);
break;
}
default:
{
error("Unknown allocator type %d", AllocatorType);
CPU::Stop();
}
}
}

View File

@ -0,0 +1,90 @@
#include <memory.hpp>
#include <filesystem.hpp>
#include <signal.hpp>
#include <utsname.h>
#include <time.h>
namespace Memory
{
void PageTable::Update()
{
#if defined(a86)
asmv("mov %0, %%cr3" ::"r"(this));
#elif defined(aa64)
asmv("msr ttbr0_el1, %0" ::"r"(this));
#endif
}
PageTable *PageTable::Fork()
{
PageTable *NewTable = (PageTable *)KernelAllocator.RequestPages(TO_PAGES(sizeof(PageTable)));
// memset(NewTable, 0, sizeof(PageTable));
// CreatePageTable(NewTable);
memcpy(NewTable, this, sizeof(PageTable));
debug("Forking page table %#lx to %#lx", this, NewTable);
#if defined(a64)
for (size_t i = 0; i < sizeof(Entries) / sizeof(Entries[0]); i++)
{
PageMapLevel4 *PML4 = &Entries[i];
PageMapLevel4 *NewPML4 = &NewTable->Entries[i];
if (!PML4->Present)
continue;
PageDirectoryPointerTableEntryPtr *ptrPDPT = (PageDirectoryPointerTableEntryPtr *)(PML4->GetAddress() << 12);
PageDirectoryPointerTableEntryPtr *ptrNewPDPT = (PageDirectoryPointerTableEntryPtr *)KernelAllocator.RequestPage();
NewPML4->SetAddress((uintptr_t)ptrNewPDPT >> 12);
for (size_t j = 0; j < sizeof(ptrPDPT->Entries) / sizeof(ptrPDPT->Entries[0]); j++)
{
PageDirectoryPointerTableEntry *PDPT = &ptrPDPT->Entries[j];
PageDirectoryPointerTableEntry *NewPDPT = &ptrNewPDPT->Entries[j];
*NewPDPT = *PDPT;
if (!PDPT->Present)
continue;
if (PDPT->PageSize)
continue;
PageDirectoryEntryPtr *ptrPDE = (PageDirectoryEntryPtr *)(PDPT->GetAddress() << 12);
PageDirectoryEntryPtr *ptrNewPDE = (PageDirectoryEntryPtr *)KernelAllocator.RequestPage();
NewPDPT->SetAddress((uintptr_t)ptrNewPDE >> 12);
for (size_t k = 0; k < sizeof(ptrPDE->Entries) / sizeof(ptrPDE->Entries[0]); k++)
{
PageDirectoryEntry *PDE = &ptrPDE->Entries[k];
PageDirectoryEntry *NewPDE = &ptrNewPDE->Entries[k];
*NewPDE = *PDE;
if (!PDE->Present)
continue;
if (PDE->PageSize)
continue;
PageTableEntryPtr *ptrPTE = (PageTableEntryPtr *)(PDE->GetAddress() << 12);
PageTableEntryPtr *ptrNewPTE = (PageTableEntryPtr *)KernelAllocator.RequestPage();
NewPDE->SetAddress((uintptr_t)ptrNewPTE >> 12);
for (size_t l = 0; l < sizeof(ptrPTE->Entries) / sizeof(ptrPTE->Entries[0]); l++)
{
PageTableEntry *PTE = &ptrPTE->Entries[l];
PageTableEntry *NewPTE = &ptrNewPTE->Entries[l];
*NewPTE = *PTE;
}
}
}
}
#else
#error "PageTable::Fork() not implemented for other architectures"
#endif
debug("Forked page table %#lx to %#lx", this, NewTable);
return NewTable;
}
/* We can't have Memory::Virtual in the header */
void *PageTable::__getPhysical(void *Address)
{
Virtual vmm(this);
void *PhysAddr = vmm.GetPhysical((void *)Address);
return PhysAddr;
}
}

View File

@ -0,0 +1,54 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <memory.hpp>
namespace Memory
{
Virtual::PageMapIndexer::PageMapIndexer(uintptr_t VirtualAddress)
{
uintptr_t Address = VirtualAddress;
#if defined(a64)
Address >>= 12;
this->PTEIndex = Address & 0x1FF;
Address >>= 9;
this->PDEIndex = Address & 0x1FF;
Address >>= 9;
this->PDPTEIndex = Address & 0x1FF;
Address >>= 9;
this->PMLIndex = Address & 0x1FF;
#elif defined(a32)
Address >>= 12;
this->PTEIndex = Address & 0x3FF;
Address >>= 10;
this->PDEIndex = Address & 0x3FF;
#elif defined(aa64)
#endif
if (VirtualAddress > PAGE_SIZE)
{
assert(
this->PTEIndex != 0 ||
this->PDEIndex != 0
#if defined(a64)
|| this->PDPTEIndex != 0 ||
this->PMLIndex != 0
#endif
);
}
}
}

356
Kernel/core/memory/pmm.cpp Normal file
View File

@ -0,0 +1,356 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <memory.hpp>
#include <acpi.hpp>
#include <debug.h>
#include <elf.h>
#ifdef DEBUG
#include <uart.hpp>
#endif
#include "../../kernel.h"
namespace Memory
{
uint64_t Physical::GetTotalMemory()
{
return this->TotalMemory.load();
}
uint64_t Physical::GetFreeMemory()
{
return this->FreeMemory.load();
}
uint64_t Physical::GetReservedMemory()
{
return this->ReservedMemory.load();
}
uint64_t Physical::GetUsedMemory()
{
return this->UsedMemory.load();
}
bool Physical::SwapPage(void *Address)
{
fixme("%p", Address);
return false;
}
bool Physical::SwapPages(void *Address, size_t PageCount)
{
for (size_t i = 0; i < PageCount; i++)
{
if (!this->SwapPage((void *)((uintptr_t)Address + (i * PAGE_SIZE))))
return false;
}
return false;
}
bool Physical::UnswapPage(void *Address)
{
fixme("%p", Address);
return false;
}
bool Physical::UnswapPages(void *Address, size_t PageCount)
{
for (size_t i = 0; i < PageCount; i++)
{
if (!this->UnswapPage((void *)((uintptr_t)Address + (i * PAGE_SIZE))))
return false;
}
return false;
}
void *Physical::RequestPage()
{
SmartLock(this->MemoryLock);
for (; PageBitmapIndex < PageBitmap.Size * 8; PageBitmapIndex++)
{
if (PageBitmap[PageBitmapIndex] == true)
continue;
this->LockPage((void *)(PageBitmapIndex * PAGE_SIZE));
return (void *)(PageBitmapIndex * PAGE_SIZE);
}
if (this->SwapPage((void *)(PageBitmapIndex * PAGE_SIZE)))
{
this->LockPage((void *)(PageBitmapIndex * PAGE_SIZE));
return (void *)(PageBitmapIndex * PAGE_SIZE);
}
if (TaskManager && !TaskManager->IsPanic())
{
error("Out of memory! Killing current process...");
TaskManager->KillProcess(thisProcess, Tasking::KILL_OOM);
TaskManager->Yield();
}
error("Out of memory! (Free: %ld MiB; Used: %ld MiB; Reserved: %ld MiB)",
TO_MiB(FreeMemory.load()), TO_MiB(UsedMemory.load()), TO_MiB(ReservedMemory.load()));
KPrint("Out of memory! (Free: %ld MiB; Used: %ld MiB; Reserved: %ld MiB)",
TO_MiB(FreeMemory.load()), TO_MiB(UsedMemory.load()), TO_MiB(ReservedMemory.load()));
debug("Raw values: free %#lx used %#lx reserved %#lx",
FreeMemory.load(), UsedMemory.load(), ReservedMemory.load());
CPU::Stop();
__builtin_unreachable();
}
void *Physical::RequestPages(size_t Count)
{
SmartLock(this->MemoryLock);
for (; PageBitmapIndex < PageBitmap.Size * 8; PageBitmapIndex++)
{
if (PageBitmap[PageBitmapIndex] == true)
continue;
for (uint64_t Index = PageBitmapIndex; Index < PageBitmap.Size * 8; Index++)
{
if (PageBitmap[Index] == true)
continue;
for (size_t i = 0; i < Count; i++)
{
if (PageBitmap[Index + i] == true)
goto NextPage;
}
this->LockPages((void *)(Index * PAGE_SIZE), Count);
return (void *)(Index * PAGE_SIZE);
NextPage:
Index += Count;
continue;
}
}
if (this->SwapPages((void *)(PageBitmapIndex * PAGE_SIZE), Count))
{
this->LockPages((void *)(PageBitmapIndex * PAGE_SIZE), Count);
return (void *)(PageBitmapIndex * PAGE_SIZE);
}
if (TaskManager && !TaskManager->IsPanic())
{
error("Out of memory! Killing current process...");
TaskManager->KillProcess(thisProcess, Tasking::KILL_OOM);
TaskManager->Yield();
}
error("Out of memory! (Free: %ld MiB; Used: %ld MiB; Reserved: %ld MiB)",
TO_MiB(FreeMemory.load()), TO_MiB(UsedMemory.load()), TO_MiB(ReservedMemory.load()));
KPrint("Out of memory! (Free: %ld MiB; Used: %ld MiB; Reserved: %ld MiB)",
TO_MiB(FreeMemory.load()), TO_MiB(UsedMemory.load()), TO_MiB(ReservedMemory.load()));
debug("Raw values: free %#lx used %#lx reserved %#lx",
FreeMemory.load(), UsedMemory.load(), ReservedMemory.load());
CPU::Halt(true);
__builtin_unreachable();
}
void Physical::FreePage(void *Address)
{
SmartLock(this->MemoryLock);
if (unlikely(Address == nullptr))
{
warn("Null pointer passed to FreePage.");
return;
}
size_t Index = (size_t)Address / PAGE_SIZE;
if (unlikely(PageBitmap[Index] == false))
{
warn("Tried to free an already free page. (%p)",
Address);
return;
}
if (PageBitmap.Set(Index, false))
{
FreeMemory.fetch_add(PAGE_SIZE);
UsedMemory.fetch_sub(PAGE_SIZE);
if (PageBitmapIndex > Index)
PageBitmapIndex = Index;
}
}
void Physical::FreePages(void *Address, size_t Count)
{
if (unlikely(Address == nullptr || Count == 0))
{
warn("%s%s%s passed to FreePages.", Address == nullptr ? "Null pointer " : "", Address == nullptr && Count == 0 ? "and " : "", Count == 0 ? "Zero count" : "");
return;
}
for (size_t t = 0; t < Count; t++)
this->FreePage((void *)((uintptr_t)Address + (t * PAGE_SIZE)));
}
void Physical::LockPage(void *Address)
{
if (unlikely(Address == nullptr))
warn("Trying to lock null address.");
uintptr_t Index = (uintptr_t)Address / PAGE_SIZE;
if (unlikely(PageBitmap[Index] == true))
return;
if (PageBitmap.Set(Index, true))
{
FreeMemory.fetch_sub(PAGE_SIZE);
UsedMemory.fetch_add(PAGE_SIZE);
}
}
void Physical::LockPages(void *Address, size_t PageCount)
{
if (unlikely(Address == nullptr || PageCount == 0))
warn("Trying to lock %s%s.",
Address ? "null address" : "",
PageCount ? "0 pages" : "");
for (size_t i = 0; i < PageCount; i++)
this->LockPage((void *)((uintptr_t)Address + (i * PAGE_SIZE)));
}
void Physical::ReservePage(void *Address)
{
if (unlikely(Address == nullptr))
warn("Trying to reserve null address.");
uintptr_t Index = (Address == NULL) ? 0 : (uintptr_t)Address / PAGE_SIZE;
if (unlikely(PageBitmap[Index] == true))
return;
if (PageBitmap.Set(Index, true))
{
FreeMemory.fetch_sub(PAGE_SIZE);
ReservedMemory.fetch_add(PAGE_SIZE);
}
}
void Physical::ReservePages(void *Address, size_t PageCount)
{
if (unlikely(Address == nullptr || PageCount == 0))
warn("Trying to reserve %s%s.",
Address ? "null address" : "",
PageCount ? "0 pages" : "");
for (size_t t = 0; t < PageCount; t++)
{
uintptr_t Index = ((uintptr_t)Address + (t * PAGE_SIZE)) / PAGE_SIZE;
if (unlikely(PageBitmap[Index] == true))
return;
if (PageBitmap.Set(Index, true))
{
FreeMemory.fetch_sub(PAGE_SIZE);
ReservedMemory.fetch_add(PAGE_SIZE);
}
}
}
void Physical::UnreservePage(void *Address)
{
if (unlikely(Address == nullptr))
warn("Trying to unreserve null address.");
uintptr_t Index = (Address == NULL) ? 0 : (uintptr_t)Address / PAGE_SIZE;
if (unlikely(PageBitmap[Index] == false))
return;
if (PageBitmap.Set(Index, false))
{
FreeMemory.fetch_add(PAGE_SIZE);
ReservedMemory.fetch_sub(PAGE_SIZE);
if (PageBitmapIndex > Index)
PageBitmapIndex = Index;
}
}
void Physical::UnreservePages(void *Address, size_t PageCount)
{
if (unlikely(Address == nullptr || PageCount == 0))
warn("Trying to unreserve %s%s.",
Address ? "null address" : "",
PageCount ? "0 pages" : "");
for (size_t t = 0; t < PageCount; t++)
{
uintptr_t Index = ((uintptr_t)Address + (t * PAGE_SIZE)) / PAGE_SIZE;
if (unlikely(PageBitmap[Index] == false))
return;
if (PageBitmap.Set(Index, false))
{
FreeMemory.fetch_add(PAGE_SIZE);
ReservedMemory.fetch_sub(PAGE_SIZE);
if (PageBitmapIndex > Index)
PageBitmapIndex = Index;
}
}
}
void Physical::Init()
{
SmartLock(this->MemoryLock);
uint64_t MemorySize = bInfo.Memory.Size;
debug("Memory size: %lld bytes (%ld pages)",
MemorySize, TO_PAGES(MemorySize));
TotalMemory.store(MemorySize);
FreeMemory.store(MemorySize);
size_t BitmapSize = (size_t)(MemorySize / PAGE_SIZE) / 8 + 1;
uintptr_t BitmapAddress = 0x0;
size_t BitmapAddressSize = 0;
FindBitmapRegion(BitmapAddress, BitmapAddressSize);
if (BitmapAddress == 0x0)
{
error("No free memory found!");
CPU::Stop();
}
debug("Initializing Bitmap at %p-%p (%d Bytes)",
BitmapAddress,
(void *)(BitmapAddress + BitmapSize),
BitmapSize);
PageBitmap.Size = BitmapSize;
PageBitmap.Buffer = (uint8_t *)BitmapAddress;
for (size_t i = 0; i < BitmapSize; i++)
*(uint8_t *)(PageBitmap.Buffer + i) = 0;
ReserveEssentials();
}
Physical::Physical() {}
Physical::~Physical() {}
}

View File

@ -0,0 +1,207 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <memory.hpp>
#include <acpi.hpp>
#include <debug.h>
#include <elf.h>
#ifdef DEBUG
#include <uart.hpp>
#endif
#include "../../kernel.h"
namespace Memory
{
__no_sanitize("alignment") void Physical::ReserveEssentials()
{
debug("Reserving pages...");
/* The bootloader won't give us the entire mapping, so we
reserve everything and then unreserve the usable pages. */
this->ReservePages(0, TO_PAGES(bInfo.Memory.Size));
debug("Unreserving usable pages...");
for (uint64_t i = 0; i < bInfo.Memory.Entries; i++)
{
if (bInfo.Memory.Entry[i].Type == Usable)
{
if (uintptr_t(bInfo.Memory.Entry[i].BaseAddress) <= 0xFFFFF)
continue;
this->UnreservePages(bInfo.Memory.Entry[i].BaseAddress,
TO_PAGES(bInfo.Memory.Entry[i].Length));
}
}
debug("Reserving 0x0-0xFFFFF range...");
// this->ReservePage((void *)0x0); /* Trampoline stack, gdt, idt, etc... */
// this->ReservePages((void *)0x2000, 4); /* TRAMPOLINE_START */
/* Reserve the lower part of memory. (0x0-0xFFFFF)
This includes: BIOS, EBDA, VGA, SMP, etc...
https://wiki.osdev.org/Memory_Map_(x86)
*/
this->ReservePages((void *)0x0, TO_PAGES(0xFFFFF));
debug("Reserving bitmap region %#lx-%#lx...",
PageBitmap.Buffer,
(void *)((uintptr_t)PageBitmap.Buffer + PageBitmap.Size));
this->ReservePages(PageBitmap.Buffer, TO_PAGES(PageBitmap.Size));
debug("Reserving kernel physical region %#lx-%#lx...",
bInfo.Kernel.PhysicalBase,
(void *)((uintptr_t)bInfo.Kernel.PhysicalBase + bInfo.Kernel.Size));
this->ReservePages(bInfo.Kernel.PhysicalBase, TO_PAGES(bInfo.Kernel.Size));
debug("Reserving kernel file and symbols...");
if (bInfo.Kernel.FileBase)
this->ReservePages(bInfo.Kernel.FileBase, TO_PAGES(bInfo.Kernel.Size));
if (bInfo.Kernel.Symbols.Num &&
bInfo.Kernel.Symbols.EntSize &&
bInfo.Kernel.Symbols.Shndx)
{
char *sections = r_cst(char *, bInfo.Kernel.Symbols.Sections);
debug("Reserving sections region %#lx-%#lx...",
sections,
(void *)((uintptr_t)sections + bInfo.Kernel.Symbols.EntSize *
bInfo.Kernel.Symbols.Num));
this->ReservePages(sections, TO_PAGES(bInfo.Kernel.Symbols.EntSize *
bInfo.Kernel.Symbols.Num));
Elf_Sym *Symbols = nullptr;
uint8_t *StringAddress = nullptr;
#if defined(a64) || defined(aa64)
Elf64_Xword SymbolSize = 0;
Elf64_Xword StringSize = 0;
#elif defined(a32)
Elf32_Word SymbolSize = 0;
Elf32_Word StringSize = 0;
#endif
for (size_t i = 0; i < bInfo.Kernel.Symbols.Num; ++i)
{
Elf_Shdr *sym = (Elf_Shdr *)&sections[bInfo.Kernel.Symbols.EntSize * i];
Elf_Shdr *str = (Elf_Shdr *)&sections[bInfo.Kernel.Symbols.EntSize *
sym->sh_link];
if (sym->sh_type == SHT_SYMTAB &&
str->sh_type == SHT_STRTAB)
{
Symbols = (Elf_Sym *)sym->sh_addr;
StringAddress = (uint8_t *)str->sh_addr;
SymbolSize = (int)sym->sh_size;
StringSize = (int)str->sh_size;
debug("Symbol table found, %d entries (%ld KiB)",
SymbolSize / sym->sh_entsize,
TO_KiB(SymbolSize));
this->ReservePages(Symbols, TO_PAGES(SymbolSize));
break;
}
}
if (Symbols)
{
debug("Reserving symbol table region %#lx-%#lx...",
Symbols, (void *)((uintptr_t)Symbols + SymbolSize));
this->ReservePages(Symbols, TO_PAGES(SymbolSize));
}
if (StringAddress)
{
debug("Reserving string table region %#lx-%#lx...",
StringAddress, (void *)((uintptr_t)StringAddress + StringSize));
this->ReservePages(StringAddress, TO_PAGES(StringSize));
}
}
debug("Reserving kernel modules...");
for (uint64_t i = 0; i < MAX_MODULES; i++)
{
if (bInfo.Modules[i].Address == 0x0)
continue;
debug("Reserving module %s (%#lx-%#lx)...", bInfo.Modules[i].CommandLine,
bInfo.Modules[i].Address,
(void *)((uintptr_t)bInfo.Modules[i].Address + bInfo.Modules[i].Size));
this->ReservePages((void *)bInfo.Modules[i].Address,
TO_PAGES(bInfo.Modules[i].Size));
}
#if defined(a86)
if (bInfo.RSDP)
{
debug("Reserving RSDT region %#lx-%#lx...", bInfo.RSDP,
(void *)((uintptr_t)bInfo.RSDP + sizeof(BootInfo::RSDPInfo)));
this->ReservePages(bInfo.RSDP, TO_PAGES(sizeof(BootInfo::RSDPInfo)));
ACPI::ACPI::ACPIHeader *ACPIPtr;
bool XSDT = false;
if (bInfo.RSDP->Revision >= 2 && bInfo.RSDP->XSDTAddress)
{
ACPIPtr = (ACPI::ACPI::ACPIHeader *)bInfo.RSDP->XSDTAddress;
XSDT = true;
}
else
ACPIPtr = (ACPI::ACPI::ACPIHeader *)(uintptr_t)bInfo.RSDP->RSDTAddress;
debug("Reserving RSDT...");
this->ReservePages((void *)bInfo.RSDP, TO_PAGES(sizeof(BootInfo::RSDPInfo)));
if (!Memory::Virtual().Check(ACPIPtr))
{
error("ACPI table is located in an unmapped region.");
return;
}
size_t TableSize = ((ACPIPtr->Length - sizeof(ACPI::ACPI::ACPIHeader)) /
(XSDT ? 8 : 4));
debug("Reserving %d ACPI tables...", TableSize);
for (size_t t = 0; t < TableSize; t++)
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
// TODO: Should I be concerned about unaligned memory access?
ACPI::ACPI::ACPIHeader *SDTHdr = nullptr;
if (XSDT)
SDTHdr =
(ACPI::ACPI::ACPIHeader *)(*(uint64_t *)((uint64_t)ACPIPtr +
sizeof(ACPI::ACPI::ACPIHeader) +
(t * 8)));
else
SDTHdr =
(ACPI::ACPI::ACPIHeader *)(*(uint32_t *)((uint64_t)ACPIPtr +
sizeof(ACPI::ACPI::ACPIHeader) +
(t * 4)));
#pragma GCC diagnostic pop
this->ReservePages(SDTHdr, TO_PAGES(SDTHdr->Length));
}
}
#elif defined(aa64)
#endif
}
}

View File

@ -0,0 +1,164 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <memory.hpp>
#include <debug.h>
namespace Memory
{
bool StackGuard::Expand(uintptr_t FaultAddress)
{
if (!this->UserMode)
assert(!"Kernel mode stack expansion not implemented");
if (FaultAddress < USER_STACK_END ||
FaultAddress > USER_STACK_BASE)
{
info("Fault address %#lx is not in range of stack %#lx - %#lx",
FaultAddress, USER_STACK_END, USER_STACK_BASE);
return false; /* It's not about the stack. */
}
uintptr_t roundFA = ROUND_DOWN(FaultAddress, PAGE_SIZE);
uintptr_t diff = (uintptr_t)this->StackBottom - roundFA;
size_t stackPages = TO_PAGES(diff);
stackPages = stackPages < 1 ? 1 : stackPages;
debug("roundFA: %#lx, sb: %#lx, diff: %#lx, stackPages: %d",
roundFA, this->StackBottom, diff, stackPages);
void *AllocatedStack = vma->RequestPages(stackPages);
debug("AllocatedStack: %#lx", AllocatedStack);
for (size_t i = 0; i < stackPages; i++)
{
void *vAddress = (void *)((uintptr_t)this->StackBottom - (i * PAGE_SIZE));
void *pAddress = (void *)((uintptr_t)AllocatedStack + (i * PAGE_SIZE));
vma->Map(vAddress, pAddress, PAGE_SIZE, PTFlag::RW | PTFlag::US);
AllocatedPages ap = {
.PhysicalAddress = pAddress,
.VirtualAddress = vAddress,
};
AllocatedPagesList.push_back(ap);
debug("Mapped %#lx to %#lx", pAddress, vAddress);
}
this->StackBottom = (void *)((uintptr_t)this->StackBottom - (stackPages * PAGE_SIZE));
this->Size += stackPages * PAGE_SIZE;
debug("Stack expanded to %#lx", this->StackBottom);
this->Expanded = true;
return true;
}
void StackGuard::Fork(StackGuard *Parent)
{
if (!this->UserMode)
assert(!"Kernel mode stack fork not implemented");
this->UserMode = Parent->UserMode;
this->StackBottom = Parent->StackBottom;
this->StackTop = Parent->StackTop;
this->StackPhysicalBottom = Parent->StackPhysicalBottom;
this->StackPhysicalTop = Parent->StackPhysicalTop;
this->Size = Parent->Size;
this->Expanded = Parent->Expanded;
std::list<AllocatedPages> ParentAllocatedPages = Parent->GetAllocatedPages();
foreach (auto Page in ParentAllocatedPages)
{
void *NewPhysical = vma->RequestPages(1);
debug("Forking address %#lx to %#lx", Page.PhysicalAddress, NewPhysical);
memcpy(NewPhysical, Page.PhysicalAddress, PAGE_SIZE);
vma->Remap(Page.VirtualAddress, NewPhysical, PTFlag::RW | PTFlag::US);
AllocatedPages ap = {
.PhysicalAddress = NewPhysical,
.VirtualAddress = Page.VirtualAddress,
};
AllocatedPagesList.push_back(ap);
debug("Mapped %#lx to %#lx", NewPhysical, Page.VirtualAddress);
}
}
StackGuard::StackGuard(bool User, VirtualMemoryArea *_vma)
{
this->UserMode = User;
this->vma = _vma;
if (this->UserMode)
{
void *AllocatedStack = vma->RequestPages(TO_PAGES(USER_STACK_SIZE));
this->StackBottom = (void *)USER_STACK_BASE;
this->StackTop = (void *)(USER_STACK_BASE + USER_STACK_SIZE);
this->StackPhysicalBottom = AllocatedStack;
this->StackPhysicalTop = (void *)((uintptr_t)AllocatedStack + USER_STACK_SIZE);
this->Size = USER_STACK_SIZE;
debug("AllocatedStack: %#lx", AllocatedStack);
for (size_t i = 0; i < TO_PAGES(USER_STACK_SIZE); i++)
{
void *vAddress = (void *)(USER_STACK_BASE + (i * PAGE_SIZE));
void *pAddress = (void *)((uintptr_t)AllocatedStack + (i * PAGE_SIZE));
vma->Map(vAddress, pAddress, PAGE_SIZE, PTFlag::RW | PTFlag::US);
AllocatedPages ap = {
.PhysicalAddress = pAddress,
.VirtualAddress = vAddress,
};
AllocatedPagesList.push_back(ap);
debug("Mapped %#lx to %#lx", pAddress, vAddress);
}
}
else
{
Memory::KernelStackManager::StackAllocation sa = StackManager.DetailedAllocate(LARGE_STACK_SIZE);
this->StackBottom = sa.VirtualAddress;
this->StackTop = (void *)((uintptr_t)this->StackBottom + LARGE_STACK_SIZE);
this->StackPhysicalBottom = sa.PhysicalAddress;
this->StackPhysicalTop = (void *)((uintptr_t)this->StackPhysicalBottom + LARGE_STACK_SIZE);
this->Size = LARGE_STACK_SIZE;
debug("StackBottom: %#lx", this->StackBottom);
for (size_t i = 0; i < TO_PAGES(LARGE_STACK_SIZE); i++)
{
AllocatedPages pa = {
.PhysicalAddress = (void *)((uintptr_t)this->StackPhysicalBottom + (i * PAGE_SIZE)),
.VirtualAddress = (void *)((uintptr_t)this->StackBottom + (i * PAGE_SIZE)),
};
AllocatedPagesList.push_back(pa);
}
}
debug("Allocated stack at %#lx", this->StackBottom);
debug("Stack Range: %#lx - %#lx", this->StackBottom, this->StackTop);
}
StackGuard::~StackGuard()
{
if (!this->UserMode)
{
for (auto Page : this->AllocatedPagesList)
StackManager.Free(Page.VirtualAddress);
}
/* VMA will free the stack */
}
}

142
Kernel/core/memory/va.cpp Normal file
View File

@ -0,0 +1,142 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <memory/table.hpp>
#include <memory/va.hpp>
#include <cpu.hpp>
#include <debug.h>
#include <bitset>
#include "../../kernel.h"
namespace Memory
{
VirtualAllocation::AllocatedPages VirtualAllocation::RequestPages(size_t Count)
{
func("%lld", Count);
void *pAddress = KernelAllocator.RequestPages(Count);
memset(pAddress, 0, FROM_PAGES(Count));
Virtual vmm(this->Table);
SmartLock(MgrLock);
forItr(itr, AllocatedPagesList)
{
if (likely(itr->Free == false))
continue;
if (itr->PageCount == Count)
{
itr->Free = false;
vmm.Map(itr->VirtualAddress, pAddress, FROM_PAGES(Count), RW | KRsv | G);
return *itr;
}
if (itr->PageCount > Count)
{
/* Split the block */
void *vAddress = itr->VirtualAddress;
void *pAddress = itr->PhysicalAddress;
size_t PageCount = itr->PageCount;
AllocatedPagesList.erase(itr);
AllocatedPagesList.push_back({(void *)((uintptr_t)pAddress + FROM_PAGES(Count)),
(void *)((uintptr_t)vAddress + FROM_PAGES(Count)),
PageCount - Count, true});
AllocatedPagesList.push_back({pAddress, vAddress, Count, false});
vmm.Map(vAddress, pAddress, FROM_PAGES(Count), RW | KRsv | G);
debug("Split region %#lx-%#lx", vAddress, (uintptr_t)vAddress + FROM_PAGES(Count));
debug("Free region %#lx-%#lx", (uintptr_t)vAddress + FROM_PAGES(Count), (uintptr_t)vAddress + FROM_PAGES(PageCount - Count));
return AllocatedPagesList.back();
}
}
/* Allocate new region */
void *vAddress = CurrentBase;
vmm.Map(vAddress, pAddress, FROM_PAGES(Count), RW | KRsv | G);
AllocatedPagesList.push_back({pAddress, vAddress, Count, false});
debug("New region %#lx-%#lx", vAddress, (uintptr_t)vAddress + FROM_PAGES(Count));
CurrentBase = (void *)((uintptr_t)CurrentBase + FROM_PAGES(Count));
assert(USER_ALLOC_END > (uintptr_t)CurrentBase);
return AllocatedPagesList.back();
}
void VirtualAllocation::FreePages(void *Address, size_t Count)
{
func("%#lx, %lld", Address, Count);
SmartLock(MgrLock);
foreach (auto &apl in AllocatedPagesList)
{
if (apl.VirtualAddress != Address)
continue;
if (apl.PageCount != Count)
{
error("Page count mismatch! (Allocated: %lld, Requested: %lld)",
apl.PageCount, Count);
return;
}
Virtual vmm(this->Table);
for (size_t i = 0; i < Count; i++)
{
void *AddressToUnmap = (void *)((uintptr_t)Address + FROM_PAGES(i));
vmm.Unmap(AddressToUnmap);
}
KernelAllocator.FreePages(Address, Count);
apl.Free = true;
debug("Freed region %#lx-%#lx", Address, (uintptr_t)Address + FROM_PAGES(Count));
return;
}
}
void VirtualAllocation::MapTo(AllocatedPages ap, PageTable *TargetTable)
{
func("%#lx, %#lx", ap.VirtualAddress, TargetTable);
Virtual vmm(TargetTable);
vmm.Map(ap.VirtualAddress, ap.PhysicalAddress, FROM_PAGES(ap.PageCount), RW | KRsv | G);
}
VirtualAllocation::VirtualAllocation(void *Base)
: BaseAddress(Base), CurrentBase(Base),
Table((PageTable *)CPU::PageTable())
{
func("%#lx", Base);
}
VirtualAllocation::~VirtualAllocation()
{
/* No need to remap pages, the page table will be destroyed */
Virtual vmm(this->Table);
foreach (auto ap in AllocatedPagesList)
{
KernelAllocator.FreePages(ap.PhysicalAddress, ap.PageCount);
for (size_t i = 0; i < ap.PageCount; i++)
{
void *AddressToUnmap = (void *)((uintptr_t)ap.VirtualAddress + FROM_PAGES(i));
vmm.Unmap(AddressToUnmap);
}
}
}
}

494
Kernel/core/memory/vma.cpp Normal file
View File

@ -0,0 +1,494 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <memory/vma.hpp>
#include <memory/table.hpp>
#include <cpu.hpp>
#include <debug.h>
#include <bitset>
#include "../../kernel.h"
namespace Memory
{
uint64_t VirtualMemoryArea::GetAllocatedMemorySize()
{
SmartLock(MgrLock);
uint64_t Size = 0;
foreach (auto ap in AllocatedPagesList)
Size += ap.PageCount;
return FROM_PAGES(Size);
}
void *VirtualMemoryArea::RequestPages(size_t Count, bool User, bool Protect)
{
func("%lld, %s, %s", Count,
User ? "true" : "false",
Protect ? "true" : "false");
void *Address = KernelAllocator.RequestPages(Count);
memset(Address, 0, Count * PAGE_SIZE);
int Flags = PTFlag::RW;
if (User)
Flags |= PTFlag::US;
if (Protect)
Flags |= PTFlag::KRsv;
Virtual vmm(this->Table);
SmartLock(MgrLock);
vmm.Map(Address, Address, FROM_PAGES(Count), Flags);
AllocatedPagesList.push_back({Address, Count, Protect});
debug("%#lx +{%#lx, %lld}", this, Address, Count);
return Address;
}
void VirtualMemoryArea::FreePages(void *Address, size_t Count)
{
func("%#lx, %lld", Address, Count);
SmartLock(MgrLock);
forItr(itr, AllocatedPagesList)
{
if (itr->Address != Address)
continue;
if (itr->Protected)
{
error("Address %#lx is protected", Address);
return;
}
/** TODO: Advanced checks. Allow if the page count is less than the requested one.
* This will allow the user to free only a part of the allocated pages.
*
* But this will be in a separate function because we need to specify if we
* want to free from the start or from the end and return the new address.
*/
if (itr->PageCount != Count)
{
error("Page count mismatch! (Allocated: %lld, Requested: %lld)",
itr->PageCount, Count);
return;
}
Virtual vmm(this->Table);
for (size_t i = 0; i < Count; i++)
{
void *AddressToMap = (void *)((uintptr_t)Address + (i * PAGE_SIZE));
vmm.Remap(AddressToMap, AddressToMap, PTFlag::RW);
}
KernelAllocator.FreePages(Address, Count);
AllocatedPagesList.erase(itr);
debug("%#lx -{%#lx, %lld}", this, Address, Count);
return;
}
}
void VirtualMemoryArea::DetachAddress(void *Address)
{
func("%#lx", Address);
SmartLock(MgrLock);
forItr(itr, AllocatedPagesList)
{
if (itr->Address == Address)
{
if (itr->Protected)
{
error("Address %#lx is protected", Address);
return;
}
AllocatedPagesList.erase(itr);
return;
}
}
}
void *VirtualMemoryArea::CreateCoWRegion(void *Address,
size_t Length,
bool Read, bool Write, bool Exec,
bool Fixed, bool Shared)
{
func("%#lx, %lld, %s, %s, %s, %s, %s", Address, Length,
Read ? "true" : "false",
Write ? "true" : "false",
Exec ? "true" : "false",
Fixed ? "true" : "false",
Shared ? "true" : "false");
Virtual vmm(this->Table);
// FIXME
// for (uintptr_t j = uintptr_t(Address);
// j < uintptr_t(Address) + Length;
// j += PAGE_SIZE)
// {
// if (vmm.Check((void *)j, G))
// {
// if (Fixed)
// return (void *)-EINVAL;
// Address = (void *)(j + PAGE_SIZE);
// }
// }
bool AnyAddress = Address == nullptr;
debug("AnyAddress: %s", AnyAddress ? "true" : "false");
if (AnyAddress)
{
Address = this->RequestPages(TO_PAGES(Length), true);
debug("Allocated %#lx-%#lx for pt %#lx",
Address, (uintptr_t)Address + Length, this->Table);
return Address;
}
SmartLock(MgrLock);
if (vmm.Check(Address, PTFlag::KRsv))
{
error("Cannot create CoW region at %#lx", Address);
return (void *)-EPERM;
}
debug("unmapping %#lx-%#lx", Address, (uintptr_t)Address + Length);
vmm.Unmap(Address, Length);
debug("mapping cow at %#lx-%#lx", Address, (uintptr_t)Address + Length);
vmm.Map(Address, nullptr, Length, PTFlag::CoW);
debug("CoW region created at range %#lx-%#lx for pt %#lx",
Address, (uintptr_t)Address + Length, this->Table);
SharedRegion sr{
.Address = Address,
.Read = Read,
.Write = Write,
.Exec = Exec,
.Fixed = Fixed,
.Shared = Shared,
.Length = Length,
.ReferenceCount = 0,
};
SharedRegions.push_back(sr);
debug("CoW region created at %#lx for pt %#lx",
Address, this->Table);
return Address;
}
bool VirtualMemoryArea::HandleCoW(uintptr_t PFA)
{
func("%#lx", PFA);
Virtual vmm(this->Table);
PageTableEntry *pte = vmm.GetPTE((void *)PFA);
debug("ctx: %#lx", this);
if (!pte)
{
/* Unmapped page */
debug("PTE is null!");
return false;
}
if (!pte->CopyOnWrite)
{
debug("PFA %#lx is not CoW (pt %#lx) (flags %#lx)",
PFA, this->Table, pte->raw);
return false;
}
foreach (auto sr in SharedRegions)
{
uintptr_t Start = (uintptr_t)sr.Address;
uintptr_t End = (uintptr_t)sr.Address + sr.Length;
debug("Start: %#lx, End: %#lx (PFA: %#lx)",
Start, End, PFA);
if (PFA >= Start && PFA < End)
{
if (sr.Shared)
{
fixme("Shared CoW");
return false;
}
void *pAddr = this->RequestPages(1);
if (pAddr == nullptr)
return false;
memset(pAddr, 0, PAGE_SIZE);
assert(pte->Present == true);
pte->ReadWrite = sr.Write;
pte->UserSupervisor = sr.Read;
pte->ExecuteDisable = sr.Exec;
pte->CopyOnWrite = false;
debug("PFA %#lx is CoW (pt %#lx, flags %#lx)",
PFA, this->Table, pte->raw);
#if defined(a64)
CPU::x64::invlpg((void *)PFA);
#elif defined(a32)
CPU::x32::invlpg((void *)PFA);
#endif
return true;
}
}
debug("%#lx not found in CoW regions", PFA);
return false;
}
void VirtualMemoryArea::FreeAllPages()
{
SmartLock(MgrLock);
foreach (auto ap in AllocatedPagesList)
{
KernelAllocator.FreePages(ap.Address, ap.PageCount);
Virtual vmm(this->Table);
for (size_t i = 0; i < ap.PageCount; i++)
vmm.Remap((void *)((uintptr_t)ap.Address + (i * PAGE_SIZE)),
(void *)((uintptr_t)ap.Address + (i * PAGE_SIZE)),
PTFlag::RW);
}
AllocatedPagesList.clear();
}
void VirtualMemoryArea::Fork(VirtualMemoryArea *Parent)
{
func("%#lx", Parent);
assert(Parent);
debug("parent apl:%d sr:%d [P:%#lx C:%#lx]",
Parent->AllocatedPagesList.size(), Parent->SharedRegions.size(),
Parent->Table, this->Table);
debug("ctx: this: %#lx parent: %#lx", this, Parent);
Virtual vmm(this->Table);
SmartLock(MgrLock);
foreach (auto &ap in Parent->AllocatedPagesList)
{
if (ap.Protected)
{
debug("Protected %#lx-%#lx", ap.Address,
(uintptr_t)ap.Address + (ap.PageCount * PAGE_SIZE));
continue; /* We don't want to modify these pages. */
}
MgrLock.Unlock();
void *Address = this->RequestPages(ap.PageCount);
MgrLock.Lock(__FUNCTION__);
if (Address == nullptr)
return;
memcpy(Address, ap.Address, FROM_PAGES(ap.PageCount));
for (size_t i = 0; i < ap.PageCount; i++)
{
void *AddressToMap = (void *)((uintptr_t)ap.Address + (i * PAGE_SIZE));
void *RealAddress = (void *)((uintptr_t)Address + (i * PAGE_SIZE));
#if defined(a86)
PageTableEntry *pte = vmm.GetPTE(AddressToMap);
uintptr_t Flags = 0;
Flags |= pte->Present ? (uintptr_t)PTFlag::P : 0;
Flags |= pte->ReadWrite ? (uintptr_t)PTFlag::RW : 0;
Flags |= pte->UserSupervisor ? (uintptr_t)PTFlag::US : 0;
Flags |= pte->CopyOnWrite ? (uintptr_t)PTFlag::CoW : 0;
Flags |= pte->KernelReserve ? (uintptr_t)PTFlag::KRsv : 0;
debug("Mapping %#lx to %#lx (flags %s/%s/%s/%s/%s)",
RealAddress, AddressToMap,
Flags & PTFlag::P ? "P" : "-",
Flags & PTFlag::RW ? "RW" : "-",
Flags & PTFlag::US ? "US" : "-",
Flags & PTFlag::CoW ? "CoW" : "-",
Flags & PTFlag::KRsv ? "KRsv" : "-");
MgrLock.Unlock();
this->Map(AddressToMap, RealAddress, PAGE_SIZE, Flags);
MgrLock.Lock(__FUNCTION__);
#else
#error "Not implemented"
#endif
}
debug("Forked %#lx-%#lx", ap.Address,
(uintptr_t)ap.Address + (ap.PageCount * PAGE_SIZE));
}
foreach (auto &sr in Parent->SharedRegions)
{
MgrLock.Unlock();
void *Address = this->CreateCoWRegion(sr.Address, sr.Length,
sr.Read, sr.Write, sr.Exec,
sr.Fixed, sr.Shared);
MgrLock.Lock(__FUNCTION__);
if (Address == nullptr)
return;
memcpy(Address, sr.Address, sr.Length);
debug("Forked CoW region %#lx-%#lx", sr.Address,
(uintptr_t)sr.Address + sr.Length);
}
}
int VirtualMemoryArea::Map(void *VirtualAddress, void *PhysicalAddress,
size_t Length, uint64_t Flags)
{
Virtual vmm(this->Table);
SmartLock(MgrLock);
uintptr_t intVirtualAddress = (uintptr_t)VirtualAddress;
uintptr_t intPhysicalAddress = (uintptr_t)PhysicalAddress;
for (uintptr_t va = intVirtualAddress;
va < intVirtualAddress + Length; va += PAGE_SIZE)
{
if (vmm.Check(VirtualAddress, PTFlag::KRsv))
{
error("Virtual address %#lx is reserved", VirtualAddress);
return -EPERM;
}
}
for (uintptr_t va = intPhysicalAddress;
va < intPhysicalAddress + Length; va += PAGE_SIZE)
{
if (vmm.Check(PhysicalAddress, PTFlag::KRsv))
{
error("Physical address %#lx is reserved", PhysicalAddress);
return -EPERM;
}
}
vmm.Map(VirtualAddress, PhysicalAddress, Length, Flags);
debug("Mapped %#lx-%#lx to %#lx-%#lx (flags %#lx)",
VirtualAddress, intVirtualAddress + Length,
PhysicalAddress, intPhysicalAddress + Length,
Flags);
return 0;
}
int VirtualMemoryArea::Remap(void *VirtualAddress, void *PhysicalAddress, uint64_t Flags)
{
Virtual vmm(this->Table);
SmartLock(MgrLock);
if (vmm.Check(VirtualAddress, PTFlag::KRsv))
{
error("Virtual address %#lx is reserved", VirtualAddress);
return -EPERM;
}
if (vmm.Check(PhysicalAddress, PTFlag::KRsv))
{
error("Physical address %#lx is reserved", PhysicalAddress);
return -EPERM;
}
vmm.Remap(VirtualAddress, PhysicalAddress, Flags);
debug("Remapped %#lx to %#lx (flags %#lx)",
VirtualAddress, PhysicalAddress, Flags);
return 0;
}
int VirtualMemoryArea::Unmap(void *VirtualAddress, size_t Length)
{
Virtual vmm(this->Table);
SmartLock(MgrLock);
uintptr_t intVirtualAddress = (uintptr_t)VirtualAddress;
for (uintptr_t va = intVirtualAddress;
va < intVirtualAddress + Length; va += PAGE_SIZE)
{
if (vmm.Check(VirtualAddress, PTFlag::KRsv))
{
error("Virtual address %#lx is reserved", VirtualAddress);
return -EPERM;
}
}
vmm.Unmap(VirtualAddress, Length);
debug("Unmapped %#lx-%#lx", VirtualAddress, intVirtualAddress + Length);
return 0;
}
void *VirtualMemoryArea::__UserCheckAndGetAddress(void *Address, size_t Length)
{
Virtual vmm(this->Table);
SmartLock(MgrLock);
void *pAddress = this->Table->Get(Address);
if (pAddress == nullptr)
{
debug("Virtual address %#lx returns nullptr", Address);
return nullptr;
}
uintptr_t intAddress = (uintptr_t)Address;
intAddress = ALIGN_DOWN(intAddress, PAGE_SIZE);
for (uintptr_t va = intAddress; va < intAddress + Length; va += PAGE_SIZE)
{
if (vmm.Check((void *)va, PTFlag::US))
continue;
debug("Unable to get address %#lx, page is not user accessible", va);
return nullptr;
}
return pAddress;
}
int VirtualMemoryArea::__UserCheck(void *Address, size_t Length)
{
Virtual vmm(this->Table);
SmartLock(MgrLock);
if (vmm.Check(Address, PTFlag::US))
return 0;
debug("Address %#lx is not user accessible", Address);
return -EFAULT;
}
VirtualMemoryArea::VirtualMemoryArea(PageTable *_Table)
: Table(_Table)
{
SmartLock(MgrLock);
if (_Table == nullptr)
{
if (TaskManager)
{
Tasking::PCB *pcb = thisProcess;
assert(pcb);
this->Table = thisProcess->PageTable;
}
else
this->Table = (PageTable *)CPU::PageTable();
}
}
VirtualMemoryArea::~VirtualMemoryArea()
{
/* No need to remap pages, the page table will be destroyed */
SmartLock(MgrLock);
foreach (auto ap in AllocatedPagesList)
KernelAllocator.FreePages(ap.Address, ap.PageCount);
}
}

View File

@ -0,0 +1,44 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <memory.hpp>
#include <convert.h>
#include <debug.h>
#include "../../kernel.h"
namespace Memory
{
Virtual::Virtual(PageTable *Table)
{
if (Table)
this->pTable = Table;
else
this->pTable = thisPageTable;
// debug("+ %#lx (PT: %#lx) %s", this, this->pTable,
// KernelSymbolTable
// ? KernelSymbolTable->GetSymbol((uintptr_t)__builtin_return_address(0))
// : "Unknown");
}
Virtual::~Virtual()
{
// debug("- %#lx", this);
}
}

150
Kernel/core/panic/diag.cpp Normal file
View File

@ -0,0 +1,150 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <display.hpp>
#include <bitmap.hpp>
#include <convert.h>
#include <printf.h>
#include <lock.hpp>
#include <rand.hpp>
#include <uart.hpp>
#include <time.hpp>
#include <debug.h>
#include <smp.hpp>
#include <cpu.hpp>
#include <io.h>
#if defined(a64)
#include "../../arch/amd64/cpu/gdt.hpp"
#include "../arch/amd64/cpu/apic.hpp"
#elif defined(a32)
#include "../../arch/i386/cpu/gdt.hpp"
#include "../arch/i386/cpu/apic.hpp"
#elif defined(aa64)
#endif
#include "../../kernel.h"
#include "kbd/keyboard.hpp"
extern void ExPrint(const char *Format, ...);
extern void DisplayTopOverlay();
extern CPU::ExceptionFrame *ExFrame;
/* TODO: Add more info */
struct DiagnosticFile
{
__packed __aligned(16) struct
{
uint8_t Signature[5] = {'D', 'I', 'A', 'G', '\0'};
uint32_t Version = 1;
char Is64Bit = 0;
uint32_t Length = 0;
} Header;
__packed __aligned(16) struct
{
CPU::ExceptionFrame Frame;
uint32_t KernelMemoryLength;
uint8_t KernelMemory[];
} Data;
};
nsa bool WriteDiagDataToNode(FileNode *node)
{
uintptr_t KStart = (uintptr_t)&_kernel_start;
uintptr_t kEnd = (uintptr_t)&_kernel_end;
size_t kSize = kEnd - KStart;
size_t fileSize = sizeof(DiagnosticFile) + kSize;
uint8_t *buf = (uint8_t *)KernelAllocator.RequestPages(TO_PAGES(fileSize));
if (!buf)
{
ExPrint("\x1b[0;30;41mFailed to allocate memory for diagnostic data\x1b[0m\n");
Display->UpdateBuffer();
return false;
}
DiagnosticFile *file = (DiagnosticFile *)buf;
file->Header = {};
file->Header.Length = uint32_t(fileSize);
file->Header.Is64Bit = sizeof(void *) == 8;
file->Data.Frame = *ExFrame;
file->Data.KernelMemoryLength = uint32_t(kSize);
memcpy(file->Data.KernelMemory, (void *)KStart, kSize);
ExPrint("Writing to %s\n", node->Path.c_str());
size_t w = node->Write(buf, fileSize, 0);
if (w != fileSize)
{
debug("%d out of %d bytes written", w, fileSize);
ExPrint("\x1b[0;30;41mFailed to write diagnostic data to file: %s\x1b[0m\n",
strerror((int)w));
Display->UpdateBuffer();
return false;
}
return true;
}
nsa void DiagnosticDataCollection()
{
Display->ClearBuffer();
DisplayTopOverlay();
ExPrint("\nPlease wait while we collect some diagnostic information...\n");
ExPrint("This may take a while...\n");
mode_t mode = S_IRWXU |
S_IRWXG |
S_IROTH |
S_IFDIR;
FileNode *panicDir = fs->ForceCreate(nullptr, "/var/panic", mode);
if (!panicDir)
{
ExPrint("\x1b[0;30;41mFailed to create /var/panic\x1b[0m\n");
Display->UpdateBuffer();
return;
}
FileNode *dumpFile;
Time::Clock clock = Time::ReadClock();
char filename[64];
for (int i = 0; i < INT32_MAX; i++)
{
sprintf(filename, "dump-%d-%d-%d-%d.dmp",
clock.Year, clock.Month, clock.Day, i);
if (fs->PathExists(filename, panicDir))
continue;
mode = S_IRWXU |
S_IRWXG |
S_IROTH |
S_IFREG;
dumpFile = fs->Create(panicDir, filename, mode);
break;
}
if (!WriteDiagDataToNode(dumpFile))
return;
ExPrint("You can find the diagnostic file in /var/panic/%s\n", filename);
Display->UpdateBuffer();
}

View File

@ -0,0 +1,403 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <display.hpp>
#include <bitmap.hpp>
#include <convert.h>
#include <printf.h>
#include <lock.hpp>
#include <rand.hpp>
#include <uart.hpp>
#include <kcon.hpp>
#include <debug.h>
#include <smp.hpp>
#include <cpu.hpp>
#include <io.h>
#if defined(a64)
#include "../../arch/amd64/cpu/gdt.hpp"
#include "../arch/amd64/cpu/apic.hpp"
#elif defined(a32)
#include "../../arch/i386/cpu/gdt.hpp"
#include "../arch/i386/cpu/apic.hpp"
#elif defined(aa64)
#endif
#include "../../kernel.h"
using namespace KernelConsole;
extern const char *x86ExceptionMnemonics[];
extern void DisplayCrashScreen(CPU::ExceptionFrame *);
extern bool UserModeExceptionHandler(CPU::ExceptionFrame *);
extern void DisplayStackSmashing();
extern void DisplayBufferOverflow();
extern void DisplayAssertionFailed(const char *, int, const char *);
void *FbBeforePanic = nullptr;
size_t FbPagesBeforePanic = 0;
FontRenderer CrashFontRenderer;
int ExTermColors[] = {
[TerminalColor::BLACK] = 0x000000,
[TerminalColor::RED] = 0xAA0000,
[TerminalColor::GREEN] = 0x00AA00,
[TerminalColor::YELLOW] = 0xAA5500,
[TerminalColor::BLUE] = 0x0000AA,
[TerminalColor::MAGENTA] = 0xAA00AA,
[TerminalColor::CYAN] = 0x00AAAA,
[TerminalColor::GREY] = 0xAAAAAA,
};
int ExTermBrightColors[] = {
[TerminalColor::BLACK] = 0x858585,
[TerminalColor::RED] = 0xFF5555,
[TerminalColor::GREEN] = 0x55FF55,
[TerminalColor::YELLOW] = 0xFFFF55,
[TerminalColor::BLUE] = 0x5555FF,
[TerminalColor::MAGENTA] = 0xFF55FF,
[TerminalColor::CYAN] = 0x55FFFF,
[TerminalColor::GREY] = 0xFFFFFF,
};
void paint_callback(TerminalCell *cell, long x, long y)
{
if (cell->attr.Bright)
CrashFontRenderer.Paint(x, y, cell->c, ExTermBrightColors[cell->attr.Foreground], ExTermColors[cell->attr.Background]);
else
CrashFontRenderer.Paint(x, y, cell->c, ExTermColors[cell->attr.Foreground], ExTermColors[cell->attr.Background]);
}
nsa void __printfWrapper(char c, void *)
{
KernelConsole::Terminals[15]->Process(c);
}
nsa void ExPrint(const char *Format, ...)
{
va_list args;
va_start(args, Format);
vfctprintf(__printfWrapper, NULL, Format, args);
va_end(args);
}
nsa void HaltAllCores()
{
if (SMP::CPUCores <= 1)
return;
#if defined(a86)
if (Interrupts::apic[0] == nullptr)
return;
APIC::InterruptCommandRegister icr{};
bool x2APIC = ((APIC::APIC *)Interrupts::apic[0])->x2APIC;
if (likely(x2APIC))
{
icr.x2.VEC = s_cst(uint8_t, CPU::x86::IRQ31);
icr.x2.MT = APIC::Fixed;
icr.x2.L = APIC::Assert;
for (int i = 1; i < SMP::CPUCores; i++)
{
icr.x2.DES = uint8_t(i);
((APIC::APIC *)Interrupts::apic[i])->ICR(icr);
}
}
else
{
icr.VEC = s_cst(uint8_t, CPU::x86::IRQ31);
icr.MT = APIC::Fixed;
icr.L = APIC::Assert;
for (int i = 1; i < SMP::CPUCores; i++)
{
icr.DES = uint8_t(i);
((APIC::APIC *)Interrupts::apic[i])->ICR(icr);
}
}
#elif defined(aa64)
#endif
}
nsa void InitFont()
{
/* Hope we won't crash here */
if (Display == nullptr)
{
error("Can't initialize font without display initalized");
CPU::Stop();
}
if (FbBeforePanic == nullptr)
{
FbPagesBeforePanic = TO_PAGES(Display->GetSize);
FbBeforePanic = KernelAllocator.RequestPages(FbPagesBeforePanic);
debug("Created before panic framebuffer at %#lx", FbBeforePanic);
}
memcpy(FbBeforePanic, Display->GetBuffer, Display->GetSize);
if (CrashFontRenderer.CurrentFont == nullptr)
CrashFontRenderer.CurrentFont = new Video::Font(&_binary_files_tamsyn_font_1_11_Tamsyn8x16b_psf_start,
&_binary_files_tamsyn_font_1_11_Tamsyn8x16b_psf_end,
Video::FontType::PCScreenFont2);
if (Terminals[15] == nullptr)
{
size_t Cols = Display->GetWidth / CrashFontRenderer.CurrentFont->GetInfo().Width;
size_t Rows = Display->GetHeight / CrashFontRenderer.CurrentFont->GetInfo().Height;
Terminals[15] = new VirtualTerminal(Cols, Rows, Display->GetWidth, Display->GetHeight, paint_callback, nullptr);
}
}
std::atomic<bool> UnrecoverableLock = false;
nsa __noreturn void HandleUnrecoverableException(CPU::ExceptionFrame *Frame)
{
static int setOnce = 0;
if (!setOnce++) /* FIXME: SMP */
{
for (uint32_t x = 0; x < Display->GetWidth; x++)
for (uint32_t y = 0; y < Display->GetHeight; y++)
Display->SetPixel(x, y, 0xFF250500);
ExPrint("\x1b[H");
}
CPUData *core = GetCurrentCPU();
while (UnrecoverableLock.exchange(true, std::memory_order_acquire))
CPU::Pause();
ExPrint("\x1b[0m-----------------------------------------------\n");
ExPrint("\x1b[30;41mUnrecoverable exception %#lx on CPU %d\n",
Frame->InterruptNumber, core->ID);
#if defined(a86)
ExPrint("CR0=%#lx CR2=%#lx CR3=%#lx CR4=%#lx CR8=%#lx\n",
Frame->cr0, Frame->cr2, Frame->cr3, Frame->cr4, Frame->cr8);
ExPrint("DR0=%#lx DR1=%#lx DR2=%#lx DR3=%#lx DR6=%#lx DR7=%#lx\n",
Frame->dr0, Frame->dr1, Frame->dr2, Frame->dr3, Frame->dr6, Frame->dr7);
ExPrint("GS=%#lx FS=%#lx ES=%#lx DS=%#lx SS=%#lx CS=%#lx\n",
Frame->gs, Frame->fs, Frame->es, Frame->ds, Frame->ss, Frame->cs);
#endif
#if defined(a64)
ExPrint("R8=%#lx R9=%#lx R10=%#lx R11=%#lx R12=%#lx R13=%#lx R14=%#lx R15=%#lx\n",
Frame->r8, Frame->r9, Frame->r10, Frame->r11, Frame->r12, Frame->r13,
Frame->r14, Frame->r15);
#endif
#if defined(a86)
ExPrint("AX=%#lx BX=%#lx CX=%#lx DX=%#lx SI=%#lx DI=%#lx BP=%#lx SP=%#lx\n",
#ifdef a64
Frame->rax, Frame->rbx, Frame->rcx, Frame->rdx, Frame->rsi, Frame->rdi,
Frame->rbp, Frame->rsp);
#else
Frame->eax, Frame->ebx, Frame->ecx, Frame->edx, Frame->esi, Frame->edi,
Frame->ebp, Frame->esp);
#endif /* a64 */
ExPrint("IP=%#lx FL=%#lx INT=%#lx ERR=%#lx\n",
#ifdef a64
Frame->rip, Frame->rflags.raw,
#else
Frame->eip, Frame->eflags.raw,
#endif /* a64 */
Frame->InterruptNumber, Frame->ErrorCode);
#endif /* a86 */
Display->UpdateBuffer();
error("Unrecoverable Exception: %#lx", Frame->InterruptNumber);
UnrecoverableLock.store(false, std::memory_order_release);
CPU::Stop();
}
nsa __noreturn void HandleExceptionInsideException(CPU::ExceptionFrame *Frame)
{
ExPrint("\x1b[0m-----------------------------------------------\n");
ExPrint("Exception inside exception: %#lx at %#lx\n",
Frame->InterruptNumber,
#if defined(a64)
Frame->rip);
#elif defined(a32)
Frame->eip);
#elif defined(aa64)
Frame->pc);
#endif
#if defined(a86)
ExPrint("CR0=%#lx CR2=%#lx CR3=%#lx CR4=%#lx CR8=%#lx\n",
Frame->cr0, Frame->cr2, Frame->cr3, Frame->cr4, Frame->cr8);
ExPrint("DR0=%#lx DR1=%#lx DR2=%#lx DR3=%#lx DR6=%#lx DR7=%#lx\n",
Frame->dr0, Frame->dr1, Frame->dr2, Frame->dr3, Frame->dr6, Frame->dr7);
ExPrint("GS=%#lx FS=%#lx ES=%#lx DS=%#lx SS=%#lx CS=%#lx\n",
Frame->gs, Frame->fs, Frame->es, Frame->ds, Frame->ss, Frame->cs);
#endif
#if defined(a64)
ExPrint("R8=%#lx R9=%#lx R10=%#lx R11=%#lx R12=%#lx R13=%#lx R14=%#lx R15=%#lx\n",
Frame->r8, Frame->r9, Frame->r10, Frame->r11, Frame->r12, Frame->r13,
Frame->r14, Frame->r15);
#endif
#if defined(a86)
ExPrint("AX=%#lx BX=%#lx CX=%#lx DX=%#lx SI=%#lx DI=%#lx BP=%#lx SP=%#lx\n",
#ifdef a64
Frame->rax, Frame->rbx, Frame->rcx, Frame->rdx, Frame->rsi, Frame->rdi,
Frame->rbp, Frame->rsp);
#else
Frame->eax, Frame->ebx, Frame->ecx, Frame->edx, Frame->esi, Frame->edi,
Frame->ebp, Frame->esp);
#endif /* a64 */
ExPrint("IP=%#lx FL=%#lx INT=%#lx ERR=%#lx\n",
#ifdef a64
Frame->rip, Frame->rflags.raw,
#else
Frame->eip, Frame->eflags.raw,
#endif /* a64 */
Frame->InterruptNumber, Frame->ErrorCode);
#endif /* a86 */
Display->UpdateBuffer();
CPU::Stop();
}
std::atomic<bool> ExceptionLock = false;
nsa void HandleException(CPU::ExceptionFrame *Frame)
{
/* We don't need to restore the page table
because the ExceptionHandlerStub will
do it for us if we return. */
CPU::PageTable(KernelPageTable);
InitFont();
/* First check the exception */
if (unlikely(Frame->InterruptNumber == CPU::x86::DoubleFault))
{
ExceptionLock.store(true, std::memory_order_release);
HandleUnrecoverableException(Frame);
}
if (Frame->cs == GDT_USER_CODE && Frame->ss == GDT_USER_DATA)
{
if (UserModeExceptionHandler(Frame))
goto ExceptionExit;
CPUData *core = GetCurrentCPU();
if (likely(core->CurrentThread->Security.IsCritical == false))
TaskManager->Yield();
error("Critical thread \"%s\"(%d) died",
core->CurrentThread->Name,
core->CurrentThread->ID);
}
debug("-----------------------------------------------------------------------------------");
error("Exception: %#x", Frame->InterruptNumber);
debug("%ld MiB / %ld MiB (%ld MiB Reserved)",
TO_MiB(KernelAllocator.GetUsedMemory()),
TO_MiB(KernelAllocator.GetTotalMemory()),
TO_MiB(KernelAllocator.GetReservedMemory()));
if (ExceptionLock.load(std::memory_order_acquire))
HandleExceptionInsideException(Frame);
ExceptionLock.store(true, std::memory_order_release);
{
const char msg[] = "Entering in panic mode...";
size_t msgLen = strlen(msg);
size_t msgPixels = msgLen * CrashFontRenderer.CurrentFont->GetInfo().Width;
uint32_t x = uint32_t((Display->GetWidth - msgPixels) / 2);
uint32_t y = uint32_t((Display->GetHeight - CrashFontRenderer.CurrentFont->GetInfo().Height) / 2);
x /= CrashFontRenderer.CurrentFont->GetInfo().Width;
y /= CrashFontRenderer.CurrentFont->GetInfo().Height;
printf("\x1b[2J\x1b[%d;%dH", y, x);
printf("\x1b[30;41m%s\x1b[0m\x1b[H", msg);
}
if (DriverManager)
DriverManager->Panic();
if (TaskManager)
TaskManager->Panic();
Interrupts::RemoveAll();
HaltAllCores();
ForceUnlock = true;
DisplayCrashScreen(Frame);
CPU::Stop();
ExceptionExit:
ExceptionLock.store(false, std::memory_order_release);
}
nsa void BaseBufferStackError(bool Stack)
{
/* We don't need to restore the page table
because the ExceptionHandlerStub will
do it for us if we return. */
CPU::PageTable(KernelPageTable);
if (CrashFontRenderer.CurrentFont == nullptr)
CrashFontRenderer.CurrentFont = new Video::Font(&_binary_files_tamsyn_font_1_11_Tamsyn8x16b_psf_start,
&_binary_files_tamsyn_font_1_11_Tamsyn8x16b_psf_end,
Video::FontType::PCScreenFont2);
if (Terminals[15] == nullptr)
{
size_t Cols = Display->GetWidth / CrashFontRenderer.CurrentFont->GetInfo().Width;
size_t Rows = Display->GetHeight / CrashFontRenderer.CurrentFont->GetInfo().Height;
Terminals[15] = new VirtualTerminal(Cols, Rows, Display->GetWidth, Display->GetHeight, paint_callback, nullptr);
}
ExceptionLock.store(true, std::memory_order_release);
if (DriverManager)
DriverManager->Panic();
if (TaskManager)
TaskManager->Panic();
HaltAllCores();
ForceUnlock = true;
debug("-----------------------------------------------------------------------------------");
error("%s", Stack ? "Stack smashing detected" : "Buffer overflow detected");
debug("%ld MiB / %ld MiB (%ld MiB Reserved)",
TO_MiB(KernelAllocator.GetUsedMemory()),
TO_MiB(KernelAllocator.GetTotalMemory()),
TO_MiB(KernelAllocator.GetReservedMemory()));
}
nsa __noreturn void HandleStackSmashing()
{
BaseBufferStackError(true);
DisplayStackSmashing();
CPU::Stop();
}
nsa __noreturn void HandleBufferOverflow()
{
BaseBufferStackError(false);
DisplayBufferOverflow();
CPU::Stop();
}
EXTERNC nsa __noreturn void HandleAssertionFailed(const char *File, int Line,
const char *Expression)
{
DisplayAssertionFailed(File, Line, Expression);
CPU::Stop();
}

View File

@ -0,0 +1,94 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include "ehci.hpp"
#include <interface/aip.h>
#include <display.hpp>
#include <convert.h>
#include <printf.h>
#include <kcon.hpp>
#include <debug.h>
#include <smp.hpp>
#include <cpu.hpp>
#include <io.h>
#if defined(a64)
#include "../../../arch/amd64/cpu/gdt.hpp"
#elif defined(a32)
#elif defined(aa64)
#endif
#include "../../../kernel.h"
using namespace KernelConsole;
using namespace PCI;
#define ERROR_COLOR "\x1b[31m"
#define WARN_COLOR "\x1b[33m"
#define DEFAULT_COLOR "\x1b[0m"
extern void ExPrint(const char *Format, ...);
extern void ArrowInput(uint8_t key);
extern void UserInput(char *Input);
extern FontRenderer CrashFontRenderer;
nsa void CrashEHCIKeyboardDriver::OnInterruptReceived(CPU::TrapFrame *Frame)
{
}
nsa bool CrashEHCIKeyboardDriver::Initialize()
{
return false;
}
nsa CrashEHCIKeyboardDriver::CrashEHCIKeyboardDriver(PCI::PCIDevice dev)
: Interrupts::Handler(dev)
{
Header = (PCIDeviceHeader *)dev.Header;
switch (Header->HeaderType)
{
case 128:
{
ExPrint(ERROR_COLOR "Unknown header type %d! Guessing PCI Header 0\n" DEFAULT_COLOR,
Header->HeaderType);
[[fallthrough]];
}
case 0: /* PCI Header 0 */
{
PCI::PCIHeader0 *hdr = (PCI::PCIHeader0 *)Header;
UNUSED(hdr);
break;
}
case 1: /* PCI Header 1 (PCI-to-PCI Bridge) */
{
ExPrint(ERROR_COLOR "PCI-to-PCI Bridge not supported\n" DEFAULT_COLOR);
break;
}
case 2: /* PCI Header 2 (PCI-to-CardBus Bridge) */
{
ExPrint(ERROR_COLOR "PCI-to-CardBus Bridge not supported\n" DEFAULT_COLOR);
break;
}
default:
{
ExPrint(ERROR_COLOR "Invalid PCI header type\n" DEFAULT_COLOR);
break;
}
}
}

View File

@ -0,0 +1,31 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <ints.hpp>
class CrashEHCIKeyboardDriver : public Interrupts::Handler
{
public:
PCI::PCIDeviceHeader *Header = nullptr;
void OnInterruptReceived(CPU::TrapFrame *Frame);
bool Initialize();
CrashEHCIKeyboardDriver(PCI::PCIDevice dev);
~CrashEHCIKeyboardDriver() = default;
};

View File

@ -0,0 +1,101 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include "keyboard.hpp"
#include "ps2.hpp"
#include "uhci.hpp"
#include "ehci.hpp"
#include "xhci.hpp"
#include <interface/aip.h>
#include <display.hpp>
#include <convert.h>
#include <printf.h>
#include <kcon.hpp>
#include <debug.h>
#include <smp.hpp>
#include <cpu.hpp>
#include <io.h>
#if defined(a64)
#include "../../../arch/amd64/cpu/gdt.hpp"
#elif defined(a32)
#elif defined(aa64)
#endif
#include "../../../kernel.h"
using namespace KernelConsole;
using namespace PCI;
nsa bool DetectUSBKeyboard()
{
if (!PCIManager)
return false;
std::list<PCIDevice> uhci = PCIManager->FindPCIDevice(0xC, 0x3, 0x00); /* UHCI */
debug("There are %lu UHCI devices", uhci.size());
std::list<PCIDevice> ohci = PCIManager->FindPCIDevice(0xC, 0x3, 0x10); /* OHCI */
debug("There are %lu OHCI devices", ohci.size());
std::list<PCIDevice> ehci = PCIManager->FindPCIDevice(0xC, 0x3, 0x20); /* EHCI */
debug("There are %lu EHCI devices", uhci.size());
std::list<PCIDevice> xhci = PCIManager->FindPCIDevice(0xC, 0x3, 0x30); /* XHCI */
debug("There are %lu XHCI devices", xhci.size());
debug("Initializing UHCI devices");
for (const auto &dev : uhci)
{
CrashUHCIKeyboardDriver *usb = new CrashUHCIKeyboardDriver(dev);
if (!usb->Initialize())
{
error("Failed to initialize UHCI keyboard driver");
uhci.pop_front();
}
}
debug("Initializing EHCI devices");
for (const auto &dev : ehci)
{
CrashEHCIKeyboardDriver *usb = new CrashEHCIKeyboardDriver(dev);
if (!usb->Initialize())
{
error("Failed to initialize UHCI keyboard driver");
ehci.pop_front();
}
}
debug("Initializing XHCI devices");
for (const auto &dev : xhci)
{
CrashXHCIKeyboardDriver *usb = new CrashXHCIKeyboardDriver(dev);
if (!usb->Initialize())
{
error("Failed to initialize XHCI keyboard driver");
xhci.pop_front();
}
}
return xhci.size() > 0 || uhci.size() > 0 || ohci.size() > 0;
}
nsa void InitializeKeyboards()
{
if (DetectUSBKeyboard() == false)
new CrashPS2KeyboardDriver;
}

View File

@ -0,0 +1,206 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <ints.hpp>
#pragma once
enum Keys
{
KEY_INVALID = 0x0,
KEY_D_ESCAPE = 0x1,
KEY_D_1 = 0x2,
KEY_D_2 = 0x3,
KEY_D_3 = 0x4,
KEY_D_4 = 0x5,
KEY_D_5 = 0x6,
KEY_D_6 = 0x7,
KEY_D_7 = 0x8,
KEY_D_8 = 0x9,
KEY_D_9 = 0xa,
KEY_D_0 = 0xb,
KEY_D_MINUS = 0xc,
KEY_D_EQUALS = 0xd,
KEY_D_BACKSPACE = 0xe,
KEY_D_TAB = 0xf,
KEY_D_Q = 0x10,
KEY_D_W = 0x11,
KEY_D_E = 0x12,
KEY_D_R = 0x13,
KEY_D_T = 0x14,
KEY_D_Y = 0x15,
KEY_D_U = 0x16,
KEY_D_I = 0x17,
KEY_D_O = 0x18,
KEY_D_P = 0x19,
KEY_D_LBRACKET = 0x1a,
KEY_D_RBRACKET = 0x1b,
KEY_D_RETURN = 0x1c,
KEY_D_LCTRL = 0x1d,
KEY_D_A = 0x1e,
KEY_D_S = 0x1f,
KEY_D_D = 0x20,
KEY_D_F = 0x21,
KEY_D_G = 0x22,
KEY_D_H = 0x23,
KEY_D_J = 0x24,
KEY_D_K = 0x25,
KEY_D_L = 0x26,
KEY_D_SEMICOLON = 0x27,
KEY_D_APOSTROPHE = 0x28,
KEY_D_GRAVE = 0x29,
KEY_D_LSHIFT = 0x2a,
KEY_D_BACKSLASH = 0x2b,
KEY_D_Z = 0x2c,
KEY_D_X = 0x2d,
KEY_D_C = 0x2e,
KEY_D_V = 0x2f,
KEY_D_B = 0x30,
KEY_D_N = 0x31,
KEY_D_M = 0x32,
KEY_D_COMMA = 0x33,
KEY_D_PERIOD = 0x34,
KEY_D_SLASH = 0x35,
KEY_D_RSHIFT = 0x36,
KEY_D_PRTSC = 0x37,
KEY_D_LALT = 0x38,
KEY_D_SPACE = 0x39,
KEY_D_CAPSLOCK = 0x3a,
KEY_D_NUMLOCK = 0x45,
KEY_D_SCROLLLOCK = 0x46,
KEY_D_KP_MULTIPLY = 0x37,
KEY_D_KP_7 = 0x47,
KEY_D_KP_8 = 0x48,
KEY_D_KP_9 = 0x49,
KEY_D_KP_MINUS = 0x4a,
KEY_D_KP_4 = 0x4b,
KEY_D_KP_5 = 0x4c,
KEY_D_KP_6 = 0x4d,
KEY_D_KP_PLUS = 0x4e,
KEY_D_KP_1 = 0x4f,
KEY_D_KP_2 = 0x50,
KEY_D_KP_3 = 0x51,
KEY_D_KP_0 = 0x52,
KEY_D_KP_PERIOD = 0x53,
KEY_D_F1 = 0x3b,
KEY_D_F2 = 0x3c,
KEY_D_F3 = 0x3d,
KEY_D_F4 = 0x3e,
KEY_D_F5 = 0x3f,
KEY_D_F6 = 0x40,
KEY_D_F7 = 0x41,
KEY_D_F8 = 0x42,
KEY_D_F9 = 0x43,
KEY_D_F10 = 0x44,
KEY_D_F11 = 0x57,
KEY_D_F12 = 0x58,
KEY_D_UP = 0x48,
KEY_D_LEFT = 0x4b,
KEY_D_RIGHT = 0x4d,
KEY_D_DOWN = 0x50,
KEY_U_ESCAPE = 0x81,
KEY_U_1 = 0x82,
KEY_U_2 = 0x83,
KEY_U_3 = 0x84,
KEY_U_4 = 0x85,
KEY_U_5 = 0x86,
KEY_U_6 = 0x87,
KEY_U_7 = 0x88,
KEY_U_8 = 0x89,
KEY_U_9 = 0x8a,
KEY_U_0 = 0x8b,
KEY_U_MINUS = 0x8c,
KEY_U_EQUALS = 0x8d,
KEY_U_BACKSPACE = 0x8e,
KEY_U_TAB = 0x8f,
KEY_U_Q = 0x90,
KEY_U_W = 0x91,
KEY_U_E = 0x92,
KEY_U_R = 0x93,
KEY_U_T = 0x94,
KEY_U_Y = 0x95,
KEY_U_U = 0x96,
KEY_U_I = 0x97,
KEY_U_O = 0x98,
KEY_U_P = 0x99,
KEY_U_LBRACKET = 0x9a,
KEY_U_RBRACKET = 0x9b,
KEY_U_RETURN = 0x9c,
KEY_U_LCTRL = 0x9d,
KEY_U_A = 0x9e,
KEY_U_S = 0x9f,
KEY_U_D = 0xa0,
KEY_U_F = 0xa1,
KEY_U_G = 0xa2,
KEY_U_H = 0xa3,
KEY_U_J = 0xa4,
KEY_U_K = 0xa5,
KEY_U_L = 0xa6,
KEY_U_SEMICOLON = 0xa7,
KEY_U_APOSTROPHE = 0xa8,
KEY_U_GRAVE = 0xa9,
KEY_U_LSHIFT = 0xaa,
KEY_U_BACKSLASH = 0xab,
KEY_U_Z = 0xac,
KEY_U_X = 0xad,
KEY_U_C = 0xae,
KEY_U_V = 0xaf,
KEY_U_B = 0xb0,
KEY_U_N = 0xb1,
KEY_U_M = 0xb2,
KEY_U_COMMA = 0xb3,
KEY_U_PERIOD = 0xb4,
KEY_U_SLASH = 0xb5,
KEY_U_RSHIFT = 0xb6,
KEY_U_KP_MULTIPLY = 0xb7,
KEY_U_LALT = 0xb8,
KEY_U_SPACE = 0xb9,
KEY_U_CAPSLOCK = 0xba,
KEY_U_F1 = 0xbb,
KEY_U_F2 = 0xbc,
KEY_U_F3 = 0xbd,
KEY_U_F4 = 0xbe,
KEY_U_F5 = 0xbf,
KEY_U_F6 = 0xc0,
KEY_U_F7 = 0xc1,
KEY_U_F8 = 0xc2,
KEY_U_F9 = 0xc3,
KEY_U_F10 = 0xc4,
KEY_U_NUMLOCK = 0xc5,
KEY_U_SCROLLLOCK = 0xc6,
KEY_U_KP_7 = 0xc7,
KEY_U_KP_8 = 0xc8,
KEY_U_KP_9 = 0xc9,
KEY_U_KP_MINUS = 0xca,
KEY_U_KP_4 = 0xcb,
KEY_U_KP_5 = 0xcc,
KEY_U_KP_6 = 0xcd,
KEY_U_KP_PLUS = 0xce,
KEY_U_KP_1 = 0xcf,
KEY_U_KP_2 = 0xd0,
KEY_U_KP_3 = 0xd1,
KEY_U_KP_0 = 0xd2,
KEY_U_KP_PERIOD = 0xd3,
KEY_U_F11 = 0xd7,
KEY_U_F12 = 0xd8,
};
void InitializeKeyboards();

View File

@ -0,0 +1,416 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include "ps2.hpp"
#include "keyboard.hpp"
#include <interface/aip.h>
#include <display.hpp>
#include <convert.h>
#include <printf.h>
#include <kcon.hpp>
#include <debug.h>
#include <smp.hpp>
#include <cpu.hpp>
#include <io.h>
#if defined(a64)
#include "../../../arch/amd64/cpu/gdt.hpp"
#elif defined(a32)
#elif defined(aa64)
#endif
#include "../../../kernel.h"
using namespace KernelConsole;
#define ERROR_COLOR "\x1b[31m"
#define WARN_COLOR "\x1b[33m"
#define DEFAULT_COLOR "\x1b[0m"
extern void ExPrint(const char *Format, ...);
extern void ArrowInput(uint8_t key);
extern void UserInput(char *Input);
extern FontRenderer CrashFontRenderer;
const char sc_ascii_low[] = {'?', '?', '1', '2', '3', '4', '5', '6',
'7', '8', '9', '0', '-', '=', '?', '?', 'q', 'w', 'e', 'r', 't', 'y',
'u', 'i', 'o', 'p', '[', ']', '?', '?', 'a', 's', 'd', 'f', 'g',
'h', 'j', 'k', 'l', ';', '\'', '`', '?', '\\', 'z', 'x', 'c', 'v',
'b', 'n', 'm', ',', '.', '/', '?', '?', '?', ' '};
const char sc_ascii_high[] = {'?', '?', '!', '@', '#', '$', '%', '^',
'&', '*', '(', ')', '_', '+', '?', '?', 'Q', 'W', 'E', 'R', 'T', 'Y',
'U', 'I', 'O', 'P', '{', '}', '?', '?', 'A', 'S', 'D', 'F', 'G',
'H', 'J', 'K', 'L', ';', '\"', '~', '?', '|', 'Z', 'X', 'C', 'V',
'B', 'N', 'M', '<', '>', '?', '?', '?', '?', ' '};
static int LowerCase = true;
nsa static inline int GetLetterFromScanCode(uint8_t ScanCode)
{
if (ScanCode & 0x80)
{
switch (ScanCode)
{
case KEY_U_LSHIFT:
LowerCase = true;
return KEY_INVALID;
case KEY_U_RSHIFT:
LowerCase = true;
return KEY_INVALID;
default:
return KEY_INVALID;
}
}
else
{
switch (ScanCode)
{
case KEY_D_RETURN:
return '\n';
case KEY_D_LSHIFT:
LowerCase = false;
return KEY_INVALID;
case KEY_D_RSHIFT:
LowerCase = false;
return KEY_INVALID;
case KEY_D_BACKSPACE:
return ScanCode;
default:
{
if (ScanCode > 0x39)
break;
if (LowerCase)
return sc_ascii_low[ScanCode];
else
return sc_ascii_high[ScanCode];
}
}
}
return KEY_INVALID;
}
nsa void CrashPS2KeyboardDriver::PS2Wait(bool Output)
{
#if defined(a86)
TimeoutCallNumber++;
int timeout = 100000;
PS2_STATUSES status = {.Raw = inb(PS2_STATUS)};
while (timeout--)
{
if (!Output)
{
if (status.OutputBufferFull == 0)
return;
}
else
{
if (status.InputBufferFull == 0)
return;
}
status.Raw = inb(PS2_STATUS);
}
ExPrint(WARN_COLOR "PS/2 controller timeout (%s;%d)\n" DEFAULT_COLOR,
Output ? "output" : "input", TimeoutCallNumber);
#endif // a86
}
/*
This simple driver relies on the PS/2 controller to handle the keyboard.
Maybe is not the most efficient way but most x86 devices out there
still has PS/2 support (emulated or not).
We even have to make sure IRQ1 is enabled in the PIC but on x64 we use
the I/O APIC... "outb(0x21, 0b11111101);" can be used but the EOI is
automatically sent to I/O APIC if enabled/supported which is bad.
FIXME: On some real devices, the PS/2 keyboard doesn't send interrupts.
*/
nsa CrashPS2KeyboardDriver::CrashPS2KeyboardDriver() : Interrupts::Handler(1) /* IRQ1 */
{
#define WaitRead PS2Wait(true)
#define WaitWrite PS2Wait(false)
#define SetMessageLocation \
ExPrint("\x1b[%d;%dH", (Display->GetWidth / CrashFontRenderer.CurrentFont->GetInfo().Width) - 1, 0);
CPU::Interrupts(CPU::Disable);
/* Dots will be printed at the bottom of the screen as a progress bar. */
ExPrint("\x1b[%d;%dH", (Display->GetWidth / CrashFontRenderer.CurrentFont->GetInfo().Width) - 2, 0);
#if defined(a86)
/* Disable port 1 & 2 */
{
/* Disable Port 1 */
WaitWrite;
outb(PS2_CMD, PS2_CMD_DISABLE_PORT_1);
/* Disable Port 2 */
WaitWrite;
outb(PS2_CMD, PS2_CMD_DISABLE_PORT_2);
}
ExPrint(".");
/* Flush */
{
PS2_STATUSES status;
int timeout = 0x500;
while (timeout--)
{
status.Raw = inb(PS2_STATUS);
if (status.OutputBufferFull == 0)
break;
inb(PS2_DATA);
}
if (timeout <= 0)
{
SetMessageLocation;
ExPrint(ERROR_COLOR
"PS/2 controller timeout (flush;0)\n" DEFAULT_COLOR);
CPU::Stop();
}
}
ExPrint(".");
/* Test controller */
{
/* Save config */
WaitWrite;
outb(PS2_CMD, PS2_CMD_READ_CONFIG);
WaitRead;
PS2_CONFIGURATION cfg = {.Raw = inb(PS2_DATA)};
cfg.Port1Interrupt = 1;
cfg.Port2Interrupt = 1;
cfg.Port1Translation = 1;
/* Update config */
WaitWrite;
outb(PS2_CMD, PS2_DATA);
WaitWrite;
outb(PS2_DATA, cfg.Raw);
/* Test PS/2 controller */
WaitWrite;
outb(PS2_CMD, PS2_CMD_TEST_CONTROLLER);
WaitRead;
uint8_t test = inb(PS2_DATA);
if (test != PS2_TEST_PASSED)
{
if (test == PS2_ACK)
{
trace("PS/2 controller sent ACK to test request.");
WaitRead;
test = inb(PS2_DATA);
}
if (test != PS2_TEST_PASSED)
{
SetMessageLocation;
ExPrint(ERROR_COLOR
"PS/2 controller self test failed (%#x)\n" DEFAULT_COLOR,
test);
CPU::Stop();
}
}
/* Restore config */
WaitWrite;
outb(PS2_CMD, PS2_DATA);
WaitWrite;
outb(PS2_DATA, cfg.Raw);
}
ExPrint(".");
/* Disable scanning; Enable port 1; Set default settings */
{
/* Disable scanning */
outb(PS2_DATA, PS2_KBD_CMD_DISABLE_SCANNING);
/* Enable Port 1 */
WaitWrite;
outb(PS2_CMD, PS2_CMD_ENABLE_PORT_1);
/* Set default settings */
outb(PS2_DATA, PS2_KBD_CMD_DEFAULTS);
}
ExPrint(".");
/* Test port 1 */
{
WaitWrite;
outb(PS2_CMD, PS2_CMD_TEST_PORT_1);
WaitRead;
uint8_t test = inb(PS2_DATA);
if (test != 0x00)
{
if (test == PS2_KBD_RESP_ACK)
{
trace("PS/2 keyboard sent ACK to test request.");
WaitRead;
test = inb(PS2_DATA);
}
if (test != 0x00)
{
SetMessageLocation;
ExPrint(ERROR_COLOR
"PS/2 keyboard self test failed (%#x)\n" DEFAULT_COLOR,
test);
CPU::Stop();
}
}
}
ExPrint(".");
/* Configure the controller */
{
// /* Read Controller Configuration */
// WaitWrite;
// outb(PS2_CMD, PS2_CMD_READ_CONFIG);
// WaitRead;
// uint8_t cfg = inb(PS2_DATA);
// /* Enable Port 1 & Port 1 translation */
// cfg |= 0b01000001;
// /* Write Controller Configuration */
// WaitWrite;
// outb(PS2_CMD, PS2_CMD_WRITE_CONFIG);
// WaitWrite;
// outb(PS2_DATA, cfg);
}
ExPrint(".");
/* Enable port 1; Set scan code; Enable scanning */
{
/* Enable Port 1 */
outb(PS2_CMD, PS2_CMD_ENABLE_PORT_1);
/* Set scan code set 1 */
WaitWrite;
outb(PS2_DATA, PS2_KBD_CMD_SCAN_CODE_SET);
WaitWrite;
outb(PS2_DATA, PS2_KBD_SCAN_CODE_SET_2);
/* Check if we have scan code set 1 */
WaitWrite;
outb(PS2_DATA, PS2_KBD_CMD_SCAN_CODE_SET);
WaitWrite;
outb(PS2_DATA, PS2_KBD_SCAN_CODE_GET_CURRENT);
/* Read scan code set */
WaitRead;
uint8_t scs = inb(PS2_DATA);
if (scs == PS2_KBD_RESP_ACK || scs == PS2_KBD_RESP_RESEND)
{
if (scs == PS2_KBD_RESP_ACK)
trace("PS/2 keyboard sent ACK to scan code set request.");
if (scs == PS2_KBD_RESP_RESEND)
trace("PS/2 keyboard sent RESEND to scan code set request.");
WaitRead;
scs = inb(PS2_DATA);
}
if (scs != PS2_KBD_SC_SET_2)
{
SetMessageLocation;
ExPrint(WARN_COLOR
"PS/2 keyboard scan code set 1 not supported (%#x)\n" DEFAULT_COLOR,
scs);
}
/* Enable scanning */
outb(PS2_DATA, PS2_KBD_CMD_ENABLE_SCANNING);
}
#ifdef DEBUG
WaitWrite;
outb(PS2_CMD, PS2_CMD_READ_CONFIG);
WaitRead;
PS2_CONFIGURATION cfg = {.Raw = inb(PS2_DATA)};
debug("PS2 CONFIG:\nPort1int: %d\nPort2int: %d\nSysFlg: %d\nZ: %d\nP1clk: %d\nP2clk: %d\nP1trans: %d\nz: %d",
cfg.Port1Interrupt, cfg.Port2Interrupt, cfg.SystemFlag, cfg.Zero0, cfg.Port1Clock, cfg.Port2Clock, cfg.Port1Translation, cfg.Zero1);
#endif
ExPrint(".");
#endif // defined(a86)
CPU::Interrupts(CPU::Enable);
}
nsa void CrashPS2KeyboardDriver::OnInterruptReceived(CPU::TrapFrame *)
{
#if defined(a86)
uint8_t scanCode = inb(PS2_DATA);
if (scanCode == KEY_D_TAB ||
scanCode == KEY_D_LCTRL ||
scanCode == KEY_D_LALT ||
scanCode == KEY_U_LCTRL ||
scanCode == KEY_U_LALT)
return;
switch (scanCode)
{
case KEY_D_UP:
case KEY_D_LEFT:
case KEY_D_RIGHT:
case KEY_D_DOWN:
ArrowInput(scanCode);
break;
default:
break;
}
int key = GetLetterFromScanCode(scanCode);
if (key != KEY_INVALID)
{
if (key == KEY_D_BACKSPACE)
{
if (BackSpaceLimit > 0)
{
char keyBuf[5] = {'\b', '\x1b', '[', 'K', '\0'};
ExPrint(keyBuf);
backspace(UserInputBuffer);
BackSpaceLimit--;
}
}
else if (key == '\n')
{
UserInput(UserInputBuffer);
BackSpaceLimit = 0;
UserInputBuffer[0] = '\0';
}
else
{
append(UserInputBuffer, s_cst(char, key));
char keyBuf[2] = {(char)key, '\0'};
ExPrint(keyBuf);
BackSpaceLimit++;
}
Display->UpdateBuffer(); /* Update as we type. */
}
#endif // a64 || a32
}

View File

@ -0,0 +1,35 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <ints.hpp>
class CrashPS2KeyboardDriver : public Interrupts::Handler
{
private:
void PS2Wait(bool Read);
void OnInterruptReceived(CPU::TrapFrame *Frame);
int BackSpaceLimit = 0;
char UserInputBuffer[256];
int TimeoutCallNumber = 0;
public:
CrashPS2KeyboardDriver();
~CrashPS2KeyboardDriver() = default;
};

View File

@ -0,0 +1,178 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include "uhci.hpp"
#include <interface/aip.h>
#include <display.hpp>
#include <convert.h>
#include <printf.h>
#include <kcon.hpp>
#include <debug.h>
#include <smp.hpp>
#include <cpu.hpp>
#include <io.h>
#if defined(a64)
#include "../../../arch/amd64/cpu/gdt.hpp"
#elif defined(a32)
#elif defined(aa64)
#endif
#include "../../../kernel.h"
using namespace KernelConsole;
using namespace PCI;
#define ERROR_COLOR "\x1b[31m"
#define WARN_COLOR "\x1b[33m"
#define DEFAULT_COLOR "\x1b[0m"
extern void ExPrint(const char *Format, ...);
extern void ArrowInput(uint8_t key);
extern void UserInput(char *Input);
extern FontRenderer CrashFontRenderer;
nsa void CrashUHCIKeyboardDriver::OnInterruptReceived(CPU::TrapFrame *Frame)
{
}
__no_sanitize("undefined") nsa bool CrashUHCIKeyboardDriver::Initialize()
{
return false; /* FIXME: stub */
debug("Allocating resources");
this->FrameList = (uint32_t *)(uintptr_t)KernelAllocator.RequestPages(TO_PAGES(1024 * sizeof(FrameList[0])));
this->td = (TD *)KernelAllocator.RequestPages(TO_PAGES(sizeof(TD) * 32));
memset(td, 0, sizeof(TD) * 32);
this->qh = (QH *)KernelAllocator.RequestPages(TO_PAGES(sizeof(QH) * 8));
memset(qh, 0, sizeof(QH) * 8);
/* FIXME: stub */
debug("Initializing controller");
outw((uint16_t)((uintptr_t)io + 0xC0), 0x8F00); /* Disable Legacy Mode Support */
/* Disable All Interrupts */
io->USBINTR.TOCRC = 0;
io->USBINTR.RIE = 0;
io->USBINTR.IOCE = 0;
io->USBINTR.SPIE = 0;
/* Configure Frame List */
io->FRNUM.FN = 0;
io->FRBASEADD.BA = (uint32_t)(uintptr_t)FrameList;
io->SOFMOD.SOFTVAL = 0b1000000;
io->USBSTS.raw = 0xFFFF; /* Clear USBSTS */
if (io->USBSTS.HCH)
io->USBCMD.RS = 1;
else
{
debug("Controller not halted, what to do?");
}
return true;
}
nsa CrashUHCIKeyboardDriver::CrashUHCIKeyboardDriver(PCI::PCIDevice dev)
: Interrupts::Handler(dev)
{
Header = (PCIDeviceHeader *)dev.Header;
switch (Header->HeaderType)
{
case 128:
{
ExPrint(ERROR_COLOR "Unknown header type %d! Guessing PCI Header 0\n" DEFAULT_COLOR,
Header->HeaderType);
[[fallthrough]];
}
case 0: /* PCI Header 0 */
{
PCI::PCIHeader0 *hdr = (PCI::PCIHeader0 *)Header;
uint32_t BAR[6];
size_t BARsSize[6];
BAR[0] = hdr->BAR0;
BAR[1] = hdr->BAR1;
BAR[2] = hdr->BAR2;
BAR[3] = hdr->BAR3;
BAR[4] = hdr->BAR4;
BAR[5] = hdr->BAR5;
/* BARs Size */
for (short i = 0; i < 6; i++)
{
if (BAR[i] == 0)
continue;
size_t size;
if ((BAR[i] & 1) == 0) /* Memory Base */
{
hdr->BAR0 = 0xFFFFFFFF;
size = hdr->BAR0;
hdr->BAR0 = BAR[i];
BARsSize[i] = size & (~15);
BARsSize[i] = ~BARsSize[i] + 1;
BARsSize[i] = BARsSize[i] & 0xFFFFFFFF;
debug("MEM BAR%d %#lx size: %d",
i, BAR[i], BARsSize[i]);
}
else if ((BAR[i] & 1) == 1) /* I/O Base */
{
hdr->BAR1 = 0xFFFFFFFF;
size = hdr->BAR1;
hdr->BAR1 = BAR[i];
BARsSize[i] = size & (~3);
BARsSize[i] = ~BARsSize[i] + 1;
BARsSize[i] = BARsSize[i] & 0xFFFF;
debug("I/O BAR%d %#lx size: %d",
i, BAR[i], BARsSize[i]);
}
}
uintptr_t baseAddress = BAR[4];
assert(baseAddress & 0x1); /* must be I/O */
debug("baseAddress: %#lx size %#lx", baseAddress, BARsSize[4]);
Memory::Virtual vmm;
vmm.Map((void *)baseAddress, (void *)baseAddress, BARsSize[4], Memory::RW);
io = (IORegisters *)baseAddress;
break;
}
case 1: /* PCI Header 1 (PCI-to-PCI Bridge) */
{
ExPrint(ERROR_COLOR "PCI-to-PCI Bridge not supported\n" DEFAULT_COLOR);
break;
}
case 2: /* PCI Header 2 (PCI-to-CardBus Bridge) */
{
ExPrint(ERROR_COLOR "PCI-to-CardBus Bridge not supported\n" DEFAULT_COLOR);
break;
}
default:
{
ExPrint(ERROR_COLOR "Invalid PCI header type\n" DEFAULT_COLOR);
break;
}
}
}

View File

@ -0,0 +1,215 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <ints.hpp>
class CrashUHCIKeyboardDriver : public Interrupts::Handler
{
public:
struct IORegisters
{
union
{
struct
{
uint16_t RS : 1;
uint16_t HCRESET : 1;
uint16_t GRESET : 1;
uint16_t EGSM : 1;
uint16_t FGR : 1;
uint16_t SWDBG : 1;
uint16_t CF : 1;
uint16_t MAXP : 1;
uint16_t __reserved0 : 8;
} __packed;
uint16_t raw;
} USBCMD;
union
{
struct
{
uint16_t USBINT : 1;
uint16_t USBERRINT : 1;
uint16_t RD : 1;
uint16_t HSE : 1;
uint16_t HCPE : 1;
uint16_t HCH : 1;
uint16_t __reserved0 : 10;
} __packed;
uint16_t raw;
} USBSTS;
union
{
struct
{
uint16_t TOCRC : 1;
uint16_t RIE : 1;
uint16_t IOCE : 1;
uint16_t SPIE : 1;
uint16_t __reserved0 : 12;
} __packed;
uint16_t raw;
} USBINTR;
union
{
struct
{
uint16_t FN : 9;
uint16_t __reserved0 : 7;
} __packed;
uint16_t raw;
} FRNUM;
union
{
struct
{
uint32_t __reserved0 : 12;
uint32_t BA : 20;
} __packed;
uint32_t raw;
} FRBASEADD;
union
{
struct
{
uint8_t SOFTVAL : 7;
uint8_t __reserved0 : 1;
} __packed;
uint8_t raw;
} SOFMOD;
union
{
struct
{
uint16_t CCS : 1;
uint16_t CSC : 1;
uint16_t PE : 1;
uint16_t PEC : 1;
uint16_t LS : 1;
uint16_t RD : 1;
uint16_t __reserved0 : 1; // always 1
uint16_t LSDA : 1;
uint16_t PR : 1;
uint16_t __reserved1 : 2;
uint16_t SUS : 1;
uint16_t __reserved2 : 4;
} __packed;
uint16_t raw;
} PORTSC[2];
} __packed;
struct TD
{
union
{
struct
{
uint32_t T : 1;
uint32_t Q : 1;
uint32_t Vf : 1;
uint32_t __reserved0 : 1;
uint32_t LP : 28;
} __packed;
uint32_t raw;
} LINK;
union
{
struct
{
uint32_t ActLen : 11;
uint32_t __reserved0 : 5;
uint32_t STATUS : 8;
uint32_t IOC : 1;
uint32_t IOS : 1;
uint32_t LS : 1;
uint32_t unknown_0 : 2; /* missing in the spec @ 3.2.2 */
uint32_t SPD : 1;
uint32_t __reserved1 : 2;
} __packed;
uint32_t raw;
} CS;
union
{
struct
{
uint32_t PID : 8;
uint32_t DeviceAddress : 6;
uint32_t EndPt : 4;
uint32_t D : 1;
uint32_t __reserved0 : 1;
uint32_t MaxLen : 11;
} __packed;
uint32_t raw;
} TOKEN;
union
{
struct
{
uint32_t Addr : 32;
} __packed;
uint32_t raw;
} BUFFER;
/* The last 4 DWords of the
Transfer Descriptor are
reserved for use by software.
- UHCI Design Guide 1.1 @ 3.2.5
*/
uint32_t __padding[4];
} *td __aligned(16) __packed;
struct QH
{
union
{
struct
{
uint32_t T : 1;
uint32_t Q : 1;
uint32_t __reserved0 : 2; /* must be 0 */
uint32_t QHLP : 28;
} __packed;
uint32_t raw;
} HEAD;
union
{
struct
{
uint32_t T : 1;
uint32_t Q : 1;
uint32_t __reserved0 : 1;
uint32_t __reserved1 : 1; /* must be 0 */
uint32_t QELP : 28;
} __packed;
uint32_t raw;
} ELEMENT;
/* FIXME: It is the same with TD? */
} *qh __aligned(16) __packed;
PCI::PCIDeviceHeader *Header = nullptr;
IORegisters *io = nullptr;
uint32_t *FrameList = nullptr;
void OnInterruptReceived(CPU::TrapFrame *Frame);
bool Initialize();
CrashUHCIKeyboardDriver(PCI::PCIDevice dev);
~CrashUHCIKeyboardDriver() = default;
};

View File

@ -0,0 +1,204 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include "xhci.hpp"
#include <interface/aip.h>
#include <display.hpp>
#include <convert.h>
#include <printf.h>
#include <kcon.hpp>
#include <debug.h>
#include <smp.hpp>
#include <cpu.hpp>
#include <io.h>
#if defined(a64)
#include "../../../arch/amd64/cpu/gdt.hpp"
#elif defined(a32)
#elif defined(aa64)
#endif
#include "../../../kernel.h"
using namespace KernelConsole;
using namespace PCI;
#define ERROR_COLOR "\x1b[31m"
#define WARN_COLOR "\x1b[33m"
#define DEFAULT_COLOR "\x1b[0m"
extern void ExPrint(const char *Format, ...);
extern void ArrowInput(uint8_t key);
extern void UserInput(char *Input);
extern FontRenderer CrashFontRenderer;
nsa bool CrashXHCIKeyboardDriver::TakeOwnership()
{
HCExtCap *exCap = (HCExtCap *)(uintptr_t)this->ExtendedCaps;
if (exCap->USBLEGSUP.CapID != 1)
return true;
if (exCap->USBLEGSUP.BIOSOwnsHC == 0)
return true;
exCap->USBLEGSUP.OSOwnsHC = 1;
TimeManager->Sleep(200, Time::Milliseconds);
if (exCap->USBLEGSUP.BIOSOwnsHC == 0)
return true;
ExPrint(ERROR_COLOR "BIOS owns the USB controller\n" DEFAULT_COLOR);
return false;
}
nsa bool CrashXHCIKeyboardDriver::Initialize()
{
int timeout = 10;
if (!TakeOwnership())
{
ExPrint(ERROR_COLOR "Failed to take ownership\n" DEFAULT_COLOR);
return false;
}
stub;
return false; /* FIXME: stub */
}
nsa CrashXHCIKeyboardDriver::CrashXHCIKeyboardDriver(PCIDevice xhci)
: Interrupts::Handler(xhci)
{
Header = (PCIDeviceHeader *)xhci.Header;
switch (Header->HeaderType)
{
case 128:
{
ExPrint(ERROR_COLOR "Unknown header type %d! Guessing PCI Header 0\n" DEFAULT_COLOR,
Header->HeaderType);
[[fallthrough]];
}
case 0: /* PCI Header 0 */
{
PCI::PCIHeader0 *hdr = (PCI::PCIHeader0 *)Header;
uint32_t BAR[6];
size_t BARsSize[6];
BAR[0] = hdr->BAR0;
BAR[1] = hdr->BAR1;
BAR[2] = hdr->BAR2;
BAR[3] = hdr->BAR3;
BAR[4] = hdr->BAR4;
BAR[5] = hdr->BAR5;
/* BARs Size */
for (short i = 0; i < 6; i++)
{
if (BAR[i] == 0)
continue;
size_t size;
if ((BAR[i] & 1) == 0) /* Memory Base */
{
hdr->BAR0 = 0xFFFFFFFF;
size = hdr->BAR0;
hdr->BAR0 = BAR[i];
BARsSize[i] = size & (~15);
BARsSize[i] = ~BARsSize[i] + 1;
BARsSize[i] = BARsSize[i] & 0xFFFFFFFF;
debug("MEM BAR%d %#lx size: %d",
i, BAR[i], BARsSize[i]);
}
else if ((BAR[i] & 1) == 1) /* I/O Base */
{
hdr->BAR1 = 0xFFFFFFFF;
size = hdr->BAR1;
hdr->BAR1 = BAR[i];
BARsSize[i] = size & (~3);
BARsSize[i] = ~BARsSize[i] + 1;
BARsSize[i] = BARsSize[i] & 0xFFFF;
debug("IO BAR%d %#lx size: %d",
i, BAR[i], BARsSize[i]);
}
}
debug("IO %d 64-BIT %d", BAR[0] & 0x1, BAR[0] & 0x4);
uintptr_t baseAddress = BAR[0];
if (BAR[0] & 0x4)
baseAddress |= (uintptr_t)BAR[1] << 32;
if (baseAddress & 0x1)
baseAddress &= 0xFFFFFFFFFFFFFFFC;
else
baseAddress &= 0xFFFFFFFFFFFFFFF0;
debug("baseAddress: %#lx", baseAddress);
Memory::Virtual vmm;
vmm.Map((void *)baseAddress, (void *)baseAddress, BARsSize[0], Memory::RW);
caps = (XHCIcap *)baseAddress;
ops = (XHCIop *)(baseAddress + caps->CAPLENGTH);
port = (XHCIport *)(baseAddress + caps->CAPLENGTH + 0x400);
runtime = (XHCIruntime *)(baseAddress + (caps->RTSOFF & ~0x1F));
doorbell = (XHCIdoorbell *)(baseAddress + (caps->DBOFF & ~0x3));
uint16_t exCapOffset = caps->HCCPARAMS1.xHCIExtendedCapacitiesPointer << 2;
ExtendedCaps = (uintptr_t)caps + exCapOffset;
debug("ExtendedCaps: %#lx (%#lx + %#lx)", ExtendedCaps, caps, exCapOffset);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Waddress-of-packed-member"
Interrupter = &runtime->Interrupter[0];
#pragma GCC diagnostic pop
break;
}
case 1: /* PCI Header 1 (PCI-to-PCI Bridge) */
{
ExPrint(ERROR_COLOR "PCI-to-PCI Bridge not supported\n" DEFAULT_COLOR);
break;
}
case 2: /* PCI Header 2 (PCI-to-CardBus Bridge) */
{
ExPrint(ERROR_COLOR "PCI-to-CardBus Bridge not supported\n" DEFAULT_COLOR);
break;
}
default:
{
ExPrint(ERROR_COLOR "Invalid PCI header type\n" DEFAULT_COLOR);
break;
}
}
}
nsa void CrashXHCIKeyboardDriver::OnInterruptReceived(CPU::TrapFrame *Frame)
{
debug("Interrupt received");
Interrupter->IMAN.IP = 1;
if (!ops->USBSTS.EINT)
debug("!USBSTS.EINT");
// return;
ops->USBSTS.EINT = 1;
stub;
Interrupter->IMAN.IP = 0;
ops->USBSTS.EINT = 0;
}

View File

@ -0,0 +1,257 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <ints.hpp>
class CrashXHCIKeyboardDriver : public Interrupts::Handler
{
private:
struct XHCIcap
{
uint8_t CAPLENGTH;
uint8_t __reserved0;
uint16_t HCIVERSION;
uint32_t HCSPARAMS1;
uint32_t HCSPARAMS2;
uint32_t HCSPARAMS3;
union
{
struct
{
uint32_t AC64 : 1;
uint32_t BNC : 1;
uint32_t CSZ : 1;
uint32_t PPC : 1;
uint32_t PIND : 1;
uint32_t LHRC : 1;
uint32_t LTC : 1;
uint32_t NSS : 1;
uint32_t PAE : 1;
uint32_t SPC : 1;
uint32_t SEC : 1;
uint32_t CFC : 1;
uint32_t MaxPSASize : 4;
uint32_t xHCIExtendedCapacitiesPointer : 16;
} __packed;
uint32_t raw;
} HCCPARAMS1;
uint32_t DBOFF;
uint32_t RTSOFF;
uint32_t HCCPARAMS2;
} *caps __packed;
struct XHCIop
{
union
{
struct
{
uint32_t RS : 1;
uint32_t HCRST : 1;
uint32_t INTE : 1;
uint32_t HSEE : 1;
uint32_t __reserved0 : 3;
uint32_t LHCRST : 1;
uint32_t CSS : 1;
uint32_t CRS : 1;
uint32_t EWE : 1;
uint32_t EU3S : 1;
uint32_t __reserved1 : 1;
uint32_t CME : 1;
uint32_t ETE : 1;
uint32_t TSCEN : 1;
uint32_t VTIOEN : 1;
uint32_t __reserved2 : 15;
} __packed;
uint32_t raw;
} USBCMD;
union
{
struct
{
uint32_t HCH : 1;
uint32_t __reserved0 : 1;
uint32_t HSE : 1;
uint32_t EINT : 1;
uint32_t PCB : 1;
uint32_t __reserved1 : 3;
uint32_t SSS : 1;
uint32_t RSS : 1;
uint32_t SRE : 1;
uint32_t CNR : 1;
uint32_t HCE : 1;
uint32_t __reserved2 : 18;
} __packed;
uint32_t raw;
} USBSTS;
uint32_t PAGESIZE;
uint8_t __reserved0[8];
uint32_t DNCTRL;
union
{
struct
{
uint64_t RCS : 1;
uint64_t CS : 1;
uint64_t CA : 1;
uint64_t CRR : 1;
uint64_t __reserved0 : 2;
uint64_t CRP : 58;
} __packed;
uint64_t raw;
} CRCR;
uint8_t __reserved1[16];
uint64_t DCBAAP;
uint32_t CONFIG;
} *ops __packed;
struct XHCIport
{
uint32_t PORTSC;
uint32_t PORTMSC;
uint32_t PORTLI;
uint32_t PORTHLPMC;
} *port __packed;
struct XHCIruntime
{
uint32_t MFINDEX;
uint32_t __reserved0[7];
struct XHCIinterrupter
{
union
{
struct
{
uint32_t IP : 1;
uint32_t IE : 1;
uint32_t __reserved0 : 30;
} __packed;
uint32_t raw;
} IMAN;
union
{
struct
{
uint32_t IMODI : 16;
uint32_t IMODC : 16;
} __packed;
uint32_t raw;
} IMOD;
union
{
struct
{
uint32_t ERSTSZ : 16;
uint32_t __reserved0 : 16;
} __packed;
uint32_t raw;
} ERSTSZ;
uint32_t __reserved;
union
{
struct
{
uint64_t __reserved0 : 6;
uint64_t ERSTBAR : 58;
} __packed;
uint64_t raw;
} ERSTBA;
union
{
struct
{
uint64_t DESI : 3;
uint64_t EHB : 1;
uint64_t ERDP : 60;
} __packed;
uint64_t raw;
} ERDP;
} Interrupter[] __packed;
} *runtime __packed;
union XHCIdoorbell
{
struct
{
uint32_t DBTarget : 8;
uint32_t __reserved : 8;
uint32_t DBTaskID : 16;
} __packed;
uint32_t raw;
} *doorbell;
struct HCExtCap
{
union
{
struct
{
uint32_t CapID : 8;
uint32_t NextCapPtr : 8;
uint32_t BIOSOwnsHC : 1;
uint32_t __reserved0 : 7;
uint32_t OSOwnsHC : 1;
uint32_t __reserved1 : 7;
} __packed;
uint32_t raw;
} USBLEGSUP;
union
{
struct
{
uint32_t unknown : 32;
} __packed;
uint32_t raw;
} USBLEGCTLSTS;
} __packed;
struct XHCIprotocol
{
uint8_t CAPID;
uint8_t NextCapPtr;
uint8_t RevisionMinor;
uint8_t RevisionMajor;
uint8_t Name[4];
uint8_t CompPortOffset;
uint8_t CompPortCount;
uint16_t ProtocolDefined : 12;
uint8_t PSIC : 4;
uint8_t ProtocolSlotType : 4;
uint32_t __reserved0 : 28;
} __packed;
PCI::PCIDeviceHeader *Header = nullptr;
uintptr_t ExtendedCaps = 0;
void *baseAddrArray = nullptr;
std::vector<XHCIprotocol *> Protocols = {};
XHCIruntime::XHCIinterrupter *Interrupter = nullptr;
void OnInterruptReceived(CPU::TrapFrame *Frame);
bool TakeOwnership();
public:
bool Initialize();
CrashXHCIKeyboardDriver(PCI::PCIDevice dev);
~CrashXHCIKeyboardDriver() {}
};

1072
Kernel/core/panic/ui.cpp Normal file

File diff suppressed because it is too large Load Diff

166
Kernel/core/panic/user.cpp Normal file
View File

@ -0,0 +1,166 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <display.hpp>
#include <printf.h>
#include <debug.h>
#include <smp.hpp>
#include <cpu.hpp>
#if defined(a64)
#include "../../arch/amd64/cpu/gdt.hpp"
#elif defined(a32)
#elif defined(aa64)
#endif
#include "../../kernel.h"
#ifdef DEBUG
nsa void dbgPrint(CPU::ExceptionFrame *Frame)
{
#if defined(a64)
debug("FS=%#lx GS=%#lx SS=%#lx CS=%#lx DS=%#lx", Frame->fs, Frame->gs, Frame->ss, Frame->cs, Frame->ds);
debug("R8=%#lx R9=%#lx R10=%#lx R11=%#lx", Frame->r8, Frame->r9, Frame->r10, Frame->r11);
debug("R12=%#lx R13=%#lx R14=%#lx R15=%#lx", Frame->r12, Frame->r13, Frame->r14, Frame->r15);
debug("RAX=%#lx RBX=%#lx RCX=%#lx RDX=%#lx", Frame->rax, Frame->rbx, Frame->rcx, Frame->rdx);
debug("RSI=%#lx RDI=%#lx RBP=%#lx RSP=%#lx", Frame->rsi, Frame->rdi, Frame->rbp, Frame->rsp);
debug("RIP=%#lx RFL=%#lx INT=%#lx ERR=%#lx", Frame->rip, Frame->rflags.raw, Frame->InterruptNumber, Frame->ErrorCode);
#elif defined(a32)
debug("FS=%#x GS=%#x CS=%#x DS=%#x", Frame->fs, Frame->gs, Frame->cs, Frame->ds);
debug("EAX=%#x EBX=%#x ECX=%#x EDX=%#x", Frame->eax, Frame->ebx, Frame->ecx, Frame->edx);
debug("ESI=%#x EDI=%#x EBP=%#x ESP=%#x", Frame->esi, Frame->edi, Frame->ebp, Frame->esp);
debug("EIP=%#x EFL=%#x INT=%#x ERR=%#x", Frame->eip, Frame->eflags.raw, Frame->InterruptNumber, Frame->ErrorCode);
#elif defined(aa64)
#endif
#if defined(a86)
debug("CR2=%#lx CR3=%#lx", Frame->cr2, Frame->cr3);
#endif // defined(a86)
#if defined(a64)
debug("RFL: CF:%s PF:%s AF:%s ZF:%s SF:%s TF:%s IF:%s DF:%s OF:%s IOPL:%s NT:%s RF:%s VM:%s AC:%s VIF:%s VIP:%s ID:%s AlwaysOne:%d R0:%#x R1:%#x R2:%#x R3:%#x",
Frame->rflags.CF ? "True " : "False", Frame->rflags.PF ? "True " : "False", Frame->rflags.AF ? "True " : "False", Frame->rflags.ZF ? "True " : "False",
Frame->rflags.SF ? "True " : "False", Frame->rflags.TF ? "True " : "False", Frame->rflags.IF ? "True " : "False", Frame->rflags.DF ? "True " : "False",
Frame->rflags.OF ? "True " : "False", Frame->rflags.IOPL ? "True " : "False", Frame->rflags.NT ? "True " : "False", Frame->rflags.RF ? "True " : "False",
Frame->rflags.VM ? "True " : "False", Frame->rflags.AC ? "True " : "False", Frame->rflags.VIF ? "True " : "False", Frame->rflags.VIP ? "True " : "False",
Frame->rflags.ID ? "True " : "False", Frame->rflags.AlwaysOne,
Frame->rflags.Reserved0, Frame->rflags.Reserved1, Frame->rflags.Reserved2, Frame->rflags.Reserved3);
#elif defined(a32)
debug("EFL: CF:%s PF:%s AF:%s ZF:%s SF:%s TF:%s IF:%s DF:%s OF:%s IOPL:%s NT:%s RF:%s VM:%s AC:%s VIF:%s VIP:%s ID:%s AlwaysOne:%d R0:%#x R1:%#x R2:%#x",
Frame->eflags.CF ? "True " : "False", Frame->eflags.PF ? "True " : "False", Frame->eflags.AF ? "True " : "False", Frame->eflags.ZF ? "True " : "False",
Frame->eflags.SF ? "True " : "False", Frame->eflags.TF ? "True " : "False", Frame->eflags.IF ? "True " : "False", Frame->eflags.DF ? "True " : "False",
Frame->eflags.OF ? "True " : "False", Frame->eflags.IOPL ? "True " : "False", Frame->eflags.NT ? "True " : "False", Frame->eflags.RF ? "True " : "False",
Frame->eflags.VM ? "True " : "False", Frame->eflags.AC ? "True " : "False", Frame->eflags.VIF ? "True " : "False", Frame->eflags.VIP ? "True " : "False",
Frame->eflags.ID ? "True " : "False", Frame->eflags.AlwaysOne,
Frame->eflags.Reserved0, Frame->eflags.Reserved1, Frame->eflags.Reserved2);
#elif defined(aa64)
#endif
}
#endif
nsa bool UserModeExceptionHandler(CPU::ExceptionFrame *Frame)
{
CPUData *core = GetCurrentCPU();
Tasking::PCB *proc = core->CurrentProcess;
Tasking::TCB *thread = core->CurrentThread;
debug("Current process %s(%d) and thread %s(%d)",
proc->Name, proc->ID, thread->Name, thread->ID);
thread->SetState(Tasking::Waiting);
#ifdef DEBUG
dbgPrint(Frame);
#endif
int sigRet = -1;
switch (Frame->InterruptNumber)
{
case CPU::x86::PageFault:
{
bool Handled = proc->vma->HandleCoW(Frame->cr2);
if (!Handled)
Handled = thread->Stack->Expand(Frame->cr2);
if (likely(Handled))
{
debug("Page fault handled");
Frame->cr2 = 0;
thread->SetState(Tasking::Ready);
return true;
}
sigRet = proc->Signals.SendSignal(SIGSEGV,
{Tasking::KILL_CRASH});
break;
}
case CPU::x86::Debug:
case CPU::x86::Breakpoint:
{
sigRet = proc->Signals.SendSignal(SIGTRAP,
{Tasking::KILL_CRASH});
break;
}
case CPU::x86::DivideByZero:
case CPU::x86::Overflow:
case CPU::x86::BoundRange:
case CPU::x86::x87FloatingPoint:
case CPU::x86::SIMDFloatingPoint:
{
sigRet = proc->Signals.SendSignal(SIGFPE,
{Tasking::KILL_CRASH});
break;
}
case CPU::x86::InvalidOpcode:
case CPU::x86::GeneralProtectionFault:
{
sigRet = proc->Signals.SendSignal(SIGILL,
{Tasking::KILL_CRASH});
break;
}
case CPU::x86::DeviceNotAvailable:
{
sigRet = proc->Signals.SendSignal(SIGBUS,
{Tasking::KILL_CRASH});
break;
}
case CPU::x86::NonMaskableInterrupt:
case CPU::x86::DoubleFault:
case CPU::x86::CoprocessorSegmentOverrun:
case CPU::x86::InvalidTSS:
case CPU::x86::SegmentNotPresent:
case CPU::x86::StackSegmentFault:
case CPU::x86::AlignmentCheck:
case CPU::x86::MachineCheck:
case CPU::x86::Virtualization:
case CPU::x86::Security:
default:
{
error("Unhandled exception %d on CPU %d",
Frame->InterruptNumber, core->ID);
break;
}
}
if (sigRet == 0)
{
trace("User mode exception handler handled");
thread->SetState(Tasking::Ready);
return true;
}
error("User mode exception handler failed");
return false;
}

1136
Kernel/core/pci.cpp Normal file

File diff suppressed because it is too large Load Diff

76
Kernel/core/power.cpp Normal file
View File

@ -0,0 +1,76 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <power.hpp>
#include <memory.hpp>
#include <acpi.hpp>
#include <debug.h>
#include <io.h>
#include "../kernel.h"
namespace Power
{
void Power::Reboot()
{
if (((ACPI::ACPI *)this->acpi)->FADT)
if (((ACPI::DSDT *)this->dsdt)->ACPIShutdownSupported)
((ACPI::DSDT *)this->dsdt)->Reboot();
asmv("cli");
uint8_t temp;
do
{
temp = inb(0x64);
if (((temp) & (1 << (0))) != 0)
inb(0x60);
} while (((temp) & (1 << (1))) != 0);
outb(0x64, 0xFE);
}
void Power::Shutdown()
{
if (((ACPI::ACPI *)this->acpi)->FADT)
{
if (((ACPI::DSDT *)this->dsdt)->ACPIShutdownSupported)
((ACPI::DSDT *)this->dsdt)->Shutdown();
else
KPrint("Shutdown not supported");
/* TODO: If no ACPI, display "It is now safe to turn off your computer"? */
}
/* FIXME: Detect emulators and use their shutdown methods */
#ifdef DEBUG
outl(0xB004, 0x2000); // for qemu
outl(0x604, 0x2000); // if qemu not working, bochs and older versions of qemu
outl(0x4004, 0x3400); // virtual box
#endif
}
void Power::InitDSDT()
{
if (((ACPI::ACPI *)this->acpi)->FADT)
this->dsdt = new ACPI::DSDT((ACPI::ACPI *)acpi);
}
Power::Power()
{
this->acpi = new ACPI::ACPI;
this->madt = new ACPI::MADT(((ACPI::ACPI *)acpi)->MADT);
trace("Power manager initialized");
}
}

116
Kernel/core/random.cpp Normal file
View File

@ -0,0 +1,116 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <rand.hpp>
#include <cpu.hpp>
namespace Random
{
uint64_t Seed = 0xdeadbeef;
bool RDRANDFlag, RDSEEDFlag;
__constructor void InitRandomSeed()
{
if (strcmp(CPU::Hypervisor(), x86_CPUID_VENDOR_TCG) != 0)
{
if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0)
{
CPU::x86::AMD::CPUID0x00000007_ECX_0 cpuid;
RDSEEDFlag = cpuid.EBX.RDSEED;
}
else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0)
{
CPU::x86::Intel::CPUID0x00000007_0 cpuid;
RDSEEDFlag = cpuid.EBX.RDSEED;
}
}
else
RDSEEDFlag = false;
if (strcmp(CPU::Hypervisor(), x86_CPUID_VENDOR_TCG) != 0)
{
if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0)
{
CPU::x86::AMD::CPUID0x00000001 cpuid;
RDRANDFlag = cpuid.ECX.RDRAND;
}
else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0)
{
CPU::x86::Intel::CPUID0x00000001 cpuid;
RDRANDFlag = cpuid.ECX.RDRAND;
}
}
else
RDRANDFlag = false;
if (RDSEEDFlag)
fixme("RDSEED is not implemented yet!");
}
uint16_t rand16()
{
#if defined(a86)
if (RDRANDFlag)
{
uint16_t RDRANDValue = 0;
asmv("1: rdrand %0; jnc 1b"
: "=r"(RDRANDValue));
return RDRANDValue;
}
Seed = Seed * 1103515245 + 12345;
return (uint16_t)(Seed / 65536) % __UINT16_MAX__;
#endif
return 0;
}
uint32_t rand32()
{
#if defined(a86)
if (RDRANDFlag)
{
uint32_t RDRANDValue = 0;
asmv("1: rdrand %0; jnc 1b"
: "=r"(RDRANDValue));
return RDRANDValue;
}
Seed = Seed * 1103515245 + 12345;
return (uint32_t)(Seed / 65536) % __UINT32_MAX__;
#endif
return 0;
}
uint64_t rand64()
{
#if defined(a86)
if (RDRANDFlag)
{
uintptr_t RDRANDValue = 0;
asmv("1: rdrand %0; jnc 1b"
: "=r"(RDRANDValue));
return RDRANDValue;
}
Seed = Seed * 1103515245 + 12345;
return (uint64_t)(Seed / 65536) % __UINT64_MAX__;
#endif
return 0;
}
void ChangeSeed(uint64_t CustomSeed) { Seed = CustomSeed; }
}

86
Kernel/core/smbios.cpp Normal file
View File

@ -0,0 +1,86 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include "smbios.hpp"
#include <debug.h>
#include "../kernel.h"
namespace SMBIOS
{
bool CheckSMBIOS()
{
if (bInfo.SMBIOSPtr != nullptr && bInfo.SMBIOSPtr < (void *)0xFFFFFFFFFFFF0000)
{
debug("SMBIOS is available (%#lx).", bInfo.SMBIOSPtr);
return true;
}
debug("SMBIOS is not available. (%#lx)", bInfo.SMBIOSPtr);
return false;
}
SMBIOSEntryPoint *GetSMBIOSEntryPoint() { return (SMBIOSEntryPoint *)bInfo.SMBIOSPtr; }
__no_sanitize("alignment") static inline int SMBIOSTableLength(SMBIOSHeader *Hdr)
{
int i;
const char *strtab = (char *)Hdr + Hdr->Length;
for (i = 1; strtab[i - 1] != '\0' || strtab[i] != '\0'; i++)
;
return Hdr->Length + i + 1;
}
__no_sanitize("alignment") void *GetSMBIOSHeader(SMBIOSType Type)
{
if (!CheckSMBIOS())
return nullptr;
SMBIOSEntryPoint *Header = (SMBIOSEntryPoint *)bInfo.SMBIOSPtr;
debug("Getting SMBIOS header for type %d", Type);
struct SMBIOSHeader *hdr = (SMBIOSHeader *)(uintptr_t)Header->TableAddress;
for (int i = 0; i <= 11; i++)
{
if (hdr < (void *)(uintptr_t)(Header->TableAddress + Header->TableLength))
if (hdr->Type == Type)
{
debug("Found SMBIOS header for type %d at %#lx", Type, hdr);
return hdr;
}
hdr = (struct SMBIOSHeader *)((uintptr_t)hdr + SMBIOSTableLength(hdr));
}
return nullptr;
}
SMBIOSBIOSInformation *GetBIOSInformation() { return (SMBIOSBIOSInformation *)GetSMBIOSHeader(SMBIOSTypeBIOSInformation); }
SMBIOSSystemInformation *GetSystemInformation() { return (SMBIOSSystemInformation *)GetSMBIOSHeader(SMBIOSTypeSystemInformation); }
SMBIOSBaseBoardInformation *GetBaseBoardInformation() { return (SMBIOSBaseBoardInformation *)GetSMBIOSHeader(SMBIOSTypeBaseBoardInformation); }
SMBIOSProcessorInformation *GetProcessorInformation() { return (SMBIOSProcessorInformation *)GetSMBIOSHeader(SMBIOSTypeProcessorInformation); }
SMBIOSMemoryArray *GetMemoryArray() { return (SMBIOSMemoryArray *)GetSMBIOSHeader(SMBIOSTypePhysicalMemoryArray); }
SMBIOSMemoryDevice *GetMemoryDevice() { return (SMBIOSMemoryDevice *)GetSMBIOSHeader(SMBIOSTypeMemoryDevice); }
SMBIOSMemoryArrayMappedAddress *GetMemoryArrayMappedAddress() { return (SMBIOSMemoryArrayMappedAddress *)GetSMBIOSHeader(SMBIOSTypeMemoryArrayMappedAddress); }
SMBIOSMemoryDeviceMappedAddress *GetMemoryDeviceMappedAddress() { return (SMBIOSMemoryDeviceMappedAddress *)GetSMBIOSHeader(SMBIOSTypeMemoryDeviceMappedAddress); }
}

357
Kernel/core/smbios.hpp Normal file
View File

@ -0,0 +1,357 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __FENNIX_KERNEL_SMBIOS_H__
#define __FENNIX_KERNEL_SMBIOS_H__
#include <types.h>
namespace SMBIOS
{
enum SMBIOSType
{
SMBIOSTypeBIOSInformation = 0,
SMBIOSTypeSystemInformation = 1,
SMBIOSTypeBaseBoardInformation = 2,
SMBIOSTypeSystemEnclosure = 3,
SMBIOSTypeProcessorInformation = 4,
SMBIOSTypeMemoryControllerInformation = 5,
SMBIOSTypeMemoryModuleInformation = 6,
SMBIOSTypeCacheInformation = 7,
SMBIOSTypePortConnectorInformation = 8,
SMBIOSTypeSystemSlots = 9,
SMBIOSTypeOnBoardDevicesInformation = 10,
SMBIOSTypeOEMStrings = 11,
SMBIOSTypeSystemConfigurationOptions = 12,
SMBIOSTypeBIOSLanguageInformation = 13,
SMBIOSTypeGroupAssociations = 14,
SMBIOSTypeSystemEventLog = 15,
SMBIOSTypePhysicalMemoryArray = 16,
SMBIOSTypeMemoryDevice = 17,
SMBIOSType32BitMemoryErrorInformation = 18,
SMBIOSTypeMemoryArrayMappedAddress = 19,
SMBIOSTypeMemoryDeviceMappedAddress = 20,
SMBIOSTypeBuiltInPointingDevice = 21,
SMBIOSTypePortableBattery = 22,
SMBIOSTypeSystemReset = 23,
SMBIOSTypeHardwareSecurity = 24,
SMBIOSTypeSystemPowerControls = 25,
SMBIOSTypeVoltageProbe = 26,
SMBIOSTypeCoolingDevice = 27,
SMBIOSTypeTemperatureProbe = 28,
SMBIOSTypeElectricalCurrentProbe = 29,
SMBIOSTypeOutofBandRemoteAccess = 30,
SMBIOSTypeBootIntegrityServices = 31,
SMBIOSTypeSystemBoot = 32,
SMBIOSType64BitMemoryErrorInformation = 33,
SMBIOSTypeManagementDevice = 34,
SMBIOSTypeManagementDeviceComponent = 35,
SMBIOSTypeManagementDeviceThresholdData = 36,
SMBIOSTypeMemoryChannel = 37,
SMBIOSTypeIPMIDevice = 38,
SMBIOSTypePowerSupply = 39,
SMBIOSTypeAdditionalInformation = 40,
SMBIOSTypeOnboardDevicesExtendedInformation = 41,
SMBIOSTypeManagementControllerHostInterface = 42,
SMBIOSTypeTPMDevice = 43,
SMBIOSTypeProcessorAdditionalInformation = 44,
SMBIOSTypeInactive = 126,
SMBIOSTypeEndOfTable = 127
};
struct SMBIOSHeader
{
unsigned char Type;
unsigned char Length;
unsigned short Handle;
};
struct SMBIOSEntryPoint
{
char EntryPointString[4];
unsigned char Checksum;
unsigned char Length;
unsigned char MajorVersion;
unsigned char MinorVersion;
unsigned short MaxStructureSize;
unsigned char EntryPointRevision;
char FormattedArea[5];
char EntryPointString2[5];
unsigned char Checksum2;
unsigned short TableLength;
unsigned int TableAddress;
unsigned short NumberOfStructures;
unsigned char BCDRevision;
};
static inline char *SMBIOSNextString(char *Str)
{
while (*Str != '\0')
Str++;
return Str + 1;
}
struct SMBIOSBIOSInformation
{
SMBIOSHeader Header;
unsigned char Vendor;
unsigned char Version;
unsigned short StartingAddressSegment;
unsigned char ReleaseDate;
unsigned char ROMSize;
unsigned long Characteristics;
unsigned char CharacteristicsExtensionBytes[2];
unsigned char SystemBIOSMajorRelease;
unsigned char SystemBIOSMinorRelease;
unsigned char EmbeddedControllerFirmwareMajorRelease;
unsigned char EmbeddedControllerFirmwareMinorRelease;
const char *GetString(unsigned char Index)
{
char *Str = (char *)((unsigned long)this + this->Header.Length);
Index--;
if (Index == 0 || Index > 10)
return Str;
for (unsigned char i = 0; i < Index; i++)
Str = SMBIOSNextString(Str);
return Str;
}
};
struct SMBIOSSystemInformation
{
SMBIOSHeader Header;
unsigned char Manufacturer;
unsigned char ProductName;
unsigned char Version;
unsigned char SerialNumber;
unsigned char UUID[16];
unsigned char WakeUpType;
unsigned char SKU;
unsigned char Family;
const char *GetString(unsigned char Index)
{
char *Str = (char *)((unsigned long)this + this->Header.Length);
Index--;
if (Index == 0 || Index > 10)
return Str;
for (unsigned char i = 0; i < Index; i++)
Str = SMBIOSNextString(Str);
return Str;
}
};
struct SMBIOSBaseBoardInformation
{
SMBIOSHeader Header;
unsigned char Manufacturer;
unsigned char Product;
unsigned char Version;
unsigned char SerialNumber;
unsigned char AssetTag;
unsigned char FeatureFlags;
unsigned char LocationInChassis;
unsigned short ChassisHandle;
unsigned char BoardType;
unsigned char NumberOfContainedObjectHandles;
unsigned short ContainedObjectHandles[0];
const char *GetString(unsigned char Index)
{
char *Str = (char *)((unsigned long)this + this->Header.Length);
Index--;
if (Index == 0 || Index > 10)
return Str;
for (unsigned char i = 0; i < Index; i++)
Str = SMBIOSNextString(Str);
return Str;
}
};
struct SMBIOSProcessorInformation
{
SMBIOSHeader Header;
unsigned char SocketDesignation;
unsigned char ProcessorType;
unsigned char ProcessorFamily;
unsigned char ProcessorManufacturer;
unsigned long ProcessorID;
unsigned char ProcessorVersion;
unsigned char Voltage;
unsigned short ExternalClock;
unsigned short MaxSpeed;
unsigned short CurrentSpeed;
unsigned char Status;
unsigned char ProcessorUpgrade;
unsigned short L1CacheHandle;
unsigned short L2CacheHandle;
unsigned short L3CacheHandle;
unsigned char SerialNumber;
unsigned char AssetTag;
unsigned char PartNumber;
unsigned char CoreCount;
unsigned char CoreEnabled;
unsigned char ThreadCount;
unsigned short ProcessorCharacteristics;
unsigned short ProcessorFamily2;
unsigned short CoreCount2;
unsigned short CoreEnabled2;
unsigned short ThreadCount2;
const char *GetString(unsigned char Index)
{
char *Str = (char *)((unsigned long)this + this->Header.Length);
Index--;
if (Index == 0 || Index > 10)
return Str;
for (unsigned char i = 0; i < Index; i++)
Str = SMBIOSNextString(Str);
return Str;
}
};
struct SMBIOSMemoryDevice
{
SMBIOSHeader Header;
unsigned char PhysicalMemoryArrayHandle;
unsigned char MemoryErrorInformationHandle;
unsigned short TotalWidth;
unsigned short DataWidth;
unsigned short Size;
unsigned char FormFactor;
unsigned char DeviceSet;
unsigned char DeviceLocator;
unsigned char BankLocator;
unsigned char MemoryType;
unsigned short TypeDetail;
unsigned short Speed;
unsigned char Manufacturer;
unsigned char SerialNumber;
unsigned char AssetTag;
unsigned char PartNumber;
unsigned char Attributes;
unsigned short ExtendedSize;
unsigned short ConfiguredMemoryClockSpeed;
unsigned short MinimumVoltage;
unsigned short MaximumVoltage;
unsigned short ConfiguredVoltage;
unsigned char MemoryTechnology;
unsigned char OperatingModeCapability;
unsigned char FirmwareVersion;
unsigned char ModuleManufacturerID;
unsigned char ModuleProductID;
unsigned char MemorySubsystemControllerManufacturerID;
unsigned char MemorySubsystemControllerProductID;
unsigned short NonVolatileSize;
unsigned short VolatileSize;
unsigned short CacheSize;
unsigned short LogicalSize;
unsigned char ExtendedSpeed;
unsigned char ExtendedConfiguredMemorySpeed;
const char *GetString(unsigned char Index)
{
char *Str = (char *)((unsigned long)this + this->Header.Length);
Index--;
if (Index == 0 || Index > 10)
return Str;
for (unsigned char i = 0; i < Index; i++)
Str = SMBIOSNextString(Str);
return Str;
}
};
struct SMBIOSMemoryArrayMappedAddress
{
SMBIOSHeader Header;
unsigned int StartingAddress;
unsigned int EndingAddress;
unsigned short MemoryArrayHandle;
unsigned char PartitionWidth;
const char *GetString(unsigned char Index)
{
char *Str = (char *)((unsigned long)this + this->Header.Length);
Index--;
if (Index == 0 || Index > 10)
return Str;
for (unsigned char i = 0; i < Index; i++)
Str = SMBIOSNextString(Str);
return Str;
}
};
struct SMBIOSMemoryDeviceMappedAddress
{
SMBIOSHeader Header;
unsigned int StartingAddress;
unsigned int EndingAddress;
unsigned short MemoryDeviceHandle;
unsigned short MemoryArrayMappedAddressHandle;
unsigned char PartitionRowPosition;
unsigned char InterleavePosition;
unsigned char InterleavedDataDepth;
const char *GetString(unsigned char Index)
{
char *Str = (char *)((unsigned long)this + this->Header.Length);
Index--;
if (Index == 0 || Index > 10)
return Str;
for (unsigned char i = 0; i < Index; i++)
Str = SMBIOSNextString(Str);
return Str;
}
};
struct SMBIOSMemoryArray
{
SMBIOSHeader Header;
unsigned char Location;
unsigned char Use;
unsigned char MemoryErrorCorrection;
unsigned int MaximumCapacity;
unsigned short MemoryErrorInformationHandle;
unsigned short NumberOfMemoryDevices;
const char *GetString(unsigned char Index)
{
char *Str = (char *)((unsigned long)this + this->Header.Length);
Index--;
if (Index == 0 || Index > 10)
return Str;
for (unsigned char i = 0; i < Index; i++)
Str = SMBIOSNextString(Str);
return Str;
}
};
bool CheckSMBIOS();
SMBIOSEntryPoint *GetSMBIOSEntryPoint();
void *GetSMBIOSHeader(SMBIOSType Type);
SMBIOSBIOSInformation *GetBIOSInformation();
SMBIOSSystemInformation *GetSystemInformation();
SMBIOSBaseBoardInformation *GetBaseBoardInformation();
SMBIOSProcessorInformation *GetProcessorInformation();
SMBIOSMemoryArray *GetMemoryArray();
SMBIOSMemoryDevice *GetMemoryDevice();
SMBIOSMemoryArrayMappedAddress *GetMemoryArrayMappedAddress();
SMBIOSMemoryDeviceMappedAddress *GetMemoryDeviceMappedAddress();
}
#endif // !__FENNIX_KERNEL_SMBIOS_H__

View File

@ -0,0 +1,84 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <types.h>
#include <debug.h>
#include <rand.hpp>
#include "../kernel.h"
extern __noreturn void HandleStackSmashing();
extern __noreturn void HandleBufferOverflow();
/* EXTERNC */ uintptr_t __stack_chk_guard = 0;
EXTERNC __no_stack_protector uintptr_t __stack_chk_guard_init(void)
{
int MaxRetries = 0;
#if UINTPTR_MAX == UINT32_MAX
uint32_t num;
Retry:
num = Random::rand32();
if (num < 0x1000 && MaxRetries++ < 10)
goto Retry;
return num;
#else
uint64_t num;
Retry:
num = Random::rand64();
if (num < 0x100000 && MaxRetries++ < 10)
goto Retry;
return num;
#endif
}
EXTERNC __constructor __no_stack_protector void __guard_setup(void)
{
debug("__guard_setup");
if (__stack_chk_guard == 0)
__stack_chk_guard = __stack_chk_guard_init();
debug("Stack guard value: %ld", __stack_chk_guard);
}
EXTERNC __noreturn __no_stack_protector void __stack_chk_fail(void)
{
CPU::PageTable(KernelPageTable);
void *Stack = nullptr;
#if defined(a64)
asmv("movq %%rsp, %0"
: "=r"(Stack));
#elif defined(a32)
asmv("movl %%esp, %0"
: "=r"(Stack));
#elif defined(aa64)
asmv("mov %%sp, %0"
: "=r"(Stack));
#endif
error("Stack address: %#lx", Stack);
HandleStackSmashing();
}
// https://github.com/gcc-mirror/gcc/blob/master/libssp/ssp.c
EXTERNC __noreturn nsa void __chk_fail(void)
{
CPU::PageTable(KernelPageTable);
HandleBufferOverflow();
}

330
Kernel/core/symbols.cpp Normal file
View File

@ -0,0 +1,330 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <symbols.hpp>
#include <memory.hpp>
#include <convert.h>
#include <debug.h>
#include <elf.h>
// #pragma GCC diagnostic ignored "-Wignored-qualifiers"
namespace SymbolResolver
{
const NIF char *Symbols::GetSymbol(uintptr_t Address)
{
SymbolTable Result{};
if (this->SymbolTableExists == false)
{
debug("Symbol table does not exist");
if (this->SymTable.size() > 0)
{
debug("SymbolTableExists is false but SymTable.size() is %d",
this->SymTable.size());
}
return Result.FunctionName;
}
for (auto it = this->SymTable.rbegin(); it != this->SymTable.rend(); ++it)
{
if (unlikely(it->Address <= Address && it->Address > Result.Address))
{
Result = *it;
break;
}
}
// debug("Symbol %#lx: %s", Result.Address, Result.FunctionName);
return Result.FunctionName;
}
uintptr_t Symbols::GetSymbol(const char *Name)
{
SymbolTable Result{};
if (this->SymbolTableExists == false)
{
debug("Symbol table does not exist");
if (this->SymTable.size() > 0)
{
debug("SymbolTableExists is false but SymTable.size() is %d",
this->SymTable.size());
}
return Result.Address;
}
for (auto it = this->SymTable.rbegin(); it != this->SymTable.rend(); ++it)
{
if (unlikely(strcmp(it->FunctionName, Name) == 0))
{
Result = *it;
break;
}
}
// debug("Symbol %#lx: %s", Result.Address, Result.FunctionName);
return Result.Address;
}
void Symbols::AddSymbol(uintptr_t Address, const char *Name)
{
SymbolTable tbl{};
tbl.Address = Address;
tbl.FunctionName = new char[strlen(Name) + 1];
strcpy(tbl.FunctionName, Name);
this->SymTable.push_back(tbl);
this->SymbolTableExists = true;
}
__no_sanitize("alignment") void Symbols::AddSymbolInfoFromGRUB(uint64_t Num,
uint64_t EntSize,
__unused uint64_t Shndx,
uintptr_t Sections)
{
char *sections = r_cst(char *, Sections);
Elf_Sym *Symbols = nullptr;
uint8_t *StringAddress = nullptr;
#if defined(a64) || defined(aa64)
Elf64_Xword SymbolSize = 0;
// Elf64_Xword StringSize = 0;
#elif defined(a32)
Elf32_Word SymbolSize = 0;
// Elf32_Word StringSize = 0;
#endif
size_t TotalEntries = 0;
for (size_t i = 0; i < Num; ++i)
{
Elf_Shdr *sym = (Elf_Shdr *)&sections[EntSize * i];
Elf_Shdr *str = (Elf_Shdr *)&sections[EntSize * sym->sh_link];
if (sym->sh_type == SHT_SYMTAB &&
str->sh_type == SHT_STRTAB)
{
Symbols = (Elf_Sym *)sym->sh_addr;
StringAddress = (uint8_t *)str->sh_addr;
SymbolSize = (int)sym->sh_size;
// StringSize = (int)str->sh_size;
// TotalEntries = Section.sh_size / sizeof(Elf64_Sym)
TotalEntries = sym->sh_size / sym->sh_entsize;
trace("Symbol table found, %d entries",
SymbolSize / sym->sh_entsize);
UNUSED(SymbolSize);
break;
}
}
if (Symbols != nullptr && StringAddress != nullptr)
{
size_t Index, MinimumIndex;
for (size_t i = 0; i < TotalEntries - 1; i++)
{
MinimumIndex = i;
for (Index = i + 1; Index < TotalEntries; Index++)
if (Symbols[Index].st_value < Symbols[MinimumIndex].st_value)
MinimumIndex = Index;
Elf_Sym tmp = Symbols[MinimumIndex];
Symbols[MinimumIndex] = Symbols[i];
Symbols[i] = tmp;
}
while (Symbols[0].st_value == 0)
{
if (TotalEntries <= 0)
break;
Symbols++;
TotalEntries--;
}
if (TotalEntries <= 0)
{
error("Symbol table is empty");
return;
}
trace("Symbol table loaded, %d entries (%ld KiB)",
TotalEntries, TO_KiB(TotalEntries * sizeof(SymbolTable)));
Elf_Sym *sym;
const char *name;
Memory::Virtual vmm;
for (size_t i = 0, g = TotalEntries; i < g; i++)
{
sym = &Symbols[i];
if (!vmm.Check(sym))
{
error("Symbol %d has invalid address %#lx!",
i, sym);
debug("Base: %#lx, Symbols[%d]: %#lx, Symbols[%d]: %#lx",
Symbols,
i - 1, &Symbols[i - 1],
i + 1, &Symbols[i + 1]);
continue;
}
name = (const char *)&StringAddress[Symbols[i].st_name];
if (!vmm.Check((void *)name))
{
error("String %d has invalid address %#lx!",
i, name);
debug("st_name: %d, st_info: %d, st_other: %d, st_shndx: %d, st_value: %d, st_size: %d",
sym->st_name, sym->st_info, sym->st_other,
sym->st_shndx, sym->st_value, sym->st_size);
continue;
}
if (strlen(name) == 0)
continue;
SymbolTable tbl{};
tbl.Address = sym->st_value;
tbl.FunctionName = new char[strlen(name) + 1];
strcpy(tbl.FunctionName, name);
this->SymTable.push_back(tbl);
this->SymbolTableExists = true;
// debug("Symbol %d: %#lx %s(%#lx)",
// i, tbl.Address,
// tbl.FunctionName,
// name);
}
}
}
void Symbols::AppendSymbols(uintptr_t ImageAddress, uintptr_t BaseAddress)
{
/* FIXME: Get only the required headers instead of the whole file */
if (ImageAddress == 0 ||
Memory::Virtual().Check((void *)ImageAddress) == false)
{
error("Invalid image address %#lx", ImageAddress);
return;
}
debug("Solving symbols for address: %#llx", ImageAddress);
#if defined(a64) || defined(aa64)
Elf64_Ehdr *Header = (Elf64_Ehdr *)ImageAddress;
#elif defined(a32)
Elf32_Ehdr *Header = (Elf32_Ehdr *)ImageAddress;
#endif
if (Header->e_ident[0] != 0x7F &&
Header->e_ident[1] != 'E' &&
Header->e_ident[2] != 'L' &&
Header->e_ident[3] != 'F')
{
error("Invalid ELF header");
return;
}
Elf_Shdr *ElfSections = (Elf_Shdr *)(ImageAddress + Header->e_shoff);
Elf_Sym *ElfSymbols = nullptr;
char *strtab = nullptr;
size_t TotalEntries = 0;
for (uint16_t i = 0; i < Header->e_shnum; i++)
{
switch (ElfSections[i].sh_type)
{
case SHT_SYMTAB:
ElfSymbols = (Elf_Sym *)(ImageAddress + ElfSections[i].sh_offset);
TotalEntries = ElfSections[i].sh_size / sizeof(Elf_Sym);
debug("Symbol table found, %d entries", TotalEntries);
break;
case SHT_STRTAB:
if (Header->e_shstrndx == i)
{
debug("String table found, %d entries", ElfSections[i].sh_size);
}
else
{
strtab = (char *)(ImageAddress + ElfSections[i].sh_offset);
debug("String table found, %d entries", ElfSections[i].sh_size);
}
break;
default:
break;
}
}
if (ElfSymbols != nullptr && strtab != nullptr)
{
size_t Index, MinimumIndex;
for (size_t i = 0; i < TotalEntries - 1; i++)
{
MinimumIndex = i;
for (Index = i + 1; Index < TotalEntries; Index++)
if (ElfSymbols[Index].st_value < ElfSymbols[MinimumIndex].st_value)
MinimumIndex = Index;
Elf_Sym tmp = ElfSymbols[MinimumIndex];
ElfSymbols[MinimumIndex] = ElfSymbols[i];
ElfSymbols[i] = tmp;
}
while (ElfSymbols[0].st_value == 0)
{
ElfSymbols++;
TotalEntries--;
}
trace("Symbol table loaded, %d entries (%ld KiB)",
TotalEntries, TO_KiB(TotalEntries * sizeof(SymbolTable)));
/* TODO: maybe a checker for duplicated addresses? */
Elf_Sym *sym = nullptr;
const char *name = nullptr;
for (size_t i = 0, g = TotalEntries; i < g; i++)
{
sym = &ElfSymbols[i];
name = &strtab[ElfSymbols[i].st_name];
SymbolTable tbl{};
tbl.Address = sym->st_value + BaseAddress;
tbl.FunctionName = new char[strlen(name) + 1];
strcpy(tbl.FunctionName, name);
this->SymTable.push_back(tbl);
this->SymbolTableExists = true;
// debug("Symbol %d: %#llx %s", i,
// this->SymTable[i].Address,
// this->SymTable[i].FunctionName);
}
}
if (this->SymbolTableExists)
{
debug("Symbol table exists, %d entries (%ld KiB)",
this->SymTable.size(), TO_KiB(this->SymTable.size() * sizeof(SymbolTable)));
}
}
Symbols::Symbols(uintptr_t ImageAddress)
{
debug("+ %#lx", this);
this->Image = (void *)ImageAddress;
this->AppendSymbols(ImageAddress);
}
Symbols::~Symbols()
{
debug("- %#lx", this);
debug("Freeing %d symbols",
this->SymTable.size());
foreach (auto tbl in this->SymTable)
delete[] tbl.FunctionName;
}
}

105
Kernel/core/time/hpet.cpp Normal file
View File

@ -0,0 +1,105 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <time.hpp>
#include <memory.hpp>
#include <acpi.hpp>
#include <debug.h>
#include <io.h>
#include "../../kernel.h"
namespace Time
{
bool HighPrecisionEventTimer::Sleep(size_t Duration, Units Unit)
{
#if defined(a64)
uint64_t Target = mminq(&hpet->MainCounterValue) + (Duration * ConvertUnit(Unit)) / clk;
while (mminq(&hpet->MainCounterValue) < Target)
CPU::Pause();
return true;
#elif defined(a32)
uint64_t Target = mminl(&hpet->MainCounterValue) + (Duration * ConvertUnit(Unit)) / clk;
while (mminl(&hpet->MainCounterValue) < Target)
CPU::Pause();
return true;
#endif
return false;
}
uint64_t HighPrecisionEventTimer::GetCounter()
{
#if defined(a64)
return mminq(&hpet->MainCounterValue);
#elif defined(a32)
return mminl(&hpet->MainCounterValue);
#endif
}
uint64_t HighPrecisionEventTimer::CalculateTarget(uint64_t Target, Units Unit)
{
#if defined(a64)
return mminq(&hpet->MainCounterValue) + (Target * ConvertUnit(Unit)) / clk;
#elif defined(a32)
return mminl(&hpet->MainCounterValue) + (Target * ConvertUnit(Unit)) / clk;
#endif
}
uint64_t HighPrecisionEventTimer::GetNanosecondsSinceClassCreation()
{
#if defined(a86)
uint64_t Subtraction = this->GetCounter() - this->ClassCreationTime;
if (Subtraction <= 0 || this->clk <= 0)
return 0;
Subtraction *= ConvertUnit(Units::Nanoseconds);
return uint64_t(Subtraction / this->clk);
#endif
}
HighPrecisionEventTimer::HighPrecisionEventTimer(void *hpet)
{
#if defined(a86)
ACPI::ACPI::HPETHeader *HPET_HDR = (ACPI::ACPI::HPETHeader *)hpet;
Memory::Virtual vmm;
vmm.Map((void *)HPET_HDR->Address.Address,
(void *)HPET_HDR->Address.Address,
Memory::PTFlag::RW | Memory::PTFlag::PCD);
this->hpet = (HPET *)HPET_HDR->Address.Address;
trace("%s timer is at address %016p",
HPET_HDR->Header.OEMID,
(void *)HPET_HDR->Address.Address);
clk = s_cst(uint32_t, (uint64_t)this->hpet->GeneralCapabilities >> 32);
KPrint("HPET clock is %u Hz", clk);
#ifdef a64
mmoutq(&this->hpet->GeneralConfiguration, 0);
mmoutq(&this->hpet->MainCounterValue, 0);
mmoutq(&this->hpet->GeneralConfiguration, 1);
#else
mmoutl(&this->hpet->GeneralConfiguration, 0);
mmoutl(&this->hpet->MainCounterValue, 0);
mmoutl(&this->hpet->GeneralConfiguration, 1);
#endif
ClassCreationTime = this->GetCounter();
#endif
}
HighPrecisionEventTimer::~HighPrecisionEventTimer()
{
}
}

111
Kernel/core/time/time.cpp Normal file
View File

@ -0,0 +1,111 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <time.hpp>
#include <debug.h>
#include <io.h>
namespace Time
{
Clock ReadClock()
{
Clock tm;
#if defined(a86)
uint32_t t = 0;
outb(0x70, 0x00);
t = inb(0x71);
tm.Second = ((t & 0x0F) + ((t >> 4) * 10));
outb(0x70, 0x02);
t = inb(0x71);
tm.Minute = ((t & 0x0F) + ((t >> 4) * 10));
outb(0x70, 0x04);
t = inb(0x71);
tm.Hour = ((t & 0x0F) + ((t >> 4) * 10));
outb(0x70, 0x07);
t = inb(0x71);
tm.Day = ((t & 0x0F) + ((t >> 4) * 10));
outb(0x70, 0x08);
t = inb(0x71);
tm.Month = ((t & 0x0F) + ((t >> 4) * 10));
outb(0x70, 0x09);
t = inb(0x71);
tm.Year = ((t & 0x0F) + ((t >> 4) * 10));
tm.Counter = 0;
#elif defined(aa64)
tm.Year = 0;
tm.Month = 0;
tm.Day = 0;
tm.Hour = 0;
tm.Minute = 0;
tm.Second = 0;
tm.Counter = 0;
#endif
return tm;
}
Clock ConvertFromUnix(int Timestamp)
{
Clock result;
uint64_t Seconds = Timestamp;
uint64_t Minutes = Seconds / 60;
uint64_t Hours = Minutes / 60;
uint64_t Days = Hours / 24;
result.Year = 1970;
while (Days >= 365)
{
if (result.Year % 4 == 0 &&
(result.Year % 100 != 0 ||
result.Year % 400 == 0))
{
if (Days >= 366)
{
Days -= 366;
result.Year++;
}
else
break;
}
else
{
Days -= 365;
result.Year++;
}
}
int DaysInMonth[] = {31,
result.Year % 4 == 0
? 29
: 28,
31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
for (result.Month = 0; result.Month < 12; result.Month++)
{
if (Days < s_cst(uint64_t, (DaysInMonth[result.Month])))
break;
Days -= DaysInMonth[result.Month];
}
result.Month++;
result.Day = s_cst(int, (Days) + 1);
result.Hour = s_cst(int, (Hours % 24));
result.Minute = s_cst(int, (Minutes % 60));
result.Second = s_cst(int, (Seconds % 60));
result.Counter = s_cst(uint64_t, (Timestamp));
return result;
}
}

208
Kernel/core/time/timer.cpp Normal file
View File

@ -0,0 +1,208 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <time.hpp>
#include <memory.hpp>
#include <acpi.hpp>
#include <debug.h>
#include <io.h>
#include "../../kernel.h"
namespace Time
{
bool time::Sleep(size_t Duration, Units Unit)
{
switch (ActiveTimer)
{
case NONE:
error("No timer is active");
return false;
case RTC:
fixme("RTC sleep not implemented");
return false;
case PIT:
fixme("PIT sleep not implemented");
return false;
case HPET:
return this->hpet->Sleep(Duration, Unit);
case ACPI:
fixme("ACPI sleep not implemented");
return false;
case APIC:
fixme("APIC sleep not implemented");
return false;
case TSC:
return this->tsc->Sleep(Duration, Unit);
default:
error("Unknown timer");
return false;
}
}
uint64_t time::GetCounter()
{
switch (ActiveTimer)
{
case NONE:
error("No timer is active");
return 0;
case RTC:
fixme("RTC sleep not implemented");
return 0;
case PIT:
fixme("PIT sleep not implemented");
return 0;
case HPET:
return this->hpet->GetCounter();
case ACPI:
fixme("ACPI sleep not implemented");
return 0;
case APIC:
fixme("APIC sleep not implemented");
return 0;
case TSC:
return this->tsc->GetCounter();
default:
error("Unknown timer");
return 0;
}
}
uint64_t time::CalculateTarget(uint64_t Target, Units Unit)
{
switch (ActiveTimer)
{
case NONE:
error("No timer is active");
return 0;
case RTC:
fixme("RTC sleep not implemented");
return 0;
case PIT:
fixme("PIT sleep not implemented");
return 0;
case HPET:
return this->hpet->CalculateTarget(Target, Unit);
case ACPI:
fixme("ACPI sleep not implemented");
return 0;
case APIC:
fixme("APIC sleep not implemented");
return 0;
case TSC:
return this->tsc->CalculateTarget(Target, Unit);
default:
error("Unknown timer");
return 0;
}
}
uint64_t time::GetNanosecondsSinceClassCreation()
{
switch (ActiveTimer)
{
case NONE:
error("No timer is active");
return 0;
case RTC:
fixme("RTC sleep not implemented");
return 0;
case PIT:
fixme("PIT sleep not implemented");
return 0;
case HPET:
return this->hpet->GetNanosecondsSinceClassCreation();
case ACPI:
fixme("ACPI sleep not implemented");
return 0;
case APIC:
fixme("APIC sleep not implemented");
return 0;
case TSC:
return this->tsc->GetNanosecondsSinceClassCreation();
default:
error("Unknown timer");
return 0;
}
}
void time::FindTimers(void *acpi)
{
#if defined(a86)
/* TODO: RTC check */
/* TODO: PIT check */
if (acpi)
{
if (((ACPI::ACPI *)acpi)->HPET)
{
hpet = new HighPrecisionEventTimer(((ACPI::ACPI *)acpi)->HPET);
ActiveTimer = HPET;
SupportedTimers |= HPET;
KPrint("HPET found");
}
else
{
KPrint("\x1b[33mHPET not found");
}
/* TODO: ACPI check */
/* TODO: APIC check */
}
else
{
KPrint("\x1b[33mACPI not found");
}
bool TSCInvariant = false;
if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_AMD) == 0)
{
CPU::x86::AMD::CPUID0x80000007 cpuid80000007;
if (cpuid80000007.EDX.TscInvariant)
TSCInvariant = true;
}
else if (strcmp(CPU::Vendor(), x86_CPUID_VENDOR_INTEL) == 0)
{
// TODO: Intel 0x80000007
CPU::x86::AMD::CPUID0x80000007 cpuid80000007;
if (cpuid80000007.EDX.TscInvariant)
TSCInvariant = true;
}
if (TSCInvariant)
{
tsc = new TimeStampCounter;
// FIXME: ActiveTimer = TSC;
SupportedTimers |= TSC;
KPrint("Invariant TSC found");
}
else
KPrint("\x1b[33mTSC is not invariant");
#endif
}
time::time()
{
}
time::~time()
{
}
}

76
Kernel/core/time/tsc.cpp Normal file
View File

@ -0,0 +1,76 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <time.hpp>
#include <memory.hpp>
#include <acpi.hpp>
#include <debug.h>
#include <io.h>
#include "../../kernel.h"
namespace Time
{
bool TimeStampCounter::Sleep(size_t Duration, Units Unit)
{
#if defined(a86)
uint64_t Target = this->GetCounter() + (Duration * ConvertUnit(Unit)) / this->clk;
while (this->GetCounter() < Target)
CPU::Pause();
return true;
#endif
}
uint64_t TimeStampCounter::GetCounter()
{
#if defined(a86)
return CPU::Counter();
#endif
}
uint64_t TimeStampCounter::CalculateTarget(uint64_t Target, Units Unit)
{
#if defined(a86)
return uint64_t((this->GetCounter() + (Target * ConvertUnit(Unit))) / this->clk);
#endif
}
uint64_t TimeStampCounter::GetNanosecondsSinceClassCreation()
{
#if defined(a86)
return uint64_t((this->GetCounter() - this->ClassCreationTime) / this->clk);
#endif
}
TimeStampCounter::TimeStampCounter()
{
#if defined(a86)
stub; // FIXME: This is not a good way to measure the clock speed
uint64_t Start = CPU::Counter();
TimeManager->Sleep(1, Units::Milliseconds);
uint64_t End = CPU::Counter();
this->clk = End - Start;
this->ClassCreationTime = this->GetCounter();
#endif
}
TimeStampCounter::~TimeStampCounter()
{
}
}

188
Kernel/core/uart.cpp Normal file
View File

@ -0,0 +1,188 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <uart.hpp>
#include <debug.h>
#include <vector>
bool serialports[8] = {false, false, false, false, false, false, false, false};
std::vector<UniversalAsynchronousReceiverTransmitter::Events *> RegisteredEvents;
#if defined(a86)
NIF __always_inline inline uint8_t NoProfiler_inportb(uint16_t Port)
{
uint8_t Result;
asm("in %%dx, %%al"
: "=a"(Result)
: "d"(Port));
return Result;
}
NIF __always_inline inline void NoProfiler_outportb(uint16_t Port, uint8_t Data)
{
asmv("out %%al, %%dx"
:
: "a"(Data), "d"(Port));
}
#endif
namespace UniversalAsynchronousReceiverTransmitter
{
#define SERIAL_ENABLE_DLAB 0x80
#define SERIAL_RATE_115200_LO 0x01
#define SERIAL_RATE_115200_HI 0x00
#define SERIAL_RATE_57600_LO 0x02
#define SERIAL_RATE_57600_HI 0x00
#define SERIAL_RATE_38400_LO 0x03
#define SERIAL_RATE_38400_HI 0x00
#define SERIAL_BUFFER_EMPTY 0x20
/* TODO: Serial Port implementation needs reword. https://wiki.osdev.org/Serial_Ports */
nsa NIF UART::UART(SerialPorts Port)
{
#if defined(a86)
if (Port == COMNULL)
return;
uint8_t com = NoProfiler_inportb(Port);
if (com == 0xFF)
{
error("Serial port %#lx is not available.", Port);
return;
}
this->Port = Port;
int PortNumber = 0;
switch (Port)
{
case COM1:
PortNumber = 0;
break;
case COM2:
PortNumber = 1;
break;
case COM3:
PortNumber = 2;
break;
case COM4:
PortNumber = 3;
break;
case COM5:
PortNumber = 4;
break;
case COM6:
PortNumber = 5;
break;
case COM7:
PortNumber = 6;
break;
case COM8:
PortNumber = 7;
break;
default:
return;
}
if (serialports[PortNumber])
return;
// Initialize the serial port
NoProfiler_outportb(s_cst(uint16_t, Port + 1), 0x00); // Disable all interrupts
NoProfiler_outportb(s_cst(uint16_t, Port + 3), SERIAL_ENABLE_DLAB); // Enable DLAB (set baud rate divisor)
NoProfiler_outportb(s_cst(uint16_t, Port + 0), SERIAL_RATE_115200_LO); // Set divisor to 1 (lo byte) 115200 baud
NoProfiler_outportb(s_cst(uint16_t, Port + 1), SERIAL_RATE_115200_HI); // (hi byte)
NoProfiler_outportb(s_cst(uint16_t, Port + 3), 0x03); // 8 bits, no parity, one stop bit
NoProfiler_outportb(s_cst(uint16_t, Port + 2), 0xC7); // Enable FIFO, clear them, with 14-byte threshold
NoProfiler_outportb(s_cst(uint16_t, Port + 4), 0x0B); // IRQs enabled, RTS/DSR set
/* FIXME https://wiki.osdev.org/Serial_Ports */
// NoProfiler_outportb(s_cst(uint16_t, Port + 0), 0x1E);
// NoProfiler_outportb(s_cst(uint16_t, Port + 0), 0xAE);
// Check if the serial port is faulty.
// if (NoProfiler_inportb(s_cst(uint16_t, Port + 0)) != 0xAE)
// {
// static int once = 0;
// if (!once++)
// warn("Serial port %#lx is faulty.", Port);
// // serialports[Port] = false; // ignore for now
// // return;
// }
// Set to normal operation mode.
NoProfiler_outportb(s_cst(uint16_t, Port + 4), 0x0F);
serialports[PortNumber] = true;
this->IsAvailable = true;
#endif
}
nsa NIF UART::~UART() {}
nsa NIF void UART::Write(uint8_t Char)
{
if (!this->IsAvailable)
return;
#if defined(a86)
while ((NoProfiler_inportb(s_cst(uint16_t, Port + 5)) & SERIAL_BUFFER_EMPTY) == 0)
;
NoProfiler_outportb(Port, Char);
#endif
foreach (auto e in RegisteredEvents)
if (e->GetRegisteredPort() == Port || e->GetRegisteredPort() == COMNULL)
e->OnSent(Char);
}
nsa NIF uint8_t UART::Read()
{
if (!this->IsAvailable)
return 0;
#if defined(a86)
while ((NoProfiler_inportb(s_cst(uint16_t, Port + 5)) & 1) == 0)
;
return NoProfiler_inportb(Port);
#endif
foreach (auto e in RegisteredEvents)
{
if (e->GetRegisteredPort() == Port || e->GetRegisteredPort() == COMNULL)
{
#if defined(a86)
e->OnReceived(NoProfiler_inportb(Port));
#endif
}
}
}
nsa NIF Events::Events(SerialPorts Port)
{
this->Port = Port;
RegisteredEvents.push_back(this);
}
nsa NIF Events::~Events()
{
forItr(itr, RegisteredEvents)
{
if (*itr == this)
{
RegisteredEvents.erase(itr);
return;
}
}
}
}

518
Kernel/core/ubsan.c Normal file
View File

@ -0,0 +1,518 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include "ubsan.h"
#include <convert.h>
#include <debug.h>
#ifdef DEBUG
// TODO: implement:
/*
__ubsan_handle_type_mismatch_v1_abort
__ubsan_handle_add_overflow_abort
__ubsan_handle_sub_overflow_abort
__ubsan_handle_mul_overflow_abort
__ubsan_handle_negate_overflow_abort
__ubsan_handle_divrem_overflow_abort
__ubsan_handle_shift_out_of_bounds_abort
__ubsan_handle_out_of_bounds_abort
__ubsan_handle_vla_bound_not_positive_abort
__ubsan_handle_float_cast_overflow
__ubsan_handle_float_cast_overflow_abort
__ubsan_handle_load_invalid_value_abort
__ubsan_handle_invalid_builtin_abort
__ubsan_handle_function_type_mismatch_abort
__ubsan_handle_nonnull_return_v1
__ubsan_handle_nonnull_return_v1_abort
__ubsan_handle_nullability_return_v1
__ubsan_handle_nullability_return_v1_abort
__ubsan_handle_nonnull_arg_abort
__ubsan_handle_nullability_arg
__ubsan_handle_nullability_arg_abort
__ubsan_handle_pointer_overflow_abort
__ubsan_handle_cfi_check_fail
*/
void __asan_report_load1(void *unknown)
{
ubsan("load1");
UNUSED(unknown);
}
void __asan_report_load2(void *unknown)
{
ubsan("load2");
UNUSED(unknown);
}
void __asan_report_load4(void *unknown)
{
ubsan("load4");
UNUSED(unknown);
}
void __asan_report_load8(void *unknown)
{
ubsan("load8");
UNUSED(unknown);
}
void __asan_report_load16(void *unknown)
{
ubsan("load16");
UNUSED(unknown);
}
void __asan_report_load_n(void *unknown, uintptr_t size)
{
ubsan("loadn");
UNUSED(unknown);
UNUSED(size);
}
void __asan_report_store1(void *unknown)
{
ubsan("store1");
UNUSED(unknown);
}
void __asan_report_store2(void *unknown)
{
ubsan("store2");
UNUSED(unknown);
}
void __asan_report_store4(void *unknown)
{
ubsan("store4");
UNUSED(unknown);
}
void __asan_report_store8(void *unknown)
{
ubsan("store8");
UNUSED(unknown);
}
void __asan_report_store16(void *unknown)
{
ubsan("store16");
UNUSED(unknown);
}
void __asan_report_store_n(void *unknown, uintptr_t size)
{
ubsan("storen");
UNUSED(unknown);
UNUSED(size);
}
void __asan_report_load1_noabort(void *unknown)
{
ubsan("load1");
UNUSED(unknown);
}
void __asan_report_load2_noabort(void *unknown)
{
ubsan("load2");
UNUSED(unknown);
}
void __asan_report_load4_noabort(void *unknown)
{
ubsan("load4");
UNUSED(unknown);
}
void __asan_report_load8_noabort(void *unknown)
{
ubsan("load8");
UNUSED(unknown);
}
void __asan_report_load16_noabort(void *unknown)
{
ubsan("load16");
UNUSED(unknown);
}
void __asan_report_load_n_noabort(void *unknown, uintptr_t size)
{
ubsan("loadn");
UNUSED(unknown);
UNUSED(size);
}
void __asan_report_store1_noabort(void *unknown)
{
ubsan("store1");
UNUSED(unknown);
}
void __asan_report_store2_noabort(void *unknown)
{
ubsan("store2");
UNUSED(unknown);
}
void __asan_report_store4_noabort(void *unknown)
{
ubsan("store4");
UNUSED(unknown);
}
void __asan_report_store8_noabort(void *unknown)
{
ubsan("store8");
UNUSED(unknown);
}
void __asan_report_store16_noabort(void *unknown)
{
ubsan("store16");
UNUSED(unknown);
}
void __asan_report_store_n_noabort(void *unknown, uintptr_t size)
{
ubsan("storen");
UNUSED(unknown);
UNUSED(size);
}
void __asan_stack_malloc_0(uintptr_t size)
{
ubsan("stack malloc 0");
UNUSED(size);
}
void __asan_stack_malloc_1(uintptr_t size)
{
ubsan("stack malloc 1");
UNUSED(size);
}
void __asan_stack_malloc_2(uintptr_t size)
{
ubsan("stack malloc 2");
UNUSED(size);
}
void __asan_stack_malloc_3(uintptr_t size)
{
ubsan("stack malloc 3");
UNUSED(size);
}
void __asan_stack_malloc_4(uintptr_t size)
{
ubsan("stack malloc 4");
UNUSED(size);
}
void __asan_stack_malloc_5(uintptr_t size)
{
ubsan("stack malloc 5");
UNUSED(size);
}
void __asan_stack_malloc_6(uintptr_t size)
{
ubsan("stack malloc 6");
UNUSED(size);
}
void __asan_stack_malloc_7(uintptr_t size)
{
ubsan("stack malloc 7");
UNUSED(size);
}
void __asan_stack_malloc_8(uintptr_t size)
{
ubsan("stack malloc 8");
UNUSED(size);
}
void __asan_stack_malloc_9(uintptr_t size)
{
ubsan("stack malloc 9");
UNUSED(size);
}
void __asan_stack_free_0(void *ptr, uintptr_t size)
{
ubsan("stack free 0");
UNUSED(ptr);
UNUSED(size);
}
void __asan_stack_free_1(void *ptr, uintptr_t size)
{
ubsan("stack free 1");
UNUSED(ptr);
UNUSED(size);
}
void __asan_stack_free_2(void *ptr, uintptr_t size)
{
ubsan("stack free 2");
UNUSED(ptr);
UNUSED(size);
}
void __asan_stack_free_3(void *ptr, uintptr_t size)
{
ubsan("stack free 3");
UNUSED(ptr);
UNUSED(size);
}
void __asan_stack_free_4(void *ptr, uintptr_t size)
{
ubsan("stack free 4");
UNUSED(ptr);
UNUSED(size);
}
void __asan_stack_free_5(void *ptr, uintptr_t size)
{
ubsan("stack free 5");
UNUSED(ptr);
UNUSED(size);
}
void __asan_stack_free_6(void *ptr, uintptr_t size)
{
ubsan("stack free 6");
UNUSED(ptr);
UNUSED(size);
}
void __asan_stack_free_7(void *ptr, uintptr_t size)
{
ubsan("stack free 7");
UNUSED(ptr);
UNUSED(size);
}
void __asan_stack_free_8(void *ptr, uintptr_t size)
{
ubsan("stack free 8");
UNUSED(ptr);
UNUSED(size);
}
void __asan_stack_free_9(void *ptr, uintptr_t size)
{
ubsan("stack free 9");
UNUSED(ptr);
UNUSED(size);
}
void __asan_poison_stack_memory(void *addr, uintptr_t size)
{
ubsan("poison stack memory");
UNUSED(addr);
UNUSED(size);
}
void __asan_unpoison_stack_memory(void *addr, uintptr_t size)
{
ubsan("unpoison stack memory");
UNUSED(addr);
UNUSED(size);
}
void __asan_before_dynamic_init(const char *module_name)
{
ubsan("before dynamic init");
UNUSED(module_name);
}
void __asan_after_dynamic_init(void) { ubsan("after dynamic init"); }
void __asan_register_globals(void *unknown, size_t size)
{
ubsan("register_globals");
UNUSED(unknown);
UNUSED(size);
}
void __asan_unregister_globals(void) { ubsan("unregister_globals"); }
void __asan_init(void) { ubsan("init"); }
void __asan_version_mismatch_check_v8(void) { ubsan("version_mismatch_check_v8"); }
void __asan_option_detect_stack_use_after_return(void) { ubsan("stack use after return"); }
__noreturn void __asan_handle_no_return(void)
{
ubsan("no_return");
while (1)
;
}
#define is_aligned(value, alignment) !(value & (alignment - 1))
const char *Type_Check_Kinds[] = {
"Load of",
"Store to",
"Reference binding to",
"Member access within",
"Member call on",
"Constructor call on",
"Downcast of",
"Downcast of",
"Upcast of",
"Cast to virtual base of",
};
void __ubsan_handle_type_mismatch_v1(struct type_mismatch_v1_data *type_mismatch, uintptr_t pointer)
{
struct source_location *location = &type_mismatch->location;
if (pointer == 0)
{
ubsan("\t\tIn File: %s:%i:%i", location->file, location->line, location->column);
ubsan("Null pointer access.");
}
else if (type_mismatch->alignment != 0 && is_aligned(pointer, type_mismatch->alignment))
{
ubsan("\t\tIn File: %s:%i:%i", location->file, location->line, location->column);
ubsan("Unaligned memory access %#lx.", pointer);
}
else
{
ubsan("\t\tIn File: %s:%i:%i", location->file, location->line, location->column);
ubsan("%s address %#lx with insufficient space for object of type %s",
Type_Check_Kinds[type_mismatch->type_check_kind],
(void *)pointer, type_mismatch->type->name);
}
}
void __ubsan_handle_add_overflow(struct overflow_data *data)
{
ubsan("\t\tIn File: %s:%i:%i", data->location.file, data->location.line, data->location.column);
ubsan("Addition overflow.");
}
void __ubsan_handle_sub_overflow(struct overflow_data *data)
{
ubsan("\t\tIn File: %s:%i:%i", data->location.file, data->location.line, data->location.column);
ubsan("Subtraction overflow.");
}
void __ubsan_handle_mul_overflow(struct overflow_data *data)
{
ubsan("\t\tIn File: %s:%i:%i", data->location.file, data->location.line, data->location.column);
ubsan("Multiplication overflow.");
}
void __ubsan_handle_divrem_overflow(struct overflow_data *data)
{
ubsan("\t\tIn File: %s:%i:%i", data->location.file, data->location.line, data->location.column);
ubsan("Division overflow.");
}
void __ubsan_handle_negate_overflow(struct overflow_data *data)
{
ubsan("\t\tIn File: %s:%i:%i", data->location.file, data->location.line, data->location.column);
ubsan("Negation overflow.");
}
void __ubsan_handle_pointer_overflow(struct overflow_data *data)
{
ubsan("\t\tIn File: %s:%i:%i", data->location.file, data->location.line, data->location.column);
ubsan("Pointer overflow.");
}
void __ubsan_handle_shift_out_of_bounds(struct shift_out_of_bounds_data *data)
{
ubsan("\t\tIn File: %s:%i:%i", data->location.file, data->location.line, data->location.column);
ubsan("Shift out of bounds.");
}
void __ubsan_handle_load_invalid_value(struct invalid_value_data *data)
{
ubsan("\t\tIn File: %s:%i:%i", data->location.file, data->location.line, data->location.column);
ubsan("Invalid load value.");
}
void __ubsan_handle_out_of_bounds(struct array_out_of_bounds_data *data)
{
ubsan("\t\tIn File: %s:%i:%i", data->location.file, data->location.line, data->location.column);
ubsan("Array out of bounds.");
}
void __ubsan_handle_vla_bound_not_positive(struct negative_vla_data *data)
{
ubsan("\t\tIn File: %s:%i:%i", data->location.file, data->location.line, data->location.column);
ubsan("Variable-length argument is negative.");
}
void __ubsan_handle_nonnull_return(struct nonnull_return_data *data)
{
ubsan("\t\tIn File: %s:%i:%i", data->location.file, data->location.line, data->location.column);
ubsan("Non-null return is null.");
}
void __ubsan_handle_nonnull_return_v1(struct nonnull_return_data *data)
{
ubsan("\t\tIn File: %s:%i:%i", data->location.file, data->location.line, data->location.column);
ubsan("Non-null return is null.");
}
void __ubsan_handle_nonnull_arg(struct nonnull_arg_data *data)
{
ubsan("\t\tIn File: %s:%i:%i", data->location.file, data->location.line, data->location.column);
ubsan("Non-null argument is null.");
}
void __ubsan_handle_builtin_unreachable(struct unreachable_data *data)
{
ubsan("\t\tIn File: %s:%i:%i", data->location.file, data->location.line, data->location.column);
ubsan("Unreachable code reached.");
}
void __ubsan_handle_invalid_builtin(struct invalid_builtin_data *data)
{
ubsan("\t\tIn File: %s:%i:%i", data->location.file, data->location.line, data->location.column);
ubsan("Invalid builtin.");
}
void __ubsan_handle_missing_return(struct unreachable_data *data)
{
ubsan("\t\tIn File: %s:%i:%i", data->location.file, data->location.line, data->location.column);
ubsan("Missing return.");
}
void __ubsan_vptr_type_cache(uintptr_t *cache, uintptr_t ptr)
{
ubsan("Vptr type cache.");
*cache = ptr;
}
void __ubsan_handle_dynamic_type_cache_miss(struct dynamic_type_cache_miss_data *data, uintptr_t ptr)
{
ubsan("\t\tIn File: %s:%i:%i", data->location.file, data->location.line, data->location.column);
ubsan("Dynamic type cache miss.");
UNUSED(ptr);
}
#endif

111
Kernel/core/ubsan.h Normal file
View File

@ -0,0 +1,111 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef __FENNIX_KERNEL_UBSAN_H__
#define __FENNIX_KERNEL_UBSAN_H__
#include <types.h>
struct source_location
{
const char *file;
uint32_t line;
uint32_t column;
};
struct type_descriptor
{
uint16_t kind;
uint16_t info;
char name[];
};
struct type_mismatch_v1_data
{
struct source_location location;
struct type_descriptor *type;
uint8_t alignment;
uint8_t type_check_kind;
};
struct out_of_bounds_info
{
struct source_location location;
struct type_descriptor left_type;
struct type_descriptor right_type;
};
struct overflow_data
{
struct source_location location;
struct type_descriptor *type;
};
struct negative_vla_data
{
struct source_location location;
struct type_descriptor *type;
};
struct invalid_value_data
{
struct source_location location;
struct type_descriptor *type;
};
struct nonnull_return_data
{
struct source_location location;
};
struct nonnull_arg_data
{
struct source_location location;
};
struct unreachable_data
{
struct source_location location;
};
struct invalid_builtin_data
{
struct source_location location;
uint8_t kind;
};
struct array_out_of_bounds_data
{
struct source_location location;
struct type_descriptor *array_type;
struct type_descriptor *index_type;
};
struct shift_out_of_bounds_data
{
struct source_location location;
struct type_descriptor *left_type;
struct type_descriptor *right_type;
};
struct dynamic_type_cache_miss_data
{
struct source_location location;
struct type_descriptor *type;
};
#endif // !__FENNIX_KERNEL_UBSAN_H__

View File

@ -0,0 +1,166 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <display.hpp>
#include <lock.hpp>
#include <uart.hpp>
#include <debug.h>
NewLock(PrintLock);
namespace Video
{
uint16_t Display::GetBitsPerPixel() { return this->framebuffer.BitsPerPixel; }
size_t Display::GetPitch() { return this->framebuffer.Pitch; }
void Display::ClearBuffer()
{
memset(this->Buffer, 0, this->Size);
// std::fill(this->DirtyMap.begin(), this->DirtyMap.end(), true);
}
__no_sanitize("undefined") void Display::SetPixel(uint32_t X,
uint32_t Y,
uint32_t Color)
{
if (unlikely(X >= this->Width))
X = this->Width - 1;
if (unlikely(Y >= this->Height))
Y = this->Height - 1;
uint32_t *Pixel = (uint32_t *)((uintptr_t)this->Buffer + (Y * this->Width + X) * (this->framebuffer.BitsPerPixel / 8));
*Pixel = Color;
// MarkRegionDirty(Y / RegionHeight, X / RegionWidth);
}
__no_sanitize("undefined") uint32_t Display::GetPixel(uint32_t X,
uint32_t Y)
{
if (unlikely(X >= this->Width))
X = this->Width - 1;
if (unlikely(Y >= this->Height))
Y = this->Height - 1;
uint32_t *Pixel = (uint32_t *)((uintptr_t)this->Buffer + (Y * this->Width + X) * (this->framebuffer.BitsPerPixel / 8));
return *Pixel;
}
__no_sanitize("undefined") void Display::DrawRectangle(uint32_t X,
uint32_t Y,
uint32_t Width,
uint32_t Height,
uint32_t Color)
{
for (uint32_t i = 0; i < Width; i++)
{
for (uint32_t j = 0; j < Height; j++)
{
uint32_t *Pixel =
(uint32_t *)((uintptr_t)this->Buffer + ((Y + j) *
this->Width +
(X + i)) *
(this->framebuffer.BitsPerPixel / 8));
*Pixel = Color;
}
}
}
void Display::UpdateBuffer()
{
if (!DirectWrite)
memcpy(this->framebuffer.BaseAddress, this->Buffer, this->Size);
// for (size_t i = 0; i < DirtyMap.size(); ++i)
// {
// if (DirtyMap[i])
// {
// size_t rRow = i / (framebuffer.Width / RegionWidth);
// size_t rCol = i % (framebuffer.Width / RegionWidth);
// UpdateRegion(rRow, rCol);
// DirtyMap[i] = false;
// }
// }
}
void Display::UpdateRegion(size_t RegionRow, size_t RegionColumn)
{
size_t startRow = RegionRow * RegionHeight;
size_t endRow = startRow + RegionHeight;
size_t startCol = RegionColumn * RegionWidth;
size_t endCol = startCol + RegionWidth;
uint8_t *framebufferPtr = (uint8_t *)framebuffer.BaseAddress;
uint8_t *bufferPtr = (uint8_t *)Buffer;
for (size_t row = startRow; row < endRow; ++row)
{
uint8_t *framebufferRowPtr = framebufferPtr + (row % framebuffer.Height) * framebuffer.Width * (framebuffer.BitsPerPixel / 8);
uint8_t *bufferRowPtr = bufferPtr + row * framebuffer.Width * (framebuffer.BitsPerPixel / 8);
for (size_t col = startCol; col < endCol; ++col)
{
uint8_t *framebufferPixelPtr = framebufferRowPtr + (col % framebuffer.Width) * (framebuffer.BitsPerPixel / 8);
uint8_t *bufferPixelPtr = bufferRowPtr + col * (framebuffer.BitsPerPixel / 8);
memcpy(framebufferPixelPtr, bufferPixelPtr, framebuffer.BitsPerPixel / 8);
}
}
/*
size_t startRow = RegionRow * RegionHeight;
size_t endRow = startRow + RegionHeight;
size_t startCol = RegionColumn * RegionWidth;
size_t endCol = startCol + RegionWidth;
for (size_t row = startRow; row < endRow; ++row)
{
for (size_t col = startCol; col < endCol; ++col)
{
size_t bufferIndex = row * framebuffer.Width + col;
size_t framebufferIndex = (row % framebuffer.Height) * framebuffer.Width + (col % framebuffer.Width);
memcpy((uint8_t *)framebuffer.BaseAddress + framebufferIndex * (framebuffer.BitsPerPixel / 8),
(uint8_t *)Buffer + bufferIndex * (framebuffer.BitsPerPixel / 8),
framebuffer.BitsPerPixel / 8);
}
}
*/
}
void Display::MarkRegionDirty(size_t RegionRow, size_t RegionCol)
{
size_t index = RegionRow * (framebuffer.Width / RegionWidth) + RegionCol;
DirtyMap[index] = true;
}
Display::Display(BootInfo::FramebufferInfo Info,
bool _DirectWrite)
: framebuffer(Info), DirectWrite(_DirectWrite)
{
Width = Info.Width;
Height = Info.Height;
Size = this->framebuffer.Pitch * Height;
if (DirectWrite)
Buffer = (void *)this->framebuffer.BaseAddress;
else
Buffer = KernelAllocator.RequestPages(TO_PAGES(Size));
// DirtyMap.resize((Width / RegionWidth) * (Height / RegionHeight), false);
}
Display::~Display() {}
}

View File

@ -0,0 +1,80 @@
/*
This file is part of Fennix Kernel.
Fennix Kernel is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
Fennix Kernel is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Fennix Kernel. If not, see <https://www.gnu.org/licenses/>.
*/
#include <display.hpp>
#include <debug.h>
#include <cstring>
namespace Video
{
Font::Font(uintptr_t *Start, uintptr_t *End, FontType Type)
{
trace("Initializing font with start %#lx and end %#lx Type: %d", Start, End, Type);
this->Info.StartAddress = Start;
this->Info.EndAddress = End;
this->Info.Type = Type;
size_t FontDataLength = End - Start;
if (Type == FontType::PCScreenFont2)
{
this->Info.PSF2Font = new PSF2_FONT;
PSF2_HEADER *font2 = (PSF2_HEADER *)KernelAllocator.RequestPages(TO_PAGES(FontDataLength + 1));
memcpy((void *)font2, Start, FontDataLength);
Memory::Virtual().Map((void *)font2, (void *)font2,
FontDataLength, Memory::PTFlag::RW);
if (font2->magic[0] != PSF2_MAGIC0 || font2->magic[1] != PSF2_MAGIC1 ||
font2->magic[2] != PSF2_MAGIC2 || font2->magic[3] != PSF2_MAGIC3)
{
error("Font2 magic mismatch.");
KernelAllocator.FreePages((void *)font2, TO_PAGES(FontDataLength + 1));
return;
}
this->Info.PSF2Font->Header = font2;
this->Info.PSF2Font->GlyphBuffer =
r_cst(void *, r_cst(uintptr_t, Start) + sizeof(PSF2_HEADER));
this->Info.Width = font2->width;
this->Info.Height = font2->height;
}
else if (Type == FontType::PCScreenFont1)
{
this->Info.PSF1Font = new PSF1_FONT;
PSF1_HEADER *font1 = (PSF1_HEADER *)Start;
if (font1->magic[0] != PSF1_MAGIC0 || font1->magic[1] != PSF1_MAGIC1)
error("Font1 magic mismatch.");
uint32_t glyphBufferSize = font1->charsize * 256;
if (font1->mode == 1) // 512 glyph mode
glyphBufferSize = font1->charsize * 512;
void *glyphBuffer =
r_cst(void *, r_cst(uintptr_t, Start) + sizeof(PSF1_HEADER));
this->Info.PSF1Font->Header = font1;
this->Info.PSF1Font->GlyphBuffer = glyphBuffer;
UNUSED(glyphBufferSize); // TODO: Use this in the future?
// TODO: Get font size.
this->Info.Width = 16;
this->Info.Height = 8;
}
}
Font::~Font()
{
}
}