Newer
Older
AMI-Aptio-BIOS-Reversed / SerialIo / SerialIo.md
@Ajax Dong Ajax Dong 2 days ago 15 KB Init

SerialIo

Function Table

Address Name Description
SerialIoDriverBindingSupported
SerialIoDriverBindingStart
SerialIoDriverBindingStop
SerialIoComponentNameGetDriverName
SerialIoComponentNameGetControllerName
SerialIoReset
SerialIoSetAttributes
SerialIoSetControlBits
SerialIoGetControlBits
SerialIoWrite
SerialIoRead
UartReadRegister
UartWriteRegister
UartSetFifoMode
SerialIoCreateDevicePathNode
SerialIoTransmitReadyTimer
SerialIoDetectUart
SerialIoDetectUartEnhanced
SerialIoDriverEntryPoint
HobGetAcpiDevicePathEnd
COM port name strings (off_3560 at 0x3560)
GLOBAL_REMOVE_IF_UNREFERENCED const CHAR16 *mComPortNames[10] = {
Baud rate table (dword_35D0 at 0x35D0)
Supported rates in ascending order, used to clamp requested baud rates
to the nearest valid value.
GLOBAL_REMOVE_IF_UNREFERENCED const UINT32 mBaudRateTable[19] = {
PCI UART Device Table (at 0x3AC8)
Terminated by DeviceId = 0xFFFF.
The SerialIoDriverBindingStart code iterates this table to match PCI UART
Data at 0x3AC8: first entry = {0xFFFF, 0x00FF, ...} (table may be empty/terminator)
Default serial attributes for initial Setup
xmmword_35B0 at 0x35B0 = 0x0000130E03
Hardware magic constant:
Max consecutive write timeouts before blocking
Software flow control timeout (in 100ns units, 10 seconds)
Transmit timeout loop limit
EFI_GUID gEfiSerialIoProtocolGuid = EFI_SERIAL_IO_PROTOCOL_GUID;
Device path GUID for UART serial device node
EFI_GUID gEfiSerialIoDevicePathGuid = EFI_SERIAL_IO_DEVICE_PATH_GUID;
EFI_STATUS EFIAPI
Component Name 2 Protocol
Serial I/O Protocol Functions
UART Hardware Access
UINT8 UartReadRegister (
Backend functions (not protocol interface, internal)
EFI_STATUS SerialIoCreateDevicePathNode (
caller frees
if (Private->AccessMethod) {
MMIO access (direct memory read of register)
switch (Private->RegisterWidth) {
Legacy I/O port access
return __inbyte(Offset + (UINT16)Private->BaseAddress);
PCI I/O protocol backed access
EFI_PCI_IO_PROTOCOL *PciIo;
Use PCI MMIO read via PCI I/O protocol
Use PCI I/O port read
MMIO access
Only run detection for '$SIO' magic UARTs
if (Private->Magic != SERIAL_IO_MAGIC) {
Enable FIFO, clear RCVR/XMIT FIFOs
UartWriteRegister **(Private, R_UART_FCR, FCR_FIFO_ENABLE **
Read IIR to check FIFO enable status
if (UartReadRegister (Private, R_UART_IIR) == 0xFF) {
No UART at this address (bus pulls data high)
UartWriteRegister (Private, R_UART_FCR, SavedFcr);
Flush any pending data
Perform 8250/16550 detection by writing 0x80 then checking loopback
UartWriteRegister (Private, R_UART_THR, 0x80);
Check if 0x80 can be read back correctly
if ((UartReadRegister (Private, R_UART_LSR) & LSR_DR) != 0 &&
No FIFO: standard 8250/16450
Restore FCR to enabled+cleared state for further probing
This is controlled by byte_3FA1 (a global flag, possibly setup-dependent)
if (mSomeLoopbackEnableFlag) {
Flush remaining data
while ((UartReadRegister (Private, R_UART_LSR) & LSR_DR) != 0) {
Perform DTR/RTS loopback test:
Toggle DTR bit
if ((NextMcr & MCR_DTR) != 0) {
Restore MCR
return UartWriteRegister (Private, R_UART_FCR, 0xFE);
16750 with 64-byte FIFO: enable FIFO with trigger level 14
16550A / standard: 16-byte FIFO with trigger level 1
Reset UART: clear DLAB, disable break, set LCR to default
Lcr = UartReadRegister (Private, R_UART_LCR);
Reset FIFOs
Reconfigure FIFO with default depth (64 if 16750, else 16)
UartSetFifoMode (Private, 0x40, 0);
Clear MCR loopback bits
Mcr = UartReadRegister (Private, R_UART_MCR);
Reset scratch register
UartWriteRegister (Private, R_UART_SCR, 0);
Restore serial attributes
UartCfg = Private->UartConfig;
Reset SW FIFO and state
Flush RBR (read to clear)
UartReadRegister (Private, R_UART_RBR);
Apply defaults for zero-valued parameters
if (BaudRate == 0) {
DefaultNoParity }
DefaultStopBits = 1
if (Private->HardwareType == HW_TYPE_16750 && DataBits < 7) {
Clamp baud rate to nearest valid table entry
for (BaudIndex = 0; BaudIndex < 19 - 1; BaudIndex++) {
Validate all parameters against range limits
if **(ReceiveFifoDepth - 1 > 63 **
Check if already configured at these settings
if (Private->BaudRate == BaudRate &&
Update FIFO depth in hardware if changed
if (Private->UartConfig->ReceiveFifoDepth != ReceiveFifoDepth) {
Calculate baud rate divisor
Divisor = Clock / (16 * BaudRate);
Set DLAB (Divisor Latch Access Bit) and wait for THRE+TSRE
Lcr **= UartReadRegister (Private, R_UART_LCR) LCR_DLAB;**
Write divisor
UartWriteRegister (Private, R_UART_LCR, Lcr);
Set line control (parity, stop bits, data bits)
switch (Parity) {
NoParity **NewLcr &= ~(LCR_PEN LCR_EPS LCR_SP);**
EvenParity **NewLcr = (NewLcr & ~(LCR_PEN LCR_EPS LCR_SP)) (LCR_PEN LCR_EPS);**
OddParity **NewLcr = (NewLcr & ~(LCR_EPS LCR_SP)) LCR_PEN;**
MarkParity / SpaceParity
SpaceParity (Mark=4, Space=5 in EFI)
Set word length (5, 6, 7, 8 -> 0, 1, 2, 3)
NewLcr **= (NewLcr & ~(LCR_WLS0 LCR_WLS1)) (DataBits - 5);**
Update stored configuration
Notify parent via re-propagated device path if attributes changed
if **(Private->BaudRate != BaudRate **
Duplicate device path with new attributes and re-install
VOID *OldDevPath;
Free old if present
if (Private->ParentDevicePath != NULL) {
if ((Control & 0xFFFF8FFC) != 0) {
Set DTR (MCR bit 0)
Mcr **= (Mcr & ~MCR_DTR) ((Control & SERIAL_CTRL_DTR) ? MCR_DTR : 0);**
Set RTS (MCR bit 1)
Mcr **= (Mcr & ~MCR_RTS) ((Control & SERIAL_CTRL_RTS) ? MCR_RTS : 0);**
Set Loopback (MCR bit 4) -> mapped to SERIAL_CTRL_REQUEST_TO_SEND bit 12 (0x1000)
Mcr **= (Mcr & ~MCR_LOOP) ((Control & 0x1000) ? MCR_LOOP : 0);**
Update software control bits
Read Modem Status Register
Msr = UartReadRegister (Private, R_UART_MSR);
Read Modem Control Register
Add software-controlled hardware reset bit
if ((Private->ControlBits & SERIAL_CTRL_HARDWARE_RESET) != 0) {
Read Line Status Register for error conditions
Lsr = UartReadRegister (Private, R_UART_LSR);
Not exactly - indicates TX in progress
Too many retries or CTS lost arm timer and return error
When RTS/CTS is active, limit writes to 16 bytes per call
No flow control: use SW FIFO or direct write
if (*BufferSize > 0) {
Fill SW FIFO if space available
while (Private->FifoCount < *FifoDepth) {
Hardware flow control: poll for CTS before sending
do {
XOFF character received
Standard write (no flow control or RTS)
if ((Private->ControlBits & SERIAL_CTRL_HARDWARE_RESET) != 0 &&
Assert RTS
SoftwareLoop = TRUE;
Wait for CTS (MSR bit 4)
while ((UartReadRegister (Private, R_UART_MSR) & MSR_CTS) == 0) {
Write loop
Wait for THR empty (LSR bit 5)
Write byte
UartWriteRegister (Private, R_UART_THR
Update status from partial transfer
if (AutoRts && Status == EFI_TIMEOUT) {
preserve EFI_TIMEOUT but with data written
SW FIFO mode: read from SW FIFO buffer
Flush the FIFO after an overrun
Direct read from UART: poll for data
Drain FIFO on overrun
for ( ; Index < *BufferSize; Index++) {
No more data available now
Check for CTS recovery
if ((UartReadRegister (Private, R_UART_MSR) & MSR_CTS) != 0) {
Check for THRE recovery
if ((UartReadRegister (Private, R_UART_LSR) & LSR_THRE) != 0) {
Global flag controlling extended UART detection (at 0x3FA1)
extern UINT8 mExtendedUartDetection;
Perform UART presence and loopback detection.
Returns TRUE if UART appears absent/removed, FALSE if it seems present.
UINT8 SerialIoDetectUartEnhanced (
Enable FIFOs, clear them
No UART: return TRUE = removed
Flush RBR
8250 detection: write 0x80, then 0x08, 0x20, 0x08
8250 without FIFO
goto DeviceRemoved;
Extended test: toggle DTR and check MSR CTS change
if (mExtendedUartDetection) {
Toggle DTR in MCR
if ((Msr2 & MCR_DTR) != 0) {
Restore //
EFI Driver Binding Protocol instance (installed on image handle)
EFI_DRIVER_BINDING_PROTOCOL gSerialIoDriverBinding = {
EFI Component Name 2 Protocol instance
EFI_COMPONENT_NAME2_PROTOCOL gSerialIoComponentName2 = {
Supported languages for component name
GLOBAL_REMOVE_IF_UNREFERENCED CHAR8 *mSupportedLanguages[] = {
Add more languages as needed
Install Driver Binding Protocol and Component Name 2 Protocol
Status = EfiLibInstallDriverBindingComponentName2 (
Case 1: Controller has ISA I/O protocol (legacy ISA/ACPI UART)
Status = gBS->OpenProtocol (
Check ACPI device path for UART
Case 2: UART IO protocol or ACPI device path
if (HobGetAcpiDevicePathEnd (This, ControllerHandle, &AcpiEnd, 16) >= 0 &&
ACPI device path end found: this is an ACPI UART
Check that it has type=3, subtype=14 (Serial I/O device path)
if (RemainingDevicePath != NULL &&
Case 3: PCI UART - check for PCI I/O protocol
if (mSomeLoopbackEnableFlag == 1) {
Close protocol since we only need it for Supported()
Case 4: UART IO protocol on child UART device
Check for UART mode
Probe for 512 or 256 byte FIFO via UART IO protocol
This is used for MMIO UART controllers
Found 512-byte UART
goto Found512;
Walk device path looking for ACPI UART node
ParentPath = DevicePath;
not END
ACPI device path: check HID/UID
return EFI_SUCCESS;
No supported controller found
if (Status == EFI_ALREADY_STARTED) {
GUID for serial I/O device path (0x00180A03-...)
EFI_GUID SerialDevicePathGuid = EFI_SERIAL_IO_DEVICE_PATH_GUID;
GUID for ACPI UART HID
EFI_GUID AcpiUartHid = { 0x01031804, 0x9A9D, 0x3749
Probe for ISA I/O protocol first (legacy COM ports)
ISA UART: get resources (I/O base, IRQ)
Probe for device path + ACPI table (legacy UART enumeration)
Get the UART IO protocol
Query UART capabilities
MMIO UART with ISA I/O
ACPI enumerated UART
Enumerate PCI UARTs
if (UartIo->GetChildHandle != NULL) {
Channel found with FIFO type 0 (512 byte)
MMIO Private->InsideIo = FALSE;
Open parent channel
256 or 512 byte FIFO
Walk device path to compute device path for child
Probe for ACPI device path (UART IO protocol from HOB)
if (HobGetAcpiDevicePathEnd (
This may be an ACPI enumerated UART
Open protocols
No UART IO mode: try PCI I/O protocol
actually DeviceId high
Look up clock frequency and register width in PCI UART table
if (gPciUartTableStart != (UINT16)-1) {
UART IO protocol available
If no protocols found, fail
if (Private->PciIo == NULL && Private->BaseAddress == 0) {
Initialize UART hardware
UartWriteRegister (Private, R_UART_SCR, 0xAA);
UART not present or not responding
goto CleanupAll;
Probe for FIFO type
16750 (64-byte FIFO)
16550A (16-byte FIFO, rev A)
16550A (16-byte FIFO, rev B)
Set defaults for clock and register width if not yet configured
if (Private->ClockFrequency == 0) {
Standard PC UART clock
Initialize state
Set up Serial I/O Protocol function table
Actually points to self
Initialize default serial attributes
Set default attributes on hardware
Status = Private->SetAttributes (Private, 0, 0, 0, 0, 0, 0);
Copy device path attributes if present
if (RemainingDevicePath != NULL) {
Build device path for child
ChildDevicePath = HobDuplicateDevicePath (
Install Serial I/O Protocol on new child handle
Status = gBS->InstallMultipleProtocolInterfaces (
Open protocols (child handle)
if (Private->IsaAccess) {
Create timer for transmit ready monitoring
Status = gBS->CreateEvent (
Close protocols opened by Supported()
Status = gBS->CloseProtocol (
Stop the child: get private context from child handle
Determine which protocol was opened on the controller
Close the child protocol
Uninstall protocols from child handle
Free the private context
Get the private context from the child handle
Private = NULL;
Walk to the end of the device path
while (PathWalk->Type != 0x7F) { // END
Not an ACPI device path
Status = EFI_UNSUPPORTED;

Generated by HR650X BIOS Decompilation Project