[metal] Add a uprintf() routine, for non-emergency boot logging (#905)

* [metal] Add a uprintf() routine, for non-emergency boot logging
* [metal] _Really_ push forward timing of VGA TTY initialization
* [metal] Do something useful with uprintf()
* [metal] Locate some ACPI tables, for later hardware detection

Specifically the code now tries to find the ACPI RSDP,
RSDT/XSDT, FADT, & MADT tables, whether in legacy BIOS
bootup mode or in a UEFI bootup.  These are useful for
figuring out how to (re)enable asynchronous interrupts
in legacy 8259 PIC mode.
This commit is contained in:
tkchia 2023-10-26 05:32:20 +08:00 committed by GitHub
parent 062b2d776e
commit ed17d3008b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 999 additions and 36 deletions

View file

@ -170,11 +170,13 @@ include third_party/puff/puff.mk # │
include libc/elf/elf.mk # │
include ape/ape.mk # │
include libc/fmt/fmt.mk # │
include libc/vga/vga.mk #─┘
include libc/vga/vga.mk # │
include libc/irq/irq.mk #─┘
include libc/calls/calls.mk #─┐
include third_party/nsync/nsync.mk # │
include libc/runtime/runtime.mk # ├──SYSTEMS RUNTIME
include third_party/double-conversion/dc.mk # │ You can issue system calls
include libc/irq/irq.mk # ├──SYSTEMS RUNTIME
include third_party/nsync/nsync.mk # │ You can issue system calls
include libc/runtime/runtime.mk # │
include third_party/double-conversion/dc.mk # │
include libc/crt/crt.mk # │
include third_party/dlmalloc/dlmalloc.mk #─┘
include libc/mem/mem.mk #─┐
@ -374,6 +376,7 @@ COSMOPOLITAN_OBJECTS = \
LIBC_RUNTIME \
THIRD_PARTY_NSYNC \
LIBC_ELF \
LIBC_IRQ \
LIBC_CALLS \
LIBC_SYSV_CALLS \
LIBC_VGA \

View file

@ -46,6 +46,7 @@ EXAMPLES_DIRECTDEPS = \
LIBC_DNS \
LIBC_FMT \
LIBC_INTRIN \
LIBC_IRQ \
LIBC_LOG \
LIBC_MEM \
LIBC_NEXGEN32E \

View file

@ -26,6 +26,8 @@
__static_yoink("vga_console");
__static_yoink("_idt");
__static_yoink("_AcpiMadtFlags");
__static_yoink("_AcpiBootFlags");
__static_yoink("EfiMain");
int main(int argc, char *argv[]) {

View file

@ -30,6 +30,7 @@
#include "libc/calls/internal.h"
#include "libc/calls/metalfile.internal.h"
#include "libc/intrin/directmap.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/weaken.h"
#include "libc/macros.internal.h"
#include "libc/mem/mem.h"
@ -74,6 +75,7 @@ textstartup void InitializeMetalFile(void) {
memcpy(copied_base, (void *)(BANE + IMAGE_BASE_PHYSICAL), size);
__ape_com_base = copied_base;
__ape_com_size = size;
KINFOF("%s @ %p,+%#zx", APE_COM_NAME, copied_base, size);
}
}

View file

@ -27,12 +27,12 @@
#include "libc/macros.internal.h"
#include "libc/calls/metalfile.internal.h"
.init.start 101,_init_metalfile
.init.start 102,_init_metalfile
push %rdi
push %rsi
call InitializeMetalFile
pop %rsi
pop %rdi
.init.end 101,_init_metalfile
.init.end 102,_init_metalfile
APE_COM_NAME:
.endobj APE_COM_NAME,globl,hidden

View file

@ -728,6 +728,8 @@ void abort(void) wontreturn;
#endif
#define __weak_reference(sym, alias) \
__weak_reference_impl(sym, alias)
#define __weak_reference_impl(sym, alias) \
__asm__(".weak\t" #alias "\n\t" \
".equ\t" #alias ", " #sym "\n\t" \
".type\t" #alias ",@notype")

View file

@ -25,7 +25,9 @@
OTHER DEALINGS IN THE SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/intrin/kprintf.h"
#include "libc/macros.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/runtime/pc.internal.h"
// Code and data structures for bare metal interrupt handling.

View file

@ -385,14 +385,29 @@ privileged long kloghandle(void) {
return __klog_handle;
}
privileged void _klog_serial(const char *b, size_t n) {
size_t i;
uint16_t dx;
unsigned char al;
for (i = 0; i < n; ++i) {
for (;;) {
dx = 0x3F8 + UART_LSR;
asm("inb\t%1,%0" : "=a"(al) : "dN"(dx));
if (al & UART_TTYTXR) break;
asm("pause");
}
dx = 0x3F8;
asm volatile("outb\t%0,%1"
: /* no inputs */
: "a"(b[i]), "dN"(dx));
}
}
privileged void klog(const char *b, size_t n) {
#ifdef __x86_64__
int e;
long h;
size_t i;
uint16_t dx;
uint32_t wrote;
unsigned char al;
long rax, rdi, rsi, rdx;
if ((h = kloghandle()) == -1) {
return;
@ -407,18 +422,7 @@ privileged void klog(const char *b, size_t n) {
if (_weaken(_klog_vga)) {
_weaken(_klog_vga)(b, n);
}
for (i = 0; i < n; ++i) {
for (;;) {
dx = 0x3F8 + UART_LSR;
asm("inb\t%1,%0" : "=a"(al) : "dN"(dx));
if (al & UART_TTYTXR) break;
asm("pause");
}
dx = 0x3F8;
asm volatile("outb\t%0,%1"
: /* no inputs */
: "a"(b[i]), "dN"(dx));
}
_klog_serial(b, n);
} else {
asm volatile("syscall"
: "=a"(rax), "=D"(rdi), "=S"(rsi), "=d"(rdx)
@ -1152,3 +1156,6 @@ privileged void kprintf(const char *fmt, ...) {
kvprintf(fmt, v);
va_end(v);
}
__weak_reference(kprintf, uprintf);
__weak_reference(kvprintf, uvprintf);

View file

@ -9,25 +9,49 @@
#define kvsnprintf __kvsnprintf
#define kloghandle __kloghandle
#define kisdangerous __kisdangerous
#define uprintf __uprintf
#define uvprintf __uvprintf
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
void kprintf(const char *, ...);
size_t ksnprintf(char *, size_t, const char *, ...);
void kvprintf(const char *, va_list);
size_t kvsnprintf(char *, size_t, const char *, va_list);
bool kisdangerous(const void *);
void kprintf(const char *, ...);
size_t ksnprintf(char *, size_t, const char *, ...);
void kvprintf(const char *, va_list);
size_t kvsnprintf(char *, size_t, const char *, va_list);
bool kisdangerous(const void *);
void klog(const char *, size_t);
void _klog_serial(const char *, size_t);
long kloghandle(void);
void uprintf(const char *, ...);
void uvprintf(const char *, va_list);
#ifndef TINY
#define KINFOF(FMT, ...) \
do { \
uprintf("\r\e[35m%s:%d: " FMT "\e[0m\n", \
__FILE__, __LINE__, ## __VA_ARGS__); \
} while (0)
#define KWARNF(FMT, ...) \
do { \
uprintf("\r\e[94;49mwarn: %s:%d: " FMT "\e[0m\n", \
__FILE__, __LINE__, ## __VA_ARGS__); \
} while (0)
#else
#define KINFOF(FMT, ...) ((void)0)
#define KWARNF(FMT, ...) ((void)0)
#endif
#define KDIEF(FMT, ...) \
do { \
kprintf("\r\e[30;101mfatal: %s:%d: " FMT "\e[0m\n", \
__FILE__, __LINE__, ## __VA_ARGS__); \
for (;;) asm volatile("cli\n\thlt"); \
} while (0)
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* _COSMO_SOURCE */

43
libc/irq/acpi-fadt-init.S Normal file
View file

@ -0,0 +1,43 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/irq/acpi.internal.h"
#include "libc/macros.internal.h"
#include "libc/runtime/pc.internal.h"
.init.start 312,_init_acpi_fadt
push %rdi
push %rsi
call _AcpiFadtInit
pop %rsi
pop %rdi
.init.end 312,_init_acpi_fadt
.data
_AcpiBootFlags:
.short kAcpiFadtLegacyDevices | kAcpiFadt8042
.endobj _AcpiBootFlags,globl
.previous

80
libc/irq/acpi-fadt.c Normal file
View file

@ -0,0 +1,80 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/intrin/kprintf.h"
#include "libc/irq/acpi.internal.h"
#ifdef __x86_64__
textstartup static void _AcpiDsdtInit(uintptr_t dsdt_phy) {
const AcpiTableDsdt *dsdt;
size_t length;
if (!dsdt_phy) {
KWARNF("FADT: no DSDT");
return;
}
dsdt = _AcpiMapTable(dsdt_phy);
KINFOF("FADT: DSDT @ %p", dsdt);
length = dsdt->Header.Length;
if (length <= offsetof(AcpiTableDsdt, Aml)) {
KWARNF("DSDT: no AML?");
return;
}
/* TODO: parse AML to discover hardware configuration */
}
textstartup void _AcpiFadtInit(void) {
if (IsMetal()) {
const AcpiTableFadt *fadt;
size_t length;
uint16_t flags;
uintptr_t dsdt_phy = 0;
if (!_AcpiSuccess(_AcpiGetTable("FACP", 0, (void **)&fadt))) {
KINFOF("no FADT found");
return;
}
length = fadt->Header.Length;
KINFOF("FADT @ %p,+%#zx", fadt, length);
_Static_assert(offsetof(AcpiTableFadt, Dsdt) == 40);
_Static_assert(offsetof(AcpiTableFadt, BootFlags) == 109);
_Static_assert(offsetof(AcpiTableFadt, XDsdt) == 140);
if (length >= offsetof(AcpiTableFadt, BootFlags) + sizeof(fadt->BootFlags))
{
_AcpiBootFlags = flags = fadt->BootFlags;
KINFOF("FADT: boot flags %#x", (unsigned)flags);
}
if (length >= offsetof(AcpiTableFadt, XDsdt) + sizeof(fadt->XDsdt) &&
fadt->XDsdt) {
dsdt_phy = fadt->XDsdt;
} else if (length >= offsetof(AcpiTableFadt, Dsdt) + sizeof(fadt->Dsdt)) {
dsdt_phy = fadt->Dsdt;
}
_AcpiDsdtInit(dsdt_phy);
}
}
#endif /* __x86_64__ */

51
libc/irq/acpi-madt-init.S Normal file
View file

@ -0,0 +1,51 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/irq/acpi.internal.h"
#include "libc/macros.internal.h"
#include "libc/runtime/pc.internal.h"
.init.start 311,_init_acpi_madt
push %rdi
push %rsi
call _AcpiMadtInit
pop %rsi
pop %rdi
.init.end 311,_init_acpi_madt
.data
_AcpiMadtFlags:
.long kAcpiMadtPcAtCompat
.endobj _AcpiMadtFlags,globl
.previous
.bss
_AcpiNumIoApics:
.skip 8
.endobj _AcpiNumIoApics,globl
_AcpiIoApics:
.skip 8
.endobj _AcpiIoApics,globl
.previous

81
libc/irq/acpi-madt.c Normal file
View file

@ -0,0 +1,81 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/inttypes.h"
#include "libc/irq/acpi.internal.h"
#ifdef __x86_64__
textstartup void _AcpiMadtInit(void) {
if (IsMetal()) {
const AcpiTableMadt *madt;
size_t length, num_io_apics = 0;
const char *madt_end, *p;
const AcpiSubtableHeader *h;
const AcpiMadtIoApic **icp;
uint32_t flags;
if (!_AcpiSuccess(_AcpiGetTable("APIC", 0, (void **)&madt))) {
KINFOF("no MADT found");
return;
}
length = madt->Header.Length;
if (length < sizeof(*madt)) {
KDIEF("MADT has short length %#zx", length);
}
_AcpiMadtFlags = flags = madt->Flags;
KINFOF("MADT @ %p, flags %#" PRIx32, madt, flags);
madt_end = (char *)madt + length;
p = madt->Subtable;
while (p != madt_end) {
h = (const AcpiSubtableHeader *)p;
switch (h->Type) {
case kAcpiMadtIoApic:
++num_io_apics;
}
p += h->Length;
}
KINFOF("MADT: %zu I/O APIC(s)", num_io_apics);
icp = _AcpiOsAllocate(num_io_apics * sizeof(const AcpiMadtIoApic *));
if (icp) {
_AcpiIoApics = icp;
p = madt->Subtable;
while (p != madt_end) {
h = (const AcpiSubtableHeader *)p;
switch (h->Type) {
case kAcpiMadtIoApic:
*icp++ = (const AcpiMadtIoApic *)p;
}
p += h->Length;
}
_AcpiNumIoApics = num_io_apics;
} else {
KWARNF("MADT: no memory for I/O APICs");
}
}
}
#endif /* __x86_64__ */

46
libc/irq/acpi-xsdt-init.S Normal file
View file

@ -0,0 +1,46 @@
/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│
vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/irq/acpi.internal.h"
#include "libc/macros.internal.h"
#include "libc/runtime/pc.internal.h"
.init.start 310,_init_acpi_xsdt
push %rdi
push %rsi
call _AcpiXsdtInit
pop %rsi
pop %rdi
.init.end 310,_init_acpi_xsdt
.bss
_AcpiXsdtNumEntries:
.skip 8
.endobj _AcpiXsdtNumEntries,globl
_AcpiXsdtEntries:
.skip 8
.endobj _AcpiXsdtEntries,globl
.previous

243
libc/irq/acpi-xsdt.c Normal file
View file

@ -0,0 +1,243 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/intrin/atomic.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/directmap.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/irq/acpi.internal.h"
#include "libc/log/color.internal.h"
#include "libc/macros.internal.h"
#include "libc/nt/efi.h"
#include "libc/runtime/pc.internal.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
#ifdef __x86_64__
textstartup static void *_AcpiOsMapRoMemory(uintptr_t phy, size_t n) {
__invert_and_perm_ref_memory_area(__get_mm(), __get_pml4t(), phy, n,
PAGE_XD);
return (void *)(BANE + phy);
}
textstartup static void *_AcpiOsMapMemory(uintptr_t phy, size_t n) {
__invert_and_perm_ref_memory_area(__get_mm(), __get_pml4t(), phy, n,
PAGE_XD | PAGE_RW);
return (void *)(BANE + phy);
}
textstartup void *_AcpiOsMapUncachedMemory(uintptr_t phy, size_t n) {
__invert_and_perm_ref_memory_area(__get_mm(), __get_pml4t(), phy, n,
PAGE_XD | PAGE_PCD | PAGE_RW);
return (void *)(BANE + phy);
}
textstartup static void *_AcpiOsAllocatePages(size_t n) {
struct DirectMap dm = sys_mmap_metal(NULL, n, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
void *addr = dm.addr;
if (addr == (void *)-1) addr = NULL;
return addr;
}
textstartup void *_AcpiOsAllocate(size_t n) {
static _Atomic(char *) slack = NULL;
char *addr = NULL;
size_t align = __BIGGEST_ALIGNMENT__, use;
if (n >= 4096) return _AcpiOsAllocatePages(n);
n = ROUNDUP(n, align);
for (;;) {
addr = atomic_exchange(&slack, NULL);
if (!addr) {
addr = _AcpiOsAllocatePages(4096);
if (!addr) return NULL;
}
use = (uintptr_t)addr % 4096 + n;
if (use <= 4096) {
if (use < 4096) atomic_store(&slack, addr + n);
return addr;
}
}
}
textstartup static uint8_t _AcpiTbChecksum(const uint8_t *p, size_t n) {
uint8_t c = 0;
while (n-- != 0) c += *p++;
return c;
}
textstartup static AcpiStatus _AcpiTbVerifyChecksum(const uint8_t *p,
size_t n) {
uint8_t sum = _AcpiTbChecksum(p, n);
if (!sum) return kAcpiOk;
KWARNF("bad ACPI table cksum %#x != 0 @ %p,+%#zx", (unsigned)sum, p, n);
return kAcpiExBadChecksum;
}
textstartup static AcpiStatus _AcpiRsdpVerifyChecksums(const uint8_t *p) {
const AcpiTableRsdp *q = (const AcpiTableRsdp *)p;
size_t length = offsetof(AcpiTableRsdp, Length);
AcpiStatus sta = _AcpiTbVerifyChecksum(p, length);
if (!_AcpiSuccess(sta)) return sta;
if (q->Revision <= 1) return kAcpiOk;
length = q->Length;
if (length < offsetof(AcpiTableRsdp, Reserved)) {
KWARNF("malformed ACPI 2+ RSDP, length %#zx < %#zx",
length, offsetof(AcpiTableRsdp, Reserved));
if (length < offsetof(AcpiTableRsdp, RsdtPhysicalAddress)
+ sizeof(q->RsdtPhysicalAddress)) {
return kAcpiExBadHeader;
}
return kAcpiOk;
}
return _AcpiTbVerifyChecksum(p, length);
}
textstartup static bool _AcpiTbIsValidRsdp(const uint8_t *p) {
const AcpiTableRsdp *q = (const AcpiTableRsdp *)p;
if (READ64LE(q->Signature) != READ64LE("RSD PTR ")) return false;
KINFOF("\"RSD PTR \" @ %p, ACPI rev %u", q, (unsigned)q->Revision);
if (!_AcpiSuccess(_AcpiRsdpVerifyChecksums(p))) return false;
return true;
}
textstartup static const AcpiTableRsdp *_AcpiFindRsdp(void) {
const size_t PARA_SZ = 0x10;
size_t off;
struct mman *mm = __get_mm();
uint64_t rsdp_phy = mm->pc_acpi_rsdp;
uint16_t ebda_para;
const uint8_t *area;
if (rsdp_phy) return _AcpiOsMapRoMemory(rsdp_phy, sizeof(AcpiTableRsdp));
/*
* "OSPM finds the Root System Description Pointer (RSDP) structure by
* searching physical memory ranges on 16-byte boundaries for a valid
* Root System Description Pointer structure signature and checksum match
* as follows:
* * The first 1 KB of the Extended BIOS Data Area (EBDA). For EISA or
* MCA systems, the EBDA can be found in the two-byte location 40:0Eh
* on the BIOS data area.
* * The BIOS read-only memory space between 0E0000h and 0FFFFFh."
* Advanced Configuration and Power Interface (ACPI) Specification,
* Release 6.5, §5.2.5.1
*/
ebda_para = *(const uint16_t *)(BANE + PC_BIOS_DATA_AREA + 0xE);
if (ebda_para) {
area = _AcpiOsMapMemory((uint32_t)ebda_para * PARA_SZ, 1024);
KINFOF("search EBDA @ %p for RSDP", area);
off = 1024;
while (off != 0) {
off -= PARA_SZ;
if (_AcpiTbIsValidRsdp(area + off)) {
return (const AcpiTableRsdp *)(area + off);
}
}
}
area = _AcpiOsMapMemory(0xE0000, 0xFFFFF + 1 - 0xE0000);
KINFOF("search ROM BIOS for RSDP");
off = ROUNDUP(0xFFFFF + 1 - 0xE0000, PARA_SZ);
while (off != 0) {
off -= PARA_SZ;
if (_AcpiTbIsValidRsdp(area + off)) {
return (const AcpiTableRsdp *)(area + off);
}
}
return NULL;
}
textstartup void *_AcpiMapTable(uintptr_t phy) {
void *p = _AcpiOsMapRoMemory(phy, sizeof(AcpiTableHeader));
AcpiTableHeader *hdr = p;
size_t length = hdr->Length;
if (length < sizeof(*hdr)) {
KDIEF("ACPI table @ %p has short length %#zx", p, length);
} else {
p = _AcpiOsMapRoMemory(phy, length);
}
_AcpiTbVerifyChecksum((const uint8_t *)p, length);
return p;
}
textstartup void _AcpiXsdtInit(void) {
if (IsMetal()) {
size_t nents, i;
void **ents = NULL;
const AcpiTableRsdp *rsdp = _AcpiFindRsdp();
if (!rsdp) {
KINFOF("no RSDP found");
return;
}
KINFOF("RSDP @ %p", rsdp);
if (rsdp->Revision <= 1 ||
rsdp->Length < offsetof(AcpiTableRsdp, Reserved) ||
!rsdp->XsdtPhysicalAddress) {
const AcpiTableRsdt *rsdt = _AcpiMapTable(rsdp->RsdtPhysicalAddress);
nents = (rsdt->Header.Length - sizeof(rsdt->Header)) / sizeof(uint32_t);
KINFOF("RSDT @ %p, %#zx entries", rsdt, nents);
ents = _AcpiOsAllocate(nents * sizeof(AcpiTableHeader *));
if (ents) {
for (i = 0; i < nents; ++i) {
ents[i] = _AcpiMapTable(rsdt->TableOffsetEntry[i]);
}
}
} else {
const AcpiTableXsdt *xsdt = _AcpiMapTable(rsdp->XsdtPhysicalAddress);
nents = (xsdt->Header.Length - sizeof(xsdt->Header)) / sizeof(uint64_t);
KINFOF("XSDT @ %p, %#zx entries", xsdt, nents);
ents = _AcpiOsAllocate(nents * sizeof(AcpiTableHeader *));
if (ents) {
for (i = 0; i < nents; ++i) {
ents[i] = _AcpiMapTable(xsdt->TableOffsetEntry[i]);
}
}
}
if (!ents) {
KWARNF("no memory for mapped RSDT/XSDT entries");
nents = 0;
}
_AcpiXsdtEntries = ents;
_AcpiXsdtNumEntries = nents;
}
}
textstartup AcpiStatus _AcpiGetTableImpl(uint32_t sig, uint32_t inst,
void **phdr) {
void **p = _AcpiXsdtEntries;
size_t n = _AcpiXsdtNumEntries;
while (n-- != 0) {
AcpiTableHeader *h = *p++;
if (READ32LE(h->Signature) != sig) continue;
if (inst-- != 0) continue;
*phdr = h;
return kAcpiOk;
}
return kAcpiExNotFound;
}
#endif /* __x86_64__ */

232
libc/irq/acpi.internal.h Normal file
View file

@ -0,0 +1,232 @@
#ifndef COSMOPOLITAN_LIBC_IRQ_ACPI_INTERNAL_H_
#define COSMOPOLITAN_LIBC_IRQ_ACPI_INTERNAL_H_
#include "libc/intrin/bits.h"
#include "libc/intrin/kprintf.h"
#include "libc/log/color.internal.h"
/**
* AcpiStatus values.
*/
#define kAcpiOk 0x0000
#define kAcpiExNotFound 0x0005
#define kAcpiExBadHeader 0x2002
#define kAcpiExBadChecksum 0x2003
/**
* Flags for AcpiTableMadt::Flags.
*/
#define kAcpiMadtPcAtCompat 0x0001
/**
* Flags for AcpiTableFadt::BootFlags.
*/
#define kAcpiFadtLegacyDevices 0x0001
#define kAcpiFadt8042 0x0002
#define kAcpiFadtNoVga 0x0004
#define kAcpiFadtNoMsi 0x0008
#define kAcpiFadtNoAspm 0x0010
#define kAcpiFadtNoCmosRtc 0x0020
/**
* Values for AcpiSubtableHeader::Type under an AcpiTableMadt.
*/
#define kAcpiMadtLocalApic 0
#define kAcpiMadtIoApic 1
#define kAcpiMadtIntOverride 2
#if !(__ASSEMBLER__ + __LINKER__ + 0)
/**
* @fileoverview Declarations for bare metal code to interact with ACPI
*
* @see UEFI Forum, Inc. Advanced Configuration and Power Interface (ACPI)
* Specification, Version 6.5, 2022. https://uefi.org/specifications
* @see Intel Corporation. ACPI Component Architecture: User Guide and
* Programmer Reference, Revision 6.2, 2017. https://acpica.org
*/
COSMOPOLITAN_C_START_
/**
* @internal
* Structure of an ACPI Root System Description Pointer (RSDP) table. This
* points to either an RSDT, or an XSDT, or both.
*/
typedef struct {
uint8_t Signature[8];
uint8_t Checksum;
uint8_t OemId[6];
uint8_t Revision;
uint32_t RsdtPhysicalAddress;
uint32_t Length;
uint64_t XsdtPhysicalAddress;
uint8_t ExtendedChecksum;
uint8_t Reserved[3];
} AcpiTableRsdp;
/**
* @internal
* Structure of a System Description Table Header, which appears at the
* beginning of each ACPI system description table.
*/
typedef struct thatispacked {
uint8_t Signature[4];
uint32_t Length;
uint8_t Revision;
uint8_t Checksum;
uint8_t OemId[6];
uint8_t OemTableId[8];
uint32_t OemRevision;
uint8_t AslCompilerId[4];
uint32_t AslCompilerRevision;
} AcpiTableHeader;
/**
* @internal
* ACPI Root System Description Table (RSDT) structure.
*/
typedef struct thatispacked {
AcpiTableHeader Header;
uint32_t TableOffsetEntry[];
} AcpiTableRsdt;
/**
* @internal
* ACPI Extended System Description Table (XSDT) structure.
*/
typedef struct thatispacked {
AcpiTableHeader Header;
uint64_t TableOffsetEntry[];
} AcpiTableXsdt;
typedef struct thatispacked {
uint8_t SpaceId;
uint8_t BitWidth;
uint8_t BitOffset;
uint8_t AccessWidth;
uint64_t Address;
} AcpiGenericAddress;
typedef struct thatispacked {
uint8_t Type;
uint8_t Length;
} AcpiSubtableHeader;
typedef struct thatispacked {
AcpiSubtableHeader Header;
uint8_t Id;
uint8_t Reserved;
uint32_t Address;
uint32_t GlobalIrqBase;
} AcpiMadtIoApic;
/**
* @internal
* ACPI Multiple APIC Description Table (MADT) structure.
*/
typedef struct thatispacked {
AcpiTableHeader Header;
uint32_t Address; /* local APIC address */
uint32_t Flags; /* multiple APIC flags */
char Subtable[]; /* ...interrupt controller structures... */
} AcpiTableMadt;
/**
* @internal
* Fixed ACPI Description Table (FADT) structure.
*/
typedef struct thatispacked {
AcpiTableHeader Header;
uint32_t Facs;
uint32_t Dsdt;
uint8_t Model;
uint8_t PreferredProfile;
uint16_t SciInterrupt;
uint32_t SmiCommand;
uint8_t AcpiEnable;
uint8_t AcpiDisable;
uint8_t S4BiosRequest;
uint8_t PstateControl;
uint32_t Pm1aEventBlock;
uint32_t Pm1bEventBlock;
uint32_t Pm1aControlBlock;
uint32_t Pm1bControlBlock;
uint32_t Pm2ControlBlock;
uint32_t PmTimerBlock;
uint32_t Gpe0Block;
uint32_t Gpe1Block;
uint8_t Pm1EventLength;
uint8_t Pm1ControlLength;
uint8_t Pm2ControlLength;
uint8_t PmTimerLength;
uint8_t Gpe0BlockLength;
uint8_t Gpe1BlockLength;
uint8_t Gpe1Base;
uint8_t CstControl;
uint16_t C2Latency;
uint16_t C3Latency;
uint16_t FlushSize;
uint16_t FlushStride;
uint8_t DutyOffset;
uint8_t DutyWidth;
uint8_t DayAlarm;
uint8_t MonthAlarm;
uint8_t Century;
uint16_t BootFlags;
uint8_t Reserved;
uint32_t Flags;
AcpiGenericAddress ResetRegister;
uint8_t ResetValue;
uint16_t ArmBootFlags;
uint8_t MinorRevision;
uint64_t XFacs;
uint64_t XDsdt;
AcpiGenericAddress XPm1aEventBlock;
AcpiGenericAddress XPm1bEventBlock;
AcpiGenericAddress XPm1aControlBlock;
AcpiGenericAddress XPm1bControlBlock;
AcpiGenericAddress XPm2ControlBlock;
AcpiGenericAddress XPmTimerBlock;
AcpiGenericAddress XGpe0Block;
AcpiGenericAddress XGpe1Block;
AcpiGenericAddress SleepControl;
AcpiGenericAddress SleepStatus;
uint64_t HypervisorId;
} AcpiTableFadt;
/**
* @internal
* ACPI Differentiated System Description Table (DSDT) structure.
*/
typedef struct thatispacked {
AcpiTableHeader Header;
uint8_t Aml[];
} AcpiTableDsdt;
typedef uint32_t AcpiStatus;
extern size_t _AcpiXsdtNumEntries, _AcpiNumIoApics;
extern void **_AcpiXsdtEntries;
extern uint16_t _AcpiBootFlags;
extern uint32_t _AcpiMadtFlags;
extern const AcpiMadtIoApic **_AcpiIoApics;
extern void *_AcpiOsMapUncachedMemory(uintptr_t, size_t);
extern void *_AcpiOsAllocate(size_t);
extern void *_AcpiMapTable(uintptr_t);
extern AcpiStatus _AcpiGetTableImpl(uint32_t, uint32_t, void **);
forceinline bool _AcpiSuccess(AcpiStatus __sta) {
return __sta == kAcpiOk;
}
forceinline AcpiStatus _AcpiGetTable(const char __sig[4], uint32_t __inst,
void **__phdr) {
uint8_t __sig_copy[4] = { __sig[0], __sig[1], __sig[2], __sig[3] };
return _AcpiGetTableImpl(READ32LE(__sig_copy), __inst, __phdr);
}
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_IRQ_ACPI_INTERNAL_H_ */

57
libc/irq/irq.mk Normal file
View file

@ -0,0 +1,57 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
PKGS += LIBC_IRQ
LIBC_IRQ_ARTIFACTS += LIBC_IRQ_A
LIBC_IRQ_A = o/$(MODE)/libc/irq/irq.a
LIBC_IRQ_A_FILES := $(wildcard libc/irq/*)
LIBC_IRQ_A_HDRS = $(filter %.h,$(LIBC_IRQ_A_FILES))
LIBC_IRQ_A_INCS = $(filter %.inc,$(LIBC_IRQ_A_FILES))
LIBC_IRQ_A_SRCS_S = $(filter %.S,$(LIBC_IRQ_A_FILES))
LIBC_IRQ_A_SRCS_C = $(filter %.c,$(LIBC_IRQ_A_FILES))
LIBC_IRQ = \
$(LIBC_IRQ_A_DEPS) \
$(LIBC_IRQ_A)
LIBC_IRQ_A_SRCS = \
$(LIBC_IRQ_A_SRCS_S) \
$(LIBC_IRQ_A_SRCS_C)
LIBC_IRQ_A_OBJS = \
$(LIBC_IRQ_A_SRCS_S:%.S=o/$(MODE)/%.o) \
$(LIBC_IRQ_A_SRCS_C:%.c=o/$(MODE)/%.o)
LIBC_IRQ_A_CHECKS = \
$(LIBC_IRQ_A).pkg \
$(LIBC_IRQ_A_HDRS:%=o/$(MODE)/%.ok)
LIBC_IRQ_A_DIRECTDEPS = \
LIBC_INTRIN \
LIBC_STR \
LIBC_SYSV
LIBC_IRQ_A_DEPS := \
$(call uniq,$(foreach x,$(LIBC_IRQ_A_DIRECTDEPS),$($(x))))
$(LIBC_IRQ_A):libc/irq/ \
$(LIBC_IRQ_A).pkg \
$(LIBC_IRQ_A_OBJS)
$(LIBC_IRQ_A).pkg: \
$(LIBC_IRQ_A_OBJS) \
$(foreach x,$(LIBC_IRQ_A_DIRECTDEPS),$($(x)_A).pkg)
LIBC_IRQ_LIBS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)))
LIBC_IRQ_SRCS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_SRCS))
LIBC_IRQ_HDRS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_HDRS))
LIBC_IRQ_INCS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_INCS))
LIBC_IRQ_BINS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_BINS))
LIBC_IRQ_CHECKS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_CHECKS))
LIBC_IRQ_OBJS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_OBJS))
LIBC_IRQ_TESTS = $(foreach x,$(LIBC_IRQ_ARTIFACTS),$($(x)_TESTS))
$(LIBC_IRQ_OBJS): $(BUILD_FILES) libc/irq/irq.mk
.PHONY: o/$(MODE)/libc/irq
o/$(MODE)/libc/irq: $(LIBC_IRQ_CHECKS)

View file

@ -20,6 +20,7 @@ o/$(MODE)/libc: o/$(MODE)/libc/calls \
o/$(MODE)/libc/elf \
o/$(MODE)/libc/fmt \
o/$(MODE)/libc/intrin \
o/$(MODE)/libc/irq \
o/$(MODE)/libc/log \
o/$(MODE)/libc/mem \
o/$(MODE)/libc/nexgen32e \

View file

@ -103,6 +103,18 @@
0x96, 0xFB, 0x7A, 0xDE, 0xD0, 0x80, 0x51, 0x6A \
} \
}
#define ACPI_20_TABLE_GUID \
{ \
0x8868E871, 0xE4F1, 0x11D3, { \
0xBC, 0x22, 0x00, 0x80, 0xC7, 0x3C, 0x88, 0x81 \
} \
}
#define ACPI_10_TABLE_GUID \
{ \
0xEB9D2D30, 0x2D88, 0x11D3, { \
0x9A, 0x16, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D \
} \
}
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_

View file

@ -43,6 +43,8 @@ struct EfiArgs {
static EFI_GUID kEfiLoadedImageProtocol = LOADED_IMAGE_PROTOCOL;
static EFI_GUID kEfiGraphicsOutputProtocol = GRAPHICS_OUTPUT_PROTOCOL;
static const EFI_GUID kEfiAcpi20TableGuid = ACPI_20_TABLE_GUID;
static const EFI_GUID kEfiAcpi10TableGuid = ACPI_10_TABLE_GUID;
extern const char vga_console[];
extern void _EfiPostboot(struct mman *, uint64_t *, uintptr_t, char **);
@ -117,6 +119,25 @@ static void EfiInitVga(struct mman *mm, EFI_SYSTEM_TABLE *SystemTable) {
GraphMode->FrameBufferSize, 0);
}
static void EfiInitAcpi(struct mman *mm, EFI_SYSTEM_TABLE *SystemTable) {
void *rsdp1 = NULL, *rsdp2 = NULL;
uintptr_t n = SystemTable->NumberOfTableEntries, i;
EFI_CONFIGURATION_TABLE *tab;
for (i = 0, tab = SystemTable->ConfigurationTable; i < n; ++i, ++tab) {
EFI_GUID *guid = &tab->VendorGuid;
if (memcmp(guid, &kEfiAcpi20TableGuid, sizeof(EFI_GUID)) == 0) {
rsdp2 = tab->VendorTable;
} else if (memcmp(guid, &kEfiAcpi20TableGuid, sizeof(EFI_GUID)) == 0) {
rsdp1 = tab->VendorTable;
}
}
if (rsdp2) {
mm->pc_acpi_rsdp = (uintptr_t)rsdp2;
} else {
mm->pc_acpi_rsdp = (uintptr_t)rsdp1;
}
}
/**
* EFI Application Entrypoint.
*
@ -203,6 +224,11 @@ __msabi EFI_STATUS EfiMain(EFI_HANDLE ImageHandle,
*/
if (_weaken(vga_console)) EfiInitVga(mm, SystemTable);
/*
* Gets a pointer to the ACPI RSDP.
*/
EfiInitAcpi(mm, SystemTable);
/*
* Asks UEFI which parts of our RAM we're allowed to use.
*/

View file

@ -39,9 +39,12 @@ struct mman {
struct { /* 0x1d48 — starting cursor pos. */
unsigned short y, x;
} pc_video_curs_info;
unsigned short pc_video_char_height; /* 0x1d4c — character height (useful
for setting cursor shape
in text mode) */
unsigned short pc_video_char_height; /* 0x1d4c — character height (useful
for setting cursor shape
in text mode) */
uint64_t pc_acpi_rsdp; /* 0x1d50 — pointer to ACPI RSDP;
NULL means to search for
it in legacy BIOS areas */
};
COSMOPOLITAN_C_END_

View file

@ -91,12 +91,12 @@
vga_console:
.endobj vga_console,globl,hidden
.previous
.init.start 305,_init_vga
.init.start 101,_init_vga
push %rdi
push %rsi
call _vga_init
pop %rsi
pop %rdi
.init.end 305,_init_vga
.init.end 101,_init_vga
.yoink sys_writev_vga
.yoink sys_readv_vga

View file

@ -33,8 +33,8 @@
#ifdef __x86_64__
/*
* @fileoverview Instantiation of routines for emergency or system console
* output in graphical video modes.
* @fileoverview Instantiation of routines for emergency console output in
* graphical video modes.
*
* @see libc/vga/tty-graph.inc
*/

View file

@ -25,7 +25,10 @@
OTHER DEALINGS IN THE SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/intrin/kprintf.h"
#include "libc/runtime/pc.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/str/str.h"
#include "libc/vga/vga.internal.h"
@ -72,4 +75,47 @@ textstartup void _vga_init(void) {
}
}
#if !IsTiny()
/**
* Non-emergency console vprintf(), useful for dumping debugging or
* informational messages at program startup.
*
* @see uprintf()
*/
void uvprintf(const char *fmt, va_list v) {
if (!IsMetal()) {
kvprintf(fmt, v);
} else {
long size = __get_safe_size(8000, 3000);
char *buf = alloca(size);
CheckLargeStackAllocation(buf, size);
size_t count = kvsnprintf(buf, size, fmt, v);
if (count >= size) count = size - 1;
_TtyWrite(&_vga_tty, buf, count);
_klog_serial(buf, count);
}
}
/**
* Non-emergency console printf(), useful for dumping debugging or
* informational messages at program startup.
*
* uprintf() is similar to kprintf(), but on bare metal with VGA support, it
* uses the normal, fast graphical console, rather than initializing an
* emergency console. This makes uprintf() faster on bare metal at the
* expense of being less crash-proof.
*
* (The uprintf() function name comes from the FreeBSD kernel.)
*
* @see kprintf()
* @see https://man.freebsd.org/cgi/man.cgi?query=uprintf&sektion=9&n=1
*/
void uprintf(const char *fmt, ...) {
va_list v;
va_start(v, fmt);
uvprintf(fmt, v);
va_end(v);
}
#endif /* !IsTiny() */
#endif /* __x86_64__ */

View file

@ -255,8 +255,7 @@ void _TtyGraphMoveLineCells(struct Tty *, size_t, size_t, size_t, size_t,
size_t);
/*
* Routines that implement emergency or system console output in graphical
* video modes.
* Routines that implement emergency console output in graphical video modes.
*/
void _TtyKlog16Update(struct Tty *);
void _TtyKlog16DrawChar(struct Tty *, size_t, size_t, wchar_t);