Hi everyone!
Having a serial port is a nice feature for an osdev programmer; unfortunately many modern PCs and laptops lack it. My Latitude E5470 happens to have an UART port right in the middle of it's motherboard so I've decided to make use of it.
The machine's EFI firmware provides 2 options for this supposedly debug port in the NVRAM Setup variable: enabling UART as a simple serial port available somewhere in ACPI namespace (I guess, in that case the port is detected via DSDT/SSDT tables; I haven't found that detection code in linux kernel yet though) or as a Debug Port specified in DBGP/DBG2 tables (those are used by the Windows SoC for debugging). Needless to say, using debug port tables is much easier than interpreting AML-coded ACPI tables. However, linux kernel doesn't support using DBGP tables to enable /dev/tty*, only as an early_printk debugging interface and only USB EHCI/XHCI debug ports. There are a couple of patches enabling it for MMIO & IO ports however they were prevented from getting to mainline because of Microsoft-connected licensing concerns (sic!); yet they're not easy to be used as a reference implementation; you can find these patches in linux kernel mailing lists.
After soldering an external port to the internal UART pins I have managed to use it (both RXD and TXD communications with other computer) as a simple /dev/ttyS0 by enabling it as a serial port, so I know the port is actually working.
There's very little information available on this topic, so I'm very curious about others' experiences with it. I have tried accessing MMIO registers specified in DBGP using same offsets from IO COM ports but it has given no result so far. Are offsets the same for IO and MMIO ports? How are memory mapped serial ports usually handled?
Ilya
MMIO serial ports & DBGP/DBG2 ACPI tables
-
- Posts: 3
- Joined: Fri Apr 28, 2017 6:09 am
MMIO serial ports & DBGP/DBG2 ACPI tables
Last edited by ilya101010 on Sun Jul 09, 2017 3:28 am, edited 1 time in total.
working on a minimal hypervisor: https://github.com/jinet-vm/vmm (be warned: WIP)
I'm not a native English speaker so feel free to correct all my mistakes — especially grammar ones.
I'm not a native English speaker so feel free to correct all my mistakes — especially grammar ones.
- BrightLight
- Member
- Posts: 901
- Joined: Sat Dec 27, 2014 9:11 am
- Location: Maadi, Cairo, Egypt
- Contact:
Re: MMIO serial ports & DBGP/DBG2 ACPI tables
First off, I have never heard of memory-mapped serial ports, and I doubt the ACPI DBGP/DBG2 tables you mention (that I have never heard of either) have anything to do with serial ports, because I doubt your laptop has serial ports for the purpose of debugging. Instead, the laptop probably has serial ports because it's old and serial ports were the USB of the 90's (or 80's?)
Second off, EFI/UEFI are modern firmware interfaces, and to be perfectly honest I don't know much about them, but I seriously doubt EFI/UEFI has a mechanism for detecting the base I/O ports of the serial port.
For systems running on BIOS firmware, you can read the base address of the first serial port (COM1) from physical memory address 0x400, which is probably 99% of the time 0x3F8 if a serial port is present. For EFI/UEFI, there is no mechanism for detecting serial ports. So if I were you, I would detect serial ports on EFI/UEFI systems by attempting to configure the serial port at I/O ports 0x3F8, and then reading back the values to check if they are valid. If they are, I'd assume a serial port exists, if not, then a serial port doesn't.
Since I don't know a few details here, maybe someone can give a more accurate answer.
Second off, EFI/UEFI are modern firmware interfaces, and to be perfectly honest I don't know much about them, but I seriously doubt EFI/UEFI has a mechanism for detecting the base I/O ports of the serial port.
For systems running on BIOS firmware, you can read the base address of the first serial port (COM1) from physical memory address 0x400, which is probably 99% of the time 0x3F8 if a serial port is present. For EFI/UEFI, there is no mechanism for detecting serial ports. So if I were you, I would detect serial ports on EFI/UEFI systems by attempting to configure the serial port at I/O ports 0x3F8, and then reading back the values to check if they are valid. If they are, I'd assume a serial port exists, if not, then a serial port doesn't.
Since I don't know a few details here, maybe someone can give a more accurate answer.
You know your OS is advanced when you stop using the Intel programming guide as a reference.
Re: MMIO serial ports & DBGP/DBG2 ACPI tables
There is. It's called straight - Serial I/O Protocol. And is described in the section 11.8 of the specification.omarrx024 wrote:First off, I have never heard of memory-mapped serial ports, and I doubt the ACPI DBGP/DBG2 tables you mention (that I have never heard of either) have anything to do with serial ports, because I doubt your laptop has serial ports for the purpose of debugging. Instead, the laptop probably has serial ports because it's old and serial ports were the USB of the 90's (or 80's?)
Second off, EFI/UEFI are modern firmware interfaces, and to be perfectly honest I don't know much about them, but I seriously doubt EFI/UEFI has a mechanism for detecting the base I/O ports of the serial port.
For systems running on BIOS firmware, you can read the base address of the first serial port (COM1) from physical memory address 0x400, which is probably 99% of the time 0x3F8 if a serial port is present. For EFI/UEFI, there is no mechanism for detecting serial ports. So if I were you, I would detect serial ports on EFI/UEFI systems by attempting to configure the serial port at I/O ports 0x3F8, and then reading back the values to check if they are valid. If they are, I'd assume a serial port exists, if not, then a serial port doesn't.
Since I don't know a few details here, maybe someone can give a more accurate answer.
-
- Posts: 3
- Joined: Fri Apr 28, 2017 6:09 am
Re: MMIO serial ports & DBGP/DBG2 ACPI tables
Serial ports nowadays are mostly used for debugging facilities and USB is used for peripheral devices so it's not the same. The thing you're talking is a COM port, typically a bulky DB9 connector unimaginable in modern thin laptops. A serial port is a wider term. In my case it describes pins right on the board, not a full external port like USB or VGA (although having com port in a modern laptop would be very handy). See guys from defcon showcasing uses of uart in modern devices here (spoiler: they're amazing).omarrx024 wrote:First off, I have never heard of memory-mapped serial ports, and I doubt the ACPI DBGP/DBG2 tables you mention (that I have never heard of either) have anything to do with serial ports, because I doubt your laptop has serial ports for the purpose of debugging. Instead, the laptop probably has serial ports because it's old and serial ports were the USB of the 90's (or 80's?)
working on a minimal hypervisor: https://github.com/jinet-vm/vmm (be warned: WIP)
I'm not a native English speaker so feel free to correct all my mistakes — especially grammar ones.
I'm not a native English speaker so feel free to correct all my mistakes — especially grammar ones.
Re: MMIO serial ports & DBGP/DBG2 ACPI tables
reading/writing from/to its registers.ilya101010 wrote: How are memory mapped serial ports usually handled?
Here is a live example of a very basic initialization and use of such a UART on a mips machine, made in the very beginning of the FW start.
Code: Select all
/* UART4 */
/* configure UART4 TxD, RxD pins - GPIO bank C ports 10 and 20, set as pins of device function 2. */
li $t1, (PIN_UART4_TXD | PIN_UART4_RXD)
sw $t1, (GPIO_C + GPIO_INTC)($s0) /* interrupt clear, not an interrupt */
sw $t1, (GPIO_C + GPIO_MSKC)($s0) /* mask clear, port is a pin of device, not gpio */
sw $t1, (GPIO_C + GPIO_PAT1S)($s0) /* patern1 set, port is function 2 or 3 */
sw $t1, (GPIO_C + GPIO_PAT0C)($s0) /* patern0 clear, port is function 2 */
sw $t1, (GPIO_C + GPIO_PEC)($s0)
/* Enable UART4 clock. UARTs are clocked from EXTCLK: no PLL required. */
la $s0, CPM_BASE /* read-modify-write sequence for */
lw $t2, CPM_CLKGR1($s0)
andi $t3, $t2, %lo(~CLKGR1_UART4) /* clear the UART4 clock gate bit */
sw $t3, CPM_CLKGR1($s0) /* ungating the clock for UART4 */
/* Uart4Init */
lui $s0, %hi(UART4_BASE)
ori $s0, %lo(UART4_BASE)
1: lw $t0, UART_ULSR($s0)
andi $t0, $t0, ULSR_TEMP /* TEMP is set when UTHR and shift register are empty */
beq $zero, $t0, 1b
nop
sw $zero, UART_UIER($s0) /* disable interrupts */
li $t3, (ULCR_DLAB | ULCR_WLS_8)
sw $t3, UART_ULCR($s0)
sw $zero, UART_UDLLR($s0)
sw $zero, UART_UDLHR($s0)
li $t3, ULCR_WLS_8
sw $t3, UART_ULCR($s0)
li $t1, UMCR_RTS
sw $t1, UART_UMCR($s0) /* modem control: RTS - Request To Send */
li $t2, (UFCR_FME | UFCR_RFRT | UFCR_TFRT | UFCR_UME)
sw $t2, UART_UFCR($s0) /* Enable FIFO, reset Rx, Tx, enable the module */
li $t3, (ULCR_DLAB | ULCR_WLS_8) /* Divisor Latch Access Bit and Word Length Select set */
sw $t3, UART_ULCR($s0) /* Now we can access DLLR and DLHR to setup the baud rate */
li $t4, (UART_DIVISOR & 0x00ff) /* lower byte into DLLR */
sw $t4, UART_UDLLR($s0)
li $t5, ((UART_DIVISOR & 0x0000ff00) >> 8) /* higher byte into DLHR */
sw $t5, UART_UDLHR($s0)
li $t6, ULCR_WLS_8
sw $t6, UART_ULCR($s0) /* DLAB bit clear */
/* say hello */
lui $a0, %hi(szHello)
jal PutString
ori $a0, %lo(szHello)
So basically you need to know what kind of UART it is, and register positions. How to do this with a laptop, I don't know, I got that from a SoC vendor manual.
Re: MMIO serial ports & DBGP/DBG2 ACPI tables
So you have two setups for the UART, "serial" or "debug" port, and as "serial" it can be found by Linux and used?
The reason you're not a fan of that is because of ACPI/AML, right? I'm not 100% sure but might it be possible to do the ACPI/AML stuff in Linux, get the MMIO (or IO port) "sequence" for using the UART then use those as hardcoded values in your OS?
Think of it like "reverse engineering" the UART with ACPI/AML under Linux, then once you know the addresses and how to use the UART you create a "MoBo driver" that has the knowledge on how to drive the UART on that specific MoBo, so there's no ugly hacks in your OS but also you don't need ACPI/AML support.
I don't know feasible the above is, just a suggestion if nothing else works..
The reason you're not a fan of that is because of ACPI/AML, right? I'm not 100% sure but might it be possible to do the ACPI/AML stuff in Linux, get the MMIO (or IO port) "sequence" for using the UART then use those as hardcoded values in your OS?
Think of it like "reverse engineering" the UART with ACPI/AML under Linux, then once you know the addresses and how to use the UART you create a "MoBo driver" that has the knowledge on how to drive the UART on that specific MoBo, so there's no ugly hacks in your OS but also you don't need ACPI/AML support.
I don't know feasible the above is, just a suggestion if nothing else works..
Re: MMIO serial ports & DBGP/DBG2 ACPI tables
A byte-sized device register, accessible on the IO space at an offset x, may be accessible on the MEM space at an offset = x << shift.ilya101010 wrote:I have tried accessing MMIO registers specified in DBGP using same offsets from IO COM ports but it has give no result so far. Are offsets the same for IO and MMIO ports? How are memory mapped serial ports usually handled?
See Serial parameters.
If the laptop contains processor and chipset corresponding to Intel Skylake microarchitecture, and the UART being referred to here in the post is the PCI-based PCH UART device, then its memory-mapped registers are accessible at 4-byte offsets from the base, unless one programs the GEN_REGRW7 register to force 1-byte offsets.
Search for the document "Skylake Platform Controller Hub H and LP".
-
- Posts: 3
- Joined: Fri Apr 28, 2017 6:09 am
Re: MMIO serial ports & DBGP/DBG2 ACPI tables
That's exactly what I have been doing so far, specifically studying early_printk (see here: https://github.com/torvalds/linux/blob/ ... y_printk.c).LtG wrote:Think of it like "reverse engineering" the UART with ACPI/AML under Linux, then once you know the addresses and how to use the UART you create a "MoBo driver" that has the knowledge on how to drive the UART on that specific MoBo, so there's no ugly hacks in your OS but also you don't need ACPI/AML support.
The document is exactly the one I sought. Thank you!linuxyne wrote:If the laptop contains processor and chipset corresponding to Intel Skylake microarchitecture, and the UART being referred to here in the post is the PCI-based PCH UART device, then its memory-mapped registers are accessible at 4-byte offsets from the base, unless one programs the GEN_REGRW7 register to force 1-byte offsets.
Search for the document "Skylake Platform Controller Hub H and LP".
working on a minimal hypervisor: https://github.com/jinet-vm/vmm (be warned: WIP)
I'm not a native English speaker so feel free to correct all my mistakes — especially grammar ones.
I'm not a native English speaker so feel free to correct all my mistakes — especially grammar ones.
Re: MMIO serial ports & DBGP/DBG2 ACPI tables
Pretty much every chipset (also every modern one) has serial ports for debugging. Most boards do not have DE-9 connectors but pretty much all of them do have some serial port pins. The serial port is the most widespread peripheral device that is available.omarrx024 wrote:First off, I have never heard of memory-mapped serial ports, and I doubt the ACPI DBGP/DBG2 tables you mention (that I have never heard of either) have anything to do with serial ports, because I doubt your laptop has serial ports for the purpose of debugging. Instead, the laptop probably has serial ports because it's old and serial ports were the USB of the 90's (or 80's?)
In addition to that PCI defines a debugging port capability that enables extremely simple serial output.
managarm: Microkernel-based OS capable of running a Wayland desktop (Discord: https://discord.gg/7WB6Ur3). My OS-dev projects: [mlibc: Portable C library for managarm, qword, Linux, Sigma, ...] [LAI: AML interpreter] [xbstrap: Build system for OS distributions].