kernel: add aarch64 architecture support

Signed-off-by: EnderIce2 <enderice2@protonmail.com>
This commit is contained in:
2025-01-10 17:26:26 +02:00
parent 07abdd9f6c
commit e6933acfb0
62 changed files with 1009 additions and 299 deletions

View File

@ -194,10 +194,14 @@ namespace ACPI
if (FADT)
{
#if defined(__amd64__) || defined(__i386__)
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();
#elif defined(__aarch64__)
warn("aarch64 is not supported yet");
#endif
}
}

View File

@ -53,7 +53,7 @@ namespace CPU
memcpy(Vendor + 4, &edx, 4);
memcpy(Vendor + 8, &ecx, 4);
#elif defined(__aarch64__)
#error "Not implemented"
#warning "aarch64 not implemented"
#endif
return Vendor;
}
@ -98,7 +98,7 @@ namespace CPU
memcpy(Name + 40, &ecx, 4);
memcpy(Name + 44, &edx, 4);
#elif defined(__aarch64__)
#error "Not implemented"
#warning "aarch64 not implemented"
#endif
return Name;
}
@ -141,7 +141,7 @@ namespace CPU
memcpy(Hypervisor + 4, &ecx, 4);
memcpy(Hypervisor + 8, &edx, 4);
#elif defined(__aarch64__)
#error "Not implemented"
#warning "aarch64 not implemented"
#endif
return Hypervisor;
}
@ -164,9 +164,8 @@ namespace CPU
: "=r"(Flags));
return Flags & (1 << 9);
#elif defined(__aarch64__)
asmv("mrs %0, cpsr"
: "=r"(Flags));
return Flags & (1 << 7);
#warning "aarch64 not implemented"
return 0;
#endif
}
case Enable:
@ -174,7 +173,7 @@ namespace CPU
#if defined(__amd64__) || defined(__i386__)
asmv("sti");
#elif defined(__aarch64__)
asmv("cpsie i");
#warning "aarch64 not implemented"
#endif
return true;
}
@ -183,7 +182,7 @@ namespace CPU
#if defined(__amd64__) || defined(__i386__)
asmv("cli");
#elif defined(__aarch64__)
asmv("cpsid i");
#warning "aarch64 not implemented"
#endif
return true;
}
@ -225,10 +224,7 @@ namespace CPU
if (PT)
{
asmv("msr ttbr0_el1, %0"
:
: "r"(PT)
: "memory");
#warning "aarch64 not implemented"
}
#endif
return ret;
@ -275,6 +271,7 @@ namespace CPU
void InitializeFeatures(int Core)
{
#if defined(__amd64__) || defined(__i386__)
static int BSP = 0;
SupportedFeat feat = GetCPUFeat();
@ -386,6 +383,7 @@ namespace CPU
SSEEnabled = true;
debug("SSE support enabled.");
}
#endif
}
uint64_t Counter()
@ -399,8 +397,8 @@ namespace CPU
"=d"(edx));
Counter = ((uint64_t)eax) | (((uint64_t)edx) << 32);
#elif defined(__aarch64__)
asmv("mrs %0, cntvct_el0"
: "=r"(Counter));
#warning "aarch64 not implemented"
Counter = 0;
#endif
return Counter;
}

View File

@ -76,17 +76,15 @@ namespace v0
cs = Flags & (1 << 9);
asmv("cli");
#elif defined(__arm__) || defined(__aarch64__)
return cs;
uintptr_t Flags;
asmv("mrs %0, cpsr"
: "=r"(Flags));
cs = Flags & (1 << 7);
asmv("cpsid i");
#elif defined(__aarch64__)
stub;
return 0;
#endif
return cs;
}
void LeaveCriticalSection(dev_t DriverID, CriticalState PreviousState)
@ -101,7 +99,7 @@ namespace v0
#elif defined(__arm__) || defined(__aarch64__)
if (PreviousState)
asmv("cpsie i");
stub;
#endif
}
@ -284,15 +282,18 @@ namespace v0
{
dbg_api("%d, %d", DriverID, IRQ);
#if defined(__amd64__) || defined(__i386__)
if (IRQ >= 8)
outb(PIC2_CMD, _PIC_EOI);
outb(PIC1_CMD, _PIC_EOI);
#endif
}
void IRQ_MASK(dev_t DriverID, uint8_t IRQ)
{
dbg_api("%d, %d", DriverID, IRQ);
#if defined(__amd64__) || defined(__i386__)
uint16_t port;
uint8_t value;
@ -306,12 +307,14 @@ namespace v0
value = inb(port) | (1 << IRQ);
outb(port, value);
#endif
}
void IRQ_UNMASK(dev_t DriverID, uint8_t IRQ)
{
dbg_api("%d, %d", DriverID, IRQ);
#if defined(__amd64__) || defined(__i386__)
uint16_t port;
uint8_t value;
@ -325,12 +328,14 @@ namespace v0
value = inb(port) & ~(1 << IRQ);
outb(port, value);
#endif
}
void PS2Wait(dev_t DriverID, const bool Output)
{
dbg_api("%d, %d", DriverID, Output);
#if defined(__amd64__) || defined(__i386__)
int Timeout = 100000;
PS2_STATUSES Status = {.Raw = inb(PS2_STATUS)};
while (Timeout--)
@ -349,38 +354,51 @@ namespace v0
}
warn("PS/2 controller timeout! (Status: %#x, %d)", Status, Output);
#endif
}
void PS2WriteCommand(dev_t DriverID, uint8_t Command)
{
dbg_api("%d, %d", DriverID, Command);
#if defined(__amd64__) || defined(__i386__)
WaitInput;
outb(PS2_CMD, Command);
#endif
}
void PS2WriteData(dev_t DriverID, uint8_t Data)
{
dbg_api("%d, %d", DriverID, Data);
#if defined(__amd64__) || defined(__i386__)
WaitInput;
outb(PS2_DATA, Data);
#endif
}
uint8_t PS2ReadData(dev_t DriverID)
{
dbg_api("%d", DriverID);
#if defined(__amd64__) || defined(__i386__)
WaitOutput;
return inb(PS2_DATA);
#elif defined(__aarch64__)
return 0;
#endif
}
uint8_t PS2ReadStatus(dev_t DriverID)
{
dbg_api("%d", DriverID);
#if defined(__amd64__) || defined(__i386__)
WaitOutput;
return inb(PS2_STATUS);
#elif defined(__aarch64__)
return 0;
#endif
}
uint8_t PS2ReadAfterACK(dev_t DriverID)
@ -388,11 +406,13 @@ namespace v0
dbg_api("%d", DriverID);
uint8_t ret = PS2ReadData(DriverID);
#if defined(__amd64__) || defined(__i386__)
while (ret == PS2_ACK)
{
WaitOutput;
ret = inb(PS2_DATA);
}
#endif
return ret;
}
@ -400,6 +420,7 @@ namespace v0
{
dbg_api("%d", DriverID);
#if defined(__amd64__) || defined(__i386__)
PS2_STATUSES Status;
int timeout = 0x500;
while (timeout--)
@ -409,6 +430,7 @@ namespace v0
return;
inb(PS2_DATA);
}
#endif
}
int PS2ACKTimeout(dev_t DriverID)

View File

@ -61,6 +61,7 @@ namespace ACPI
debug("SCI Handle Triggered");
uint16_t Event = 0;
{
#if defined(__amd64__) || defined(__i386__)
uint16_t a = 0, b = 0;
if (acpi->FADT->PM1aEventBlock)
{
@ -73,6 +74,7 @@ namespace ACPI
outw(s_cst(uint16_t, acpi->FADT->PM1bEventBlock), b);
}
Event = a | b;
#endif
}
#ifdef DEBUG
@ -161,6 +163,7 @@ namespace ACPI
return;
}
#if defined(__amd64__) || defined(__i386__)
if (inw(s_cst(uint16_t, PM1a_CNT) & SCI_EN) == 0)
{
KPrint("ACPI was disabled, enabling...");
@ -195,6 +198,9 @@ namespace ACPI
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);
#elif defined(__aarch64__)
warn("ACPI Shutdown not supported");
#endif
}
void DSDT::Reboot()
@ -209,7 +215,11 @@ namespace ACPI
}
case ACPI_GAS_IO:
{
#if defined(__amd64__) || defined(__i386__)
outb(s_cst(uint16_t, acpi->FADT->ResetReg.Address), acpi->FADT->ResetValue);
#elif defined(__aarch64__)
warn("ACPI_GAS_IO not supported");
#endif
break;
}
case ACPI_GAS_PCI:
@ -294,6 +304,7 @@ namespace ACPI
KPrint("ACPI Shutdown is supported");
ACPIShutdownSupported = true;
#if defined(__amd64__) || defined(__i386__)
{
const uint16_t value = /*ACPI_TIMER |*/ ACPI_BUSMASTER | ACPI_GLOBAL |
ACPI_POWER_BUTTON | ACPI_SLEEP_BUTTON | ACPI_RTC_ALARM |
@ -322,6 +333,7 @@ namespace ACPI
}
((APIC::APIC *)Interrupts::apic[0])->RedirectIRQ(0, uint8_t(acpi->FADT->SCI_Interrupt), 1);
#endif
return;
}
warn("Failed to parse _S5_ in ACPI");

View File

@ -110,6 +110,8 @@ namespace Interrupts
/* APIC::APIC */ void *apic[MAX_CPU] = {nullptr};
/* APIC::Timer */ void *apicTimer[MAX_CPU] = {nullptr};
#elif defined(__aarch64__)
/* APIC::APIC */ void *apic[MAX_CPU] = {nullptr};
/* APIC::Timer */ void *apicTimer[MAX_CPU] = {nullptr};
#endif
void Initialize(int Core)
@ -264,6 +266,7 @@ namespace Interrupts
nsa inline void ReturnFromInterrupt()
{
#if defined(__amd64__) || defined(__i386__)
CPUData *CoreData = GetCurrentCPU();
int Core = CoreData->ID;
@ -299,8 +302,9 @@ namespace Interrupts
}
else
fixme("APIC not found for core %d", Core);
// TODO: Handle PIC too
// TODO: Handle PIC too
#endif
assert(!"EOI not handled.");
CPU::Stop();
}
@ -317,24 +321,23 @@ namespace Interrupts
{
#if defined(__amd64__) || defined(__i386__)
asmv("mov %%cr3, %0" : "=r"(Original));
#endif
if (likely(Original == KernelPageTable))
return;
#if defined(__amd64__) || defined(__i386__)
asmv("mov %0, %%cr3" : : "r"(KernelPageTable));
#endif
}
~AutoSwitchPageTable()
{
#if defined(__amd64__) || defined(__i386__)
if (likely(Original == KernelPageTable))
return;
#if defined(__amd64__) || defined(__i386__)
asmv("mov %0, %%cr3" : : "r"(Original));
#endif
}
} SwitchPageTable;
#if defined(__amd64__) || defined(__i386__)
CPU::TrapFrame *Frame = (CPU::TrapFrame *)Data;
// debug("IRQ%ld", Frame->InterruptNumber - 32);
@ -378,10 +381,12 @@ namespace Interrupts
}
ReturnFromInterrupt();
#endif
}
extern "C" nsa void SchedulerInterruptHandler(void *Data)
{
#if defined(__amd64__) || defined(__i386__)
KernelPageTable->Update();
CPU::SchedulerFrame *Frame = (CPU::SchedulerFrame *)Data;
#if defined(__amd64__) || defined(__i386__)
@ -411,6 +416,7 @@ namespace Interrupts
Handler *hnd = (Handler *)it->Data;
hnd->OnInterruptReceived(Frame);
ReturnFromInterrupt();
#endif
}
Handler::Handler(int InterruptNumber, bool Critical)
@ -469,11 +475,15 @@ namespace Interrupts
void Handler::OnInterruptReceived(CPU::TrapFrame *Frame)
{
#if defined(__amd64__) || defined(__i386__)
debug("Unhandled interrupt %d", Frame->InterruptNumber);
#endif
}
void Handler::OnInterruptReceived(CPU::SchedulerFrame *Frame)
{
#if defined(__amd64__) || defined(__i386__)
debug("Unhandled scheduler interrupt %d", Frame->InterruptNumber);
#endif
}
}

View File

@ -194,6 +194,7 @@ namespace Memory
bool VirtualMemoryArea::HandleCoW(uintptr_t PFA)
{
func("%#lx", PFA);
#if defined(__amd64__) || defined(__i386__)
Virtual vmm(this->Table);
PageTableEntry *pte = vmm.GetPTE((void *)PFA);
@ -252,6 +253,9 @@ namespace Memory
}
}
#else
#warning "Not implemented"
#endif
debug("%#lx not found in CoW regions", PFA);
return false;
}
@ -325,7 +329,7 @@ namespace Memory
this->Map(AddressToMap, RealAddress, PAGE_SIZE, Flags);
MgrLock.Lock(__FUNCTION__);
#else
#error "Not implemented"
#warning "Not implemented"
#endif
}

View File

@ -185,8 +185,10 @@ nsa __noreturn void HandleUnrecoverableException(CPU::ExceptionFrame *Frame)
CPU::Pause();
ExPrint("\x1b[0m-----------------------------------------------\n");
#if defined(__amd64__) || defined(__i386__)
ExPrint("\x1b[30;41mUnrecoverable exception %#lx on CPU %d\n",
Frame->InterruptNumber, core->ID);
#endif
#if defined(__amd64__) || defined(__i386__)
ExPrint("CR0=%#lx CR2=%#lx CR3=%#lx CR4=%#lx CR8=%#lx\n",
Frame->cr0, Frame->cr2, Frame->cr3, Frame->cr4, Frame->cr8);
@ -222,7 +224,9 @@ nsa __noreturn void HandleUnrecoverableException(CPU::ExceptionFrame *Frame)
#endif /* a86 */
Display->UpdateBuffer();
#if defined(__amd64__) || defined(__i386__)
error("Unrecoverable Exception: %#lx", Frame->InterruptNumber);
#endif
UnrecoverableLock.store(false, std::memory_order_release);
CPU::Stop();
@ -231,6 +235,7 @@ nsa __noreturn void HandleUnrecoverableException(CPU::ExceptionFrame *Frame)
nsa __noreturn void HandleExceptionInsideException(CPU::ExceptionFrame *Frame)
{
ExPrint("\x1b[0m-----------------------------------------------\n");
#if defined(__amd64__) || defined(__i386__)
ExPrint("Exception inside exception: %#lx at %#lx\n",
Frame->InterruptNumber,
#if defined(__amd64__)
@ -240,6 +245,7 @@ nsa __noreturn void HandleExceptionInsideException(CPU::ExceptionFrame *Frame)
#elif defined(__aarch64__)
Frame->pc);
#endif
#endif // __amd64__ || __i386__
#if defined(__amd64__) || defined(__i386__)
ExPrint("CR0=%#lx CR2=%#lx CR3=%#lx CR4=%#lx CR8=%#lx\n",
Frame->cr0, Frame->cr2, Frame->cr3, Frame->cr4, Frame->cr8);
@ -286,6 +292,8 @@ nsa void HandleException(CPU::ExceptionFrame *Frame)
CPU::PageTable(KernelPageTable);
InitFont();
#if defined(__amd64__) || defined(__i386__)
/* First check the exception */
if (unlikely(Frame->InterruptNumber == CPU::x86::DoubleFault))
{
@ -343,6 +351,7 @@ nsa void HandleException(CPU::ExceptionFrame *Frame)
ExceptionExit:
ExceptionLock.store(false, std::memory_order_release);
#endif
}
nsa void BaseBufferStackError(bool Stack)

View File

@ -65,7 +65,9 @@ __no_sanitize("undefined") nsa bool CrashUHCIKeyboardDriver::Initialize()
/* FIXME: stub */
debug("Initializing controller");
#if defined(__amd64__) || defined(__i386__)
outw((uint16_t)((uintptr_t)io + 0xC0), 0x8F00); /* Disable Legacy Mode Support */
#endif
/* Disable All Interrupts */
io->USBINTR.TOCRC = 0;

View File

@ -137,9 +137,7 @@ nsa const char *ExGetKSymbol(CPU::ExceptionFrame *Frame)
Frame->eip > (uintptr_t)&_kernel_end)
return "<OUTSIDE KERNEL>";
#elif defined(__aarch64__)
if (Frame->pc < (uintptr_t)&_kernel_start &&
Frame->pc > (uintptr_t)&_kernel_end)
return "<OUTSIDE KERNEL>";
#warning "aarch64 not implemented"
#endif
#if defined(__amd64__)
@ -147,7 +145,8 @@ nsa const char *ExGetKSymbol(CPU::ExceptionFrame *Frame)
#elif defined(__i386__)
return ExGetKSymbolByAddress(Frame->eip);
#elif defined(__aarch64__)
return ExGetKSymbolByAddress(Frame->pc);
#warning "aarch64 not implemented"
return "stub";
#endif
}
@ -302,9 +301,16 @@ nsa void DisplayMainScreen(CPU::ExceptionFrame *Frame)
x86Exceptions[Frame->InterruptNumber].Name,
x86Exceptions[Frame->InterruptNumber].Mnemonic,
#elif defined(__aarch64__)
#error "AA64 not implemented"
"stub",
"stub",
#warning "aarch64 not implemented"
#endif
#if defined(__amd64__) || defined(__i386__)
Frame->InterruptNumber);
#elif defined(__aarch64__)
0);
#warning "aarch64 not implemented"
#endif
#if defined(__amd64__) || defined(__i386__)
ExPrint("Cause: %s\n", x86Exceptions[Frame->InterruptNumber].Cause);
#endif
@ -315,7 +321,8 @@ nsa void DisplayMainScreen(CPU::ExceptionFrame *Frame)
#elif defined(__i386__)
Frame->eip);
#elif defined(__aarch64__)
Frame->pc);
0);
#warning "aarch64 not implemented"
#endif
CPUData *core = GetCurrentCPU();
@ -342,7 +349,7 @@ arch void DisplayDetailsScreen(CPU::ExceptionFrame *Frame);
nsa void DisplayStackScreen(CPU::ExceptionFrame *Frame)
{
Memory::Virtual vmm;
struct StackFrame *sf;
struct StackFrame *sf = nullptr;
#if defined(__amd64__)
sf = (struct StackFrame *)Frame->rbp;
#elif defined(__i386__)
@ -353,6 +360,7 @@ nsa void DisplayStackScreen(CPU::ExceptionFrame *Frame)
if (!vmm.Check(sf))
{
#if defined(__amd64__) || defined(__i386__)
void *ptr = ((Memory::PageTable *)Frame->cr3)->Get(sf);
debug("Virtual pointer %#lx -> %#lx", sf, ptr);
if (vmm.Check(ptr))
@ -362,17 +370,18 @@ nsa void DisplayStackScreen(CPU::ExceptionFrame *Frame)
ExPrint("\x1b[31m< No stack trace available. >\x1b[0m\n");
return;
}
#endif
}
/* FIXME: Get symbol offset more efficiently */
uintptr_t fIP;
uintptr_t fIP = 0;
#if defined(__amd64__)
fIP = Frame->rip;
#elif defined(__i386__)
fIP = Frame->eip;
#elif defined(__aarch64__)
fIP = Frame->pc;
#warning "aarch64 not implemented"
#endif
ExPrint("%p", (void *)fIP);
@ -557,6 +566,7 @@ nsa void DisplayCrashScreen(CPU::ExceptionFrame *Frame)
DisplayBottomOverlay();
#ifdef DEBUG
#if defined(__amd64__) || defined(__i386__)
static int once = 0;
static uint8_t com4 = 0xFF;
if (!once++)
@ -657,6 +667,7 @@ nsa void DisplayCrashScreen(CPU::ExceptionFrame *Frame)
ExPrint(keyBuf);
}
#endif
#endif // DEBUG
debug("cpu halted");
CPU::Halt(true);
}
@ -965,8 +976,10 @@ nsa void UserInput(char *Input)
#ifdef DEBUG
else if (strcmp(Input, "pt") == 0)
{
/* Helpful for qemu "info tlb" command */
/* Helpful for qemu "info tlb" command */
#if defined(__amd64__) || defined(__i386__)
CPU::PageTable((void *)ExFrame->cr3);
#endif
ExPrint("Here be dragons\n");
}
else if (strcmp(Input, "ps") == 0)

View File

@ -86,6 +86,7 @@ nsa bool UserModeExceptionHandler(CPU::ExceptionFrame *Frame)
#endif
int sigRet = -1;
#if defined(__amd64__) || defined(__i386__)
switch (Frame->InterruptNumber)
{
case CPU::x86::PageFault:
@ -153,6 +154,7 @@ nsa bool UserModeExceptionHandler(CPU::ExceptionFrame *Frame)
break;
}
}
#endif
if (sigRet == 0)
{

View File

@ -32,6 +32,7 @@ namespace Power
if (((ACPI::DSDT *)this->dsdt)->ACPIShutdownSupported)
((ACPI::DSDT *)this->dsdt)->Reboot();
#if defined(__amd64__) || defined(__i386__)
asmv("cli");
uint8_t temp;
do
@ -41,6 +42,9 @@ namespace Power
inb(0x60);
} while (((temp) & (1 << (1))) != 0);
outb(0x64, 0xFE);
#elif defined(__aarch64__)
warn("aarch64 is not supported yet");
#endif
}
void Power::Shutdown()
@ -55,9 +59,11 @@ namespace Power
}
/* FIXME: Detect emulators and use their shutdown methods */
#ifdef DEBUG
#if defined(__amd64__) || defined(__i386__)
outl(0xB004, 0x2000); // for qemu
outl(0x604, 0x2000); // if qemu not working, bochs and older versions of qemu
outl(0x4004, 0x3400); // virtual box
#endif
#endif
}

View File

@ -67,8 +67,7 @@ EXTERNC __noreturn __no_stack_protector void __stack_chk_fail(void)
asmv("movl %%esp, %0"
: "=r"(Stack));
#elif defined(__aarch64__)
asmv("mov %%sp, %0"
: "=r"(Stack));
#warning "aarch64 not implemented"
#endif
error("Stack address: %#lx", Stack);

View File

@ -36,16 +36,16 @@ public:
asmv("mov %0, %%cr3"
:
: "r"(KernelPageTable));
#endif
debug(" + %#lx %s(%d)", Original,
thisProcess->Name, thisProcess->ID);
#endif
}
~AutoSwitchPageTable()
{
#if defined(__amd64__) || defined(__i386__)
debug("- %#lx %s(%d)", Original,
thisProcess->Name, thisProcess->ID);
#if defined(__amd64__) || defined(__i386__)
asmv("mov %0, %%cr3"
:
: "r"(Original));

View File

@ -48,6 +48,8 @@ namespace Time
return mminq(&hpet->MainCounterValue);
#elif defined(__i386__)
return mminl(&hpet->MainCounterValue);
#else
return 0;
#endif
}
@ -57,6 +59,8 @@ namespace Time
return mminq(&hpet->MainCounterValue) + (Target * ConvertUnit(Unit)) / clk;
#elif defined(__i386__)
return mminl(&hpet->MainCounterValue) + (Target * ConvertUnit(Unit)) / clk;
#else
return 0;
#endif
}
@ -69,6 +73,8 @@ namespace Time
Subtraction *= ConvertUnit(Units::Nanoseconds);
return uint64_t(Subtraction / this->clk);
#else
return 0;
#endif
}

View File

@ -33,6 +33,8 @@ namespace Time
while (this->GetCounter() < Target)
CPU::Pause();
return true;
#elif defined(__aarch64__)
return 0;
#endif
}
@ -40,6 +42,8 @@ namespace Time
{
#if defined(__amd64__) || defined(__i386__)
return CPU::Counter();
#elif defined(__aarch64__)
return 0;
#endif
}
@ -47,6 +51,8 @@ namespace Time
{
#if defined(__amd64__) || defined(__i386__)
return uint64_t((this->GetCounter() + (Target * ConvertUnit(Unit))) / this->clk);
#elif defined(__aarch64__)
return 0;
#endif
}
@ -54,6 +60,8 @@ namespace Time
{
#if defined(__amd64__) || defined(__i386__)
return uint64_t((this->GetCounter() - this->ClassCreationTime) / this->clk);
#elif defined(__aarch64__)
return 0;
#endif
}

View File

@ -60,6 +60,8 @@ namespace UART
while ((inb(s_cst(uint16_t, COM1 + 5)) & 1) == 0)
;
return inb(COM1);
#else
return 0;
#endif
}
@ -82,6 +84,8 @@ namespace UART
while ((inb(s_cst(uint16_t, COM4 + 5)) & 1) == 0)
;
return inb(COM4);
#else
return 0;
#endif
}