[PATCH] searialio: Support for mmap serial ports
Some chipsets have memory mapped serial ports. The protocol is the same as an standard uart, but with memory read/write instead of inb/outb. Signed-off-by: Ricardo Ribalda Delgado <ricardo.ribalda@gmail.com> --- src/Kconfig | 3 +++ src/hw/serialio.c | 28 +++++++++++++++++++++------- src/serial.c | 2 +- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/Kconfig b/src/Kconfig index e767be1351c3..ff1f960ef361 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -532,6 +532,9 @@ menu "Debugging" default 0x3f8 help Base port for serial - generally 0x3f8, 0x2f8, 0x3e8, or 0x2e8. + On some boards the serial port is memory mapped, in those cases + provide the 32 bit address. E.g. 0xFEDC6000 for the AMD Kern + (a.k.a Hudson UART). config DEBUG_IO depends on QEMU_HARDWARE && DEBUG_LEVEL != 0 diff --git a/src/hw/serialio.c b/src/hw/serialio.c index 6486fc086b1c..d02ab65f6e9b 100644 --- a/src/hw/serialio.c +++ b/src/hw/serialio.c @@ -17,6 +17,20 @@ #define DEBUG_TIMEOUT 100000 +static void +serial_outb(u8 val, u32 port, u8 offset){ + if (port < 0x10000) + return outb(val, port + offset); + writeb((void *)port + 4*offset, val); +} + +static u8 +serial_inb(u32 port, u8 offset){ + if (port < 0x10000) + return inb(port + offset); + return readb((void *)port + 4*offset); +} + // Setup the debug serial port for output. void serial_debug_preinit(void) @@ -25,12 +39,12 @@ serial_debug_preinit(void) return; // setup for serial logging: 8N1 u8 oldparam, newparam = 0x03; - oldparam = inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LCR); - outb(newparam, CONFIG_DEBUG_SERIAL_PORT+SEROFF_LCR); + oldparam = serial_inb(CONFIG_DEBUG_SERIAL_PORT, SEROFF_LCR); + serial_outb(newparam, CONFIG_DEBUG_SERIAL_PORT, SEROFF_LCR); // Disable irqs u8 oldier, newier = 0; - oldier = inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_IER); - outb(newier, CONFIG_DEBUG_SERIAL_PORT+SEROFF_IER); + oldier = serial_inb(CONFIG_DEBUG_SERIAL_PORT, SEROFF_IER); + serial_outb(newier, CONFIG_DEBUG_SERIAL_PORT, SEROFF_IER); if (oldparam != newparam || oldier != newier) dprintf(1, "Changing serial settings was %x/%x now %x/%x\n" @@ -44,11 +58,11 @@ serial_debug(char c) if (!CONFIG_DEBUG_SERIAL) return; int timeout = DEBUG_TIMEOUT; - while ((inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LSR) & 0x20) != 0x20) + while ((serial_inb(CONFIG_DEBUG_SERIAL_PORT, SEROFF_LSR) & 0x20) != 0x20) if (!timeout--) // Ran out of time. return; - outb(c, CONFIG_DEBUG_SERIAL_PORT+SEROFF_DATA); + serial_outb(c, CONFIG_DEBUG_SERIAL_PORT, SEROFF_DATA); } void @@ -66,7 +80,7 @@ serial_debug_flush(void) if (!CONFIG_DEBUG_SERIAL) return; int timeout = DEBUG_TIMEOUT; - while ((inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LSR) & 0x60) != 0x60) + while ((serial_inb(CONFIG_DEBUG_SERIAL_PORT, SEROFF_LSR) & 0x60) != 0x60) if (!timeout--) // Ran out of time. return; diff --git a/src/serial.c b/src/serial.c index 88349c8a9267..27710cb19af9 100644 --- a/src/serial.c +++ b/src/serial.c @@ -21,7 +21,7 @@ static u16 detect_serial(u16 port, u8 timeout, u8 count) { - if (CONFIG_DEBUG_SERIAL && port == CONFIG_DEBUG_SERIAL_PORT + if (CONFIG_DEBUG_SERIAL && port == (u16) CONFIG_DEBUG_SERIAL_PORT && !romfile_loadint("etc/advertise-serial-debug-port", 1)) return 0; outb(0x02, port+SEROFF_IER); -- 2.10.2
On Tue, Dec 20, 2016 at 03:01:52PM +0100, Ricardo Ribalda Delgado wrote:
Some chipsets have memory mapped serial ports. The protocol is the same as an standard uart, but with memory read/write instead of inb/outb.
Thanks. See my comments below. [...]
--- a/src/Kconfig +++ b/src/Kconfig @@ -532,6 +532,9 @@ menu "Debugging" default 0x3f8 help Base port for serial - generally 0x3f8, 0x2f8, 0x3e8, or 0x2e8. + On some boards the serial port is memory mapped, in those cases + provide the 32 bit address. E.g. 0xFEDC6000 for the AMD Kern + (a.k.a Hudson UART).
Does this chipset hardcode the uart at that memory address or is that address part of a PCI BAR? If it's on a PCI device it would be preferable to do a pci scan and find the address.
config DEBUG_IO depends on QEMU_HARDWARE && DEBUG_LEVEL != 0 diff --git a/src/hw/serialio.c b/src/hw/serialio.c index 6486fc086b1c..d02ab65f6e9b 100644 --- a/src/hw/serialio.c +++ b/src/hw/serialio.c @@ -17,6 +17,20 @@
#define DEBUG_TIMEOUT 100000
+static void +serial_outb(u8 val, u32 port, u8 offset){ + if (port < 0x10000) + return outb(val, port + offset); + writeb((void *)port + 4*offset, val); +} + +static u8 +serial_inb(u32 port, u8 offset){ + if (port < 0x10000) + return inb(port + offset); + return readb((void *)port + 4*offset); +} + // Setup the debug serial port for output. void serial_debug_preinit(void) @@ -25,12 +39,12 @@ serial_debug_preinit(void) return; // setup for serial logging: 8N1 u8 oldparam, newparam = 0x03; - oldparam = inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LCR); - outb(newparam, CONFIG_DEBUG_SERIAL_PORT+SEROFF_LCR); + oldparam = serial_inb(CONFIG_DEBUG_SERIAL_PORT, SEROFF_LCR); + serial_outb(newparam, CONFIG_DEBUG_SERIAL_PORT, SEROFF_LCR); // Disable irqs u8 oldier, newier = 0; - oldier = inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_IER); - outb(newier, CONFIG_DEBUG_SERIAL_PORT+SEROFF_IER); + oldier = serial_inb(CONFIG_DEBUG_SERIAL_PORT, SEROFF_IER); + serial_outb(newier, CONFIG_DEBUG_SERIAL_PORT, SEROFF_IER);
if (oldparam != newparam || oldier != newier) dprintf(1, "Changing serial settings was %x/%x now %x/%x\n" @@ -44,11 +58,11 @@ serial_debug(char c) if (!CONFIG_DEBUG_SERIAL) return; int timeout = DEBUG_TIMEOUT; - while ((inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LSR) & 0x20) != 0x20) + while ((serial_inb(CONFIG_DEBUG_SERIAL_PORT, SEROFF_LSR) & 0x20) != 0x20) if (!timeout--) // Ran out of time. return; - outb(c, CONFIG_DEBUG_SERIAL_PORT+SEROFF_DATA); + serial_outb(c, CONFIG_DEBUG_SERIAL_PORT, SEROFF_DATA); }
The above code (along with serial_debug_flush() ) can be called in 16bit mode and writing to a 32bit memory address there can cause failures (crashes or memory corruption). Simplest solution is probably to only write to memory if !MODE_SEGMENT - thus providing debug support only in 32bit mode (init and boot phases).
void @@ -66,7 +80,7 @@ serial_debug_flush(void) if (!CONFIG_DEBUG_SERIAL) return; int timeout = DEBUG_TIMEOUT; - while ((inb(CONFIG_DEBUG_SERIAL_PORT+SEROFF_LSR) & 0x60) != 0x60) + while ((serial_inb(CONFIG_DEBUG_SERIAL_PORT, SEROFF_LSR) & 0x60) != 0x60) if (!timeout--) // Ran out of time. return; diff --git a/src/serial.c b/src/serial.c index 88349c8a9267..27710cb19af9 100644 --- a/src/serial.c +++ b/src/serial.c @@ -21,7 +21,7 @@ static u16 detect_serial(u16 port, u8 timeout, u8 count) { - if (CONFIG_DEBUG_SERIAL && port == CONFIG_DEBUG_SERIAL_PORT + if (CONFIG_DEBUG_SERIAL && port == (u16) CONFIG_DEBUG_SERIAL_PORT && !romfile_loadint("etc/advertise-serial-debug-port", 1)) return 0; outb(0x02, port+SEROFF_IER);
-Kevin
Hi Kent Thanks for your fast response :) On Tue, Dec 20, 2016 at 5:48 PM, Kevin O'Connor <kevin@koconnor.net> wrote:
Does this chipset hardcode the uart at that memory address or is that address part of a PCI BAR? If it's on a PCI device it would be preferable to do a pci scan and find the address.
Seems that it is hardcoded. Check out https://github.com/coreboot/coreboot/blob/master/src/southbridge/amd/pi/huds... """ There are two UART controllers in Kern. The UART registers are memory-mapped. UART controller 0 registers range from FEDC_6000h to FEDC_6FFFh. UART controller 1 registers range from FEDC_8000h to FEDC_8FFFh. """
- outb(c, CONFIG_DEBUG_SERIAL_PORT+SEROFF_DATA); + serial_outb(c, CONFIG_DEBUG_SERIAL_PORT, SEROFF_DATA); }
The above code (along with serial_debug_flush() ) can be called in 16bit mode and writing to a 32bit memory address there can cause failures (crashes or memory corruption). Simplest solution is probably to only write to memory if !MODE_SEGMENT - thus providing debug support only in 32bit mode (init and boot phases).
Will fix and resend. Thanks!
participants (2)
-
Kevin O'Connor -
Ricardo Ribalda Delgado