efi/serial: Refactor EFI bios serial services
* Convert x86 direct port I/O to our standard gUART. * sSerial is plainly EFI Serial or EFI Stdout * gUART is plainly hardware serial now Change-Id: I5730fbc0ce3427851e4e2d1d0aa8c55f7765e444 Reviewed-on: https://review.haiku-os.org/c/haiku/+/5982 Reviewed-by: Alex von Gluck IV <kallisti5@unixzen.com> Reviewed-by: Adrien Destugues <pulkomandy@pulkomandy.tk>
This commit is contained in:
parent
762b00e999
commit
81a892eddf
@ -1,5 +1,7 @@
|
||||
SubDir HAIKU_TOP src system boot arch x86 ;
|
||||
|
||||
SubDirC++Flags -fno-rtti ;
|
||||
|
||||
local defines = $(DEFINES) ;
|
||||
|
||||
local platform ;
|
||||
@ -24,6 +26,11 @@ for platform in [ MultiBootSubDirSetup bios_ia32 efi pxe_ia32 ] {
|
||||
arch_elf.cpp
|
||||
;
|
||||
|
||||
local kernelGenericDriverSources =
|
||||
debug_uart.cpp
|
||||
debug_uart_8250.cpp
|
||||
;
|
||||
|
||||
local kernelArchSpecificSources ;
|
||||
local kernelLibArchSpecificSources ;
|
||||
if $(TARGET_ARCH) = x86_64 && $(TARGET_BOOT_PLATFORM) = efi {
|
||||
@ -41,6 +48,7 @@ for platform in [ MultiBootSubDirSetup bios_ia32 efi pxe_ia32 ] {
|
||||
BootMergeObject [ FGristFiles boot_arch_$(TARGET_KERNEL_ARCH).o ] :
|
||||
$(kernelArchSources)
|
||||
$(kernelArchSpecificSources)
|
||||
$(kernelGenericDriverSources)
|
||||
$(kernelLibArchSpecificSources)
|
||||
$(librootOsArchSources)
|
||||
arch_cpu.cpp
|
||||
@ -48,8 +56,10 @@ for platform in [ MultiBootSubDirSetup bios_ia32 efi pxe_ia32 ] {
|
||||
: -std=c++11 # additional flags
|
||||
;
|
||||
|
||||
SEARCH on [ FGristFiles $(kernelGenericDriverSources) ]
|
||||
= [ FDirName $(HAIKU_TOP) src system kernel arch generic ] ;
|
||||
SEARCH on [ FGristFiles $(kernelArchSources) ]
|
||||
= [ FDirName $(HAIKU_TOP) src system kernel arch x86 ] ;
|
||||
= [ FDirName $(HAIKU_TOP) src system kernel arch $(TARGET_KERNEL_ARCH_DIR) ] ;
|
||||
|
||||
if $(TARGET_ARCH) = x86_64 && $(TARGET_BOOT_PLATFORM) = efi {
|
||||
SEARCH on [ FGristFiles $(kernelArchSpecificSources) ]
|
||||
|
@ -165,16 +165,14 @@ arch_start_kernel(addr_t kernelEntry)
|
||||
// A changing memory map shouldn't affect the generated page tables, as
|
||||
// they only needed to know about the maximum address, not any specific
|
||||
// entry.
|
||||
|
||||
dprintf("Calling ExitBootServices. So long, EFI!\n");
|
||||
serial_disable();
|
||||
while (true) {
|
||||
if (kBootServices->ExitBootServices(kImage, mapKey) == EFI_SUCCESS) {
|
||||
// If the console was provided by boot services, disable it.
|
||||
stdout = NULL;
|
||||
stderr = NULL;
|
||||
// Also switch to legacy serial output
|
||||
// (may not work on all systems)
|
||||
serial_switch_to_legacy();
|
||||
dprintf("Switched to legacy serial output\n");
|
||||
// Disconnect from EFI serial_io / stdio services
|
||||
serial_kernel_handoff();
|
||||
dprintf("Unhooked from EFI serial services\n");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -189,6 +187,8 @@ arch_start_kernel(addr_t kernelEntry)
|
||||
arch_mmu_post_efi_setup(memoryMapSize, memoryMap,
|
||||
descriptorSize, descriptorVersion);
|
||||
|
||||
serial_enable();
|
||||
|
||||
// Copy final kernel args
|
||||
// This should be the last step before jumping to the kernel
|
||||
// as there are some fixups happening to kernel_args even in the last minute
|
||||
|
@ -136,16 +136,14 @@ arch_start_kernel(addr_t kernelEntry)
|
||||
// A changing memory map shouldn't affect the generated page tables, as
|
||||
// they only needed to know about the maximum address, not any specific
|
||||
// entry.
|
||||
|
||||
dprintf("Calling ExitBootServices. So long, EFI!\n");
|
||||
serial_disable();
|
||||
while (true) {
|
||||
if (kBootServices->ExitBootServices(kImage, map_key) == EFI_SUCCESS) {
|
||||
// The console was provided by boot services, disable it.
|
||||
stdout = NULL;
|
||||
stderr = NULL;
|
||||
// Can we adjust gKernelArgs.platform_args.serial_base_ports[0]
|
||||
// to something fixed in qemu for debugging?
|
||||
serial_switch_to_legacy();
|
||||
dprintf("Switched to legacy serial output\n");
|
||||
// Disconnect from EFI serial_io / stdio services
|
||||
serial_kernel_handoff();
|
||||
dprintf("Unhooked from EFI serial services\n");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -159,6 +157,7 @@ arch_start_kernel(addr_t kernelEntry)
|
||||
|
||||
// Update EFI, generate final kernel physical memory map, etc.
|
||||
arch_mmu_post_efi_setup(memory_map_size, memory_map, descriptor_size, descriptor_version);
|
||||
serial_enable();
|
||||
|
||||
switch (el) {
|
||||
case 1:
|
||||
|
@ -119,16 +119,14 @@ arch_start_kernel(addr_t kernelEntry)
|
||||
// A changing memory map shouldn't affect the generated page tables, as
|
||||
// they only needed to know about the maximum address, not any specific
|
||||
// entry.
|
||||
|
||||
dprintf("Calling ExitBootServices. So long, EFI!\n");
|
||||
serial_disable();
|
||||
while (true) {
|
||||
if (kBootServices->ExitBootServices(kImage, map_key) == EFI_SUCCESS) {
|
||||
// The console was provided by boot services, disable it.
|
||||
stdout = NULL;
|
||||
stderr = NULL;
|
||||
// Also switch to legacy serial output
|
||||
// (may not work on all systems)
|
||||
serial_switch_to_legacy();
|
||||
dprintf("Switched to legacy serial output\n");
|
||||
// Disconnect from EFI serial_io / stdio services
|
||||
serial_kernel_handoff();
|
||||
dprintf("Unhooked from EFI serial services\n");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -145,6 +143,8 @@ arch_start_kernel(addr_t kernelEntry)
|
||||
arch_mmu_post_efi_setup(memory_map_size, memory_map,
|
||||
descriptor_size, descriptor_version);
|
||||
|
||||
serial_enable();
|
||||
|
||||
dprintf("[PRE] SetSatp()\n");
|
||||
SetSatp(satp);
|
||||
dprintf("[POST] SetSatp()\n");
|
||||
|
@ -130,16 +130,14 @@ arch_start_kernel(addr_t kernelEntry)
|
||||
// A changing memory map shouldn't affect the generated page tables, as
|
||||
// they only needed to know about the maximum address, not any specific
|
||||
// entry.
|
||||
|
||||
dprintf("Calling ExitBootServices. So long, EFI!\n");
|
||||
serial_disable();
|
||||
while (true) {
|
||||
if (kBootServices->ExitBootServices(kImage, mapKey) == EFI_SUCCESS) {
|
||||
// The console was provided by boot services, disable it.
|
||||
stdout = NULL;
|
||||
stderr = NULL;
|
||||
// Also switch to legacy serial output
|
||||
// (may not work on all systems)
|
||||
serial_switch_to_legacy();
|
||||
dprintf("Switched to legacy serial output\n");
|
||||
// Disconnect from EFI serial_io / stdio services
|
||||
serial_kernel_handoff();
|
||||
dprintf("Unhooked from EFI serial services\n");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -154,6 +152,8 @@ arch_start_kernel(addr_t kernelEntry)
|
||||
arch_mmu_post_efi_setup(memoryMapSize, memoryMap,
|
||||
descriptorSize, descriptorVersion);
|
||||
|
||||
serial_enable();
|
||||
|
||||
// Copy final kernel args
|
||||
// This should be the last step before jumping to the kernel
|
||||
// as there are some fixups happening to kernel_args even in the last minute
|
||||
|
@ -143,16 +143,14 @@ arch_start_kernel(addr_t kernelEntry)
|
||||
// A changing memory map shouldn't affect the generated page tables, as
|
||||
// they only needed to know about the maximum address, not any specific
|
||||
// entry.
|
||||
|
||||
dprintf("Calling ExitBootServices. So long, EFI!\n");
|
||||
serial_disable();
|
||||
while (true) {
|
||||
if (kBootServices->ExitBootServices(kImage, map_key) == EFI_SUCCESS) {
|
||||
// The console was provided by boot services, disable it.
|
||||
stdout = NULL;
|
||||
stderr = NULL;
|
||||
// Also switch to legacy serial output
|
||||
// (may not work on all systems)
|
||||
serial_switch_to_legacy();
|
||||
dprintf("Switched to legacy serial output\n");
|
||||
// Disconnect from EFI serial_io / stdio services
|
||||
serial_kernel_handoff();
|
||||
dprintf("Unhooked from EFI serial services\n");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -167,6 +165,9 @@ arch_start_kernel(addr_t kernelEntry)
|
||||
arch_mmu_post_efi_setup(memory_map_size, memory_map,
|
||||
descriptor_size, descriptor_version);
|
||||
|
||||
// Restart serial. gUART only until we get into the kernel
|
||||
serial_enable();
|
||||
|
||||
smp_boot_other_cpus(final_pml4, kernelEntry, (addr_t)&gKernelArgs);
|
||||
|
||||
// Enter the kernel!
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <boot/platform.h>
|
||||
#include <arch/cpu.h>
|
||||
#include <arch/generic/debug_uart.h>
|
||||
#include <arch/generic/debug_uart_8250.h>
|
||||
#include <boot/stage2.h>
|
||||
#include <boot/stdio.h>
|
||||
|
||||
@ -22,24 +23,9 @@
|
||||
static efi_guid sSerialIOProtocolGUID = EFI_SERIAL_IO_PROTOCOL_GUID;
|
||||
static const uint32 kSerialBaudRate = 115200;
|
||||
|
||||
static efi_serial_io_protocol *sSerial = NULL;
|
||||
static efi_serial_io_protocol *sEFISerialIO = NULL;
|
||||
static bool sSerialEnabled = false;
|
||||
static bool sSerialUsesEFI = true;
|
||||
|
||||
|
||||
enum serial_register_offsets {
|
||||
SERIAL_TRANSMIT_BUFFER = 0,
|
||||
SERIAL_RECEIVE_BUFFER = 0,
|
||||
SERIAL_DIVISOR_LATCH_LOW = 0,
|
||||
SERIAL_DIVISOR_LATCH_HIGH = 1,
|
||||
SERIAL_FIFO_CONTROL = 2,
|
||||
SERIAL_LINE_CONTROL = 3,
|
||||
SERIAL_MODEM_CONTROL = 4,
|
||||
SERIAL_LINE_STATUS = 5,
|
||||
SERIAL_MODEM_STATUS = 6,
|
||||
};
|
||||
|
||||
static uint16 sSerialBasePort = 0x3f8;
|
||||
static bool sEFIAvailable = true;
|
||||
|
||||
|
||||
DebugUART* gUART = NULL;
|
||||
@ -51,16 +37,16 @@ serial_putc(char ch)
|
||||
if (!sSerialEnabled)
|
||||
return;
|
||||
|
||||
if (sSerialUsesEFI && sSerial != NULL) {
|
||||
// First we use EFI serial_io output if available
|
||||
if (sEFISerialIO != NULL) {
|
||||
size_t bufSize = 1;
|
||||
sSerial->Write(sSerial, &bufSize, &ch);
|
||||
sEFISerialIO->Write(sEFISerialIO, &bufSize, &ch);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TRACE_DEBUG
|
||||
if (sSerialUsesEFI) {
|
||||
// To aid in early bring-up on EFI platforms, where the
|
||||
// serial_io protocol isn't working/available.
|
||||
#ifdef DEBUG
|
||||
// If we don't have EFI serial_io, fallback to EFI stdio
|
||||
if (sEFIAvailable) {
|
||||
char16_t ucsBuffer[2];
|
||||
ucsBuffer[0] = ch;
|
||||
ucsBuffer[1] = 0;
|
||||
@ -69,24 +55,20 @@ serial_putc(char ch)
|
||||
}
|
||||
#endif
|
||||
|
||||
// If EFI services are unavailable... try any UART
|
||||
// this can happen when serial_io is unavailable, or EFI
|
||||
// is exiting
|
||||
if (gUART != NULL) {
|
||||
gUART->PutChar(ch);
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
while ((in8(sSerialBasePort + SERIAL_LINE_STATUS) & 0x20) == 0)
|
||||
asm volatile ("pause;");
|
||||
|
||||
out8(ch, sSerialBasePort + SERIAL_TRANSMIT_BUFFER);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
extern "C" void
|
||||
serial_puts(const char* string, size_t size)
|
||||
{
|
||||
if (!sSerialEnabled || (sSerial == NULL && sSerialUsesEFI))
|
||||
if (!sSerialEnabled)
|
||||
return;
|
||||
|
||||
while (size-- != 0) {
|
||||
@ -114,50 +96,60 @@ extern "C" void
|
||||
serial_enable(void)
|
||||
{
|
||||
sSerialEnabled = true;
|
||||
if (gUART != NULL)
|
||||
gUART->InitPort(kSerialBaudRate);
|
||||
}
|
||||
|
||||
|
||||
extern "C" void
|
||||
serial_init(void)
|
||||
{
|
||||
// Check for EFI Serial
|
||||
efi_status status = kSystemTable->BootServices->LocateProtocol(
|
||||
&sSerialIOProtocolGUID, NULL, (void**)&sSerial);
|
||||
&sSerialIOProtocolGUID, NULL, (void**)&sEFISerialIO);
|
||||
|
||||
if (status != EFI_SUCCESS || sSerial == NULL) {
|
||||
sSerial = NULL;
|
||||
return;
|
||||
if (status != EFI_SUCCESS)
|
||||
sEFISerialIO = NULL;
|
||||
|
||||
if (sEFISerialIO != NULL) {
|
||||
// Setup serial, 0, 0 = Default Receive FIFO queue and default timeout
|
||||
status = sEFISerialIO->SetAttributes(sEFISerialIO, kSerialBaudRate, 0, 0, NoParity, 8,
|
||||
OneStopBit);
|
||||
|
||||
if (status != EFI_SUCCESS)
|
||||
sEFISerialIO = NULL;
|
||||
|
||||
// serial_io was successful.
|
||||
}
|
||||
|
||||
// Setup serial, 0, 0 = Default Receive FIFO queue and default timeout
|
||||
status = sSerial->SetAttributes(sSerial, kSerialBaudRate, 0, 0, NoParity, 8,
|
||||
OneStopBit);
|
||||
|
||||
if (status != EFI_SUCCESS) {
|
||||
sSerial = NULL;
|
||||
return;
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
// On x86, we can try to setup COM1 as a gUART too
|
||||
// while this serial port may not physically exist,
|
||||
// the location is fixed on the x86 arch.
|
||||
// TODO: We could also try to pull from acpi?
|
||||
if (gUART == NULL) {
|
||||
gUART = arch_get_uart_8250(0x3f8, 1843200);
|
||||
gUART->InitEarly();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
extern "C" void
|
||||
serial_switch_to_legacy(void)
|
||||
serial_kernel_handoff(void)
|
||||
{
|
||||
sSerial = NULL;
|
||||
sSerialUsesEFI = false;
|
||||
// The console was provided by boot services, disable it ASAP
|
||||
stdout = NULL;
|
||||
stderr = NULL;
|
||||
|
||||
// Disconnect from EFI serial_io services is important as we leave the bootloader
|
||||
sEFISerialIO = NULL;
|
||||
sEFIAvailable = false;
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
// TODO: convert over to exclusively arch_args.uart?
|
||||
memset(gKernelArgs.platform_args.serial_base_ports, 0,
|
||||
sizeof(uint16) * MAX_SERIAL_PORTS);
|
||||
|
||||
gKernelArgs.platform_args.serial_base_ports[0] = sSerialBasePort;
|
||||
|
||||
uint16 divisor = uint16(115200 / kSerialBaudRate);
|
||||
|
||||
out8(0x80, sSerialBasePort + SERIAL_LINE_CONTROL);
|
||||
// set divisor latch access bit
|
||||
out8(divisor & 0xf, sSerialBasePort + SERIAL_DIVISOR_LATCH_LOW);
|
||||
out8(divisor >> 8, sSerialBasePort + SERIAL_DIVISOR_LATCH_HIGH);
|
||||
out8(3, sSerialBasePort + SERIAL_LINE_CONTROL);
|
||||
// 8N1
|
||||
gKernelArgs.platform_args.serial_base_ports[0] = 0x3f8;
|
||||
#endif
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ extern void serial_puts(const char *string, size_t size);
|
||||
extern void serial_disable(void);
|
||||
extern void serial_enable(void);
|
||||
|
||||
extern void serial_switch_to_legacy(void);
|
||||
extern void serial_kernel_handoff(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -256,6 +256,8 @@ efi_main(efi_handle image, efi_system_table *systemTable)
|
||||
acpi_init();
|
||||
#ifdef _BOOT_FDT_SUPPORT
|
||||
dtb_init();
|
||||
// Start serial a second time to give any uart from DTB a chance to start up
|
||||
serial_enable();
|
||||
#endif
|
||||
timer_init();
|
||||
smp_init();
|
||||
|
@ -13,6 +13,12 @@ DebugUART::Out8(int reg, uint8 value)
|
||||
#if defined(__ARM__) || defined(__aarch64__)
|
||||
// 32-bit aligned
|
||||
*((uint8 *)Base() + reg * sizeof(uint32)) = value;
|
||||
#elif defined(__i386__) || defined(__x86_64__)
|
||||
// outb for access to IO space.
|
||||
if ((Base() + reg) <= 0xFFFF)
|
||||
__asm__ volatile ("outb %%al,%%dx" : : "a" (value), "d" (Base() + reg));
|
||||
else
|
||||
*((uint8 *)Base() + reg) = value;
|
||||
#else
|
||||
*((uint8 *)Base() + reg) = value;
|
||||
#endif
|
||||
@ -25,6 +31,14 @@ DebugUART::In8(int reg)
|
||||
#if defined(__ARM__) || defined(__aarch64__)
|
||||
// 32-bit aligned
|
||||
return *((uint8 *)Base() + reg * sizeof(uint32));
|
||||
#elif defined(__i386__) || defined(__x86_64__)
|
||||
// inb for access to IO space.
|
||||
if ((Base() + reg) <= 0xFFFF) {
|
||||
uint8 _v;
|
||||
__asm__ volatile ("inb %%dx,%%al" : "=a" (_v) : "d" (Base() + reg));
|
||||
return _v;
|
||||
}
|
||||
return *((uint8 *)Base() + reg);
|
||||
#else
|
||||
return *((uint8 *)Base() + reg);
|
||||
#endif
|
||||
|
@ -97,6 +97,10 @@ local archGenericSources =
|
||||
msi.cpp
|
||||
pic.cpp
|
||||
|
||||
# serial
|
||||
debug_uart.cpp
|
||||
debug_uart_8250.cpp
|
||||
|
||||
# paging
|
||||
X86PagingMethod.cpp
|
||||
X86PagingStructures.cpp
|
||||
|
Loading…
Reference in New Issue
Block a user