This includes generic support for MMIO serial which can be used for other PCI (or non-PCI) serial ports too.
Only outputs to the serial port in 32-bit mode, unless we're lucky enough that it's at an address below 1MiB.
Signed-off-by: David Woodhouse David.Woodhouse@intel.com --- src/Kconfig | 15 ++++++++++++++- src/hw/serialio.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-)
diff --git a/src/Kconfig b/src/Kconfig index 92c3102..696ab46 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -477,8 +477,21 @@ menu "Debugging" default n help Send debugging information to serial port. - config DEBUG_SERIAL_PORT + config DEBUG_SERIAL_MMIO depends on DEBUG_SERIAL + bool + default n + help + Send debugging information to PCI serial port. + config DEBUG_SERIAL_QUARK + bool "Intel Quark serial port debugging" + depends on DEBUG_SERIAL + select DEBUG_SERIAL_MMIO + default n + help + Use Quark UART1 (PCI device 0x14, function 5) for serial debug. + config DEBUG_SERIAL_PORT + depends on DEBUG_SERIAL && !DEBUG_SERIAL_MMIO hex "Serial port base address" default 0x3f8 help diff --git a/src/hw/serialio.c b/src/hw/serialio.c index 8ec36c2..b2bc68f 100644 --- a/src/hw/serialio.c +++ b/src/hw/serialio.c @@ -9,6 +9,9 @@ #include "output.h" // dprintf #include "serialio.h" // serial_debug_preinit #include "x86.h" // outb +#include "hw/pci_regs.h" // PCI_VENDOR_ID, PCI_BASE_ADDRESS_0 +#include "hw/pci.h" // pci_config_readl +#include "stacks.h" // call32
/**************************************************************** @@ -17,8 +20,42 @@
#define DEBUG_TIMEOUT 100000
+#if CONFIG_DEBUG_SERIAL_MMIO +static u32 serial_addr; +static u8 serial_stride; +#define serial_out(d, ofs) writeb((void *)(serial_addr + (ofs)*serial_stride), (d)) +#define serial_in(ofs) readb((void *)(serial_addr + (ofs)*serial_stride)) +// Debug output only in 32-bit mode, or when MMIO address < 1MiB +#define serial_valid() (serial_addr && (!MODE16 || serial_addr < 0x100000)) +#define set_serial_addr(adr, str) do { serial_addr = (adr); \ + serial_stride = (str); } while (0) +#else #define serial_out(d, reg) outb(d, CONFIG_DEBUG_SERIAL_PORT+(reg)) #define serial_in(reg) inb(CONFIG_DEBUG_SERIAL_PORT+(reg)) +#define serial_valid() (1) +#define set_serial_addr(adr, str) do { ; } while (0) +#endif + +static int +find_mmio_serial(void) +{ + if (CONFIG_DEBUG_SERIAL_QUARK) { + // debug port is bus 0, device 0x14, function 5. + u16 uart_bdf = pci_to_bdf(0, 0x14, 5); + + // If it isn't a Quark UART... + if (pci_config_readl(uart_bdf, PCI_VENDOR_ID) != 0x09368086) + return -1; + + u32 bar0 = pci_config_readl(uart_bdf, PCI_BASE_ADDRESS_0); + if (!(bar0 & 0xf)) { + set_serial_addr(bar0, 4); + return 0; + } + } + + return -1; +}
// Setup the debug serial port for output. void @@ -26,6 +63,21 @@ serial_debug_preinit(void) { if (!CONFIG_DEBUG_SERIAL) return; + + if (CONFIG_DEBUG_SERIAL_MMIO) { + // Need to discover serial port MMIO address + if (MODE16) { + // Needs to be done in 32-bit mode. + extern void _cfunc32flat_serial_debug_preinit(void); + call32(_cfunc32flat_serial_debug_preinit, 0, -1); + return; + } + + // Find the PCI device or whatever provides the serial port + if (find_mmio_serial()) + return; + } + // setup for serial logging: 8N1 u8 oldparam, newparam = 0x03; oldparam = serial_in(SEROFF_LCR); @@ -46,6 +98,8 @@ serial_debug(char c) { if (!CONFIG_DEBUG_SERIAL) return; + if (!serial_valid()) + return; int timeout = DEBUG_TIMEOUT; while ((serial_in(SEROFF_LSR) & 0x20) != 0x20) if (!timeout--) @@ -68,6 +122,8 @@ serial_debug_flush(void) { if (!CONFIG_DEBUG_SERIAL) return; + if (!serial_valid()) + return; int timeout = DEBUG_TIMEOUT; while ((serial_in(SEROFF_LSR) & 0x60) != 0x60) if (!timeout--)