[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 authored and GitHub committed 2023-10-25 14:32:20 -07:00
1 parent 062b2d776e
commit ed17d3008b
25 files changed
+999 -36

No files matched your search

+7 -4
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 \
+1
View File
@@ -46,6 +46,7 @@ EXAMPLES_DIRECTDEPS = \
LIBC_DNS \
LIBC_FMT \
LIBC_INTRIN \
LIBC_IRQ \
LIBC_LOG \
LIBC_MEM \
LIBC_NEXGEN32E \
+2
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[]) {
+2
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);
}
}
+2 -2
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
+2
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")
+2
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.
+22 -15
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);
+30 -6
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
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
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
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
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
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
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
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
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)
+1
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 \
+12
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_
+26
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.
*/
+6 -3
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_
+2 -2
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
+2 -2
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
*/
+46
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__ */
+1 -2
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);