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);
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!