[FREELDR][KDCOM][KDGDB] Allow specifying the serial port address in the DEBUGPORT option

This is the same feature as the one that already exists in the
NTOS:KD!kdmain.c module used by the in-kernel KDBG debugger
(GCC builds), first introduced in commit 4ce30245de (r54473),
and documented at:
https://reactos.org/wiki/Debugging#Changing_the_serial_port_address

This feature is useful if one uses a PCI, PCIe, PCMCIA, or ExpressCard
serial card on real hardware (these cards are used e.g. with laptops
without a built-in serial port), or with AMT serial over LAN.
After determining the serial port I/O address (say, 0xCC00), specify
the debug port as follows:

/DEBUGPORT=COM:0xCC00

instead of the usual syntax: /DEBUGPORT=COMn (n an integer).

The same syntax can be used for debugging FreeLoader as well: in the
FREELDR.INI file, add a "Debug" line in the "FREELOADER" section,
as follows:

```
[FREELOADER]
Debug=/DEBUG /DEBUGPORT=COM:0xCC00 /BAUDRATE=115200
```

----

NOTE for KDCOM/KDGDB:

Since `strtoul()` is used, but isn't exported by ntoskrnl, link against
the "strtol" static library that has been introduced in commit d317d4fbcc
(r71481).
This commit is contained in:
Hermès Bélusca-Maïto
2026-03-21 00:10:21 +01:00
parent f77fcc15ff
commit 6c532e771e
9 changed files with 128 additions and 97 deletions
+4 -3
View File
@@ -9,11 +9,12 @@
#include <debug.h>
#define QEMUUART 0x09000000
volatile unsigned int * UART0DR = (unsigned int *) QEMUUART;
volatile unsigned int* UART0DR = (unsigned int*)QEMUUART;
BOOLEAN
Rs232PortInitialize(IN ULONG ComPort,
IN ULONG BaudRate)
Rs232PortInitialize(
_In_ PUCHAR PortAddress,
_In_ ULONG BaudRate)
{
return TRUE;
}
+5 -1
View File
@@ -22,7 +22,11 @@
#pragma once
#if DBG
BOOLEAN Rs232PortInitialize(ULONG ComPort, ULONG BaudRate);
BOOLEAN
Rs232PortInitialize(
_In_ PUCHAR PortAddress,
_In_ ULONG BaudRate);
BOOLEAN Rs232PortGetByte(PUCHAR ByteReceived);
// BOOLEAN Rs232PortPollByte(PUCHAR ByteReceived);
VOID Rs232PortPutByte(UCHAR ByteToSend);
+24 -32
View File
@@ -33,82 +33,74 @@
/* STATIC VARIABLES ***********************************************************/
/* The COM port must only be initialized once! */
static ULONG Rs232ComPort = 0;
static PUCHAR Rs232ComPort = NULL;
static CPPORT Rs232ComPortInfo;
/* FUNCTIONS ******************************************************************/
BOOLEAN Rs232PortInitialize(IN ULONG ComPort,
IN ULONG BaudRate)
BOOLEAN
Rs232PortInitialize(
_In_ PUCHAR PortAddress,
_In_ ULONG BaudRate)
{
NTSTATUS Status;
PUCHAR Address;
/*
* Check whether it's the first time we initialize a COM port.
* If not, check whether the specified one was already initialized.
*/
if ((Rs232ComPort != 0) && (Rs232ComPort == ComPort))
/* Check whether it's the first time we initialize a COM port.
* If not, check whether the specified one was already initialized. */
if (Rs232ComPort && (Rs232ComPort == PortAddress))
return TRUE;
if (BaudRate == 0)
BaudRate = DEFAULT_BAUD_RATE;
if (ComPort == 0)
if (!PortAddress)
{
/*
* Enumerate COM ports from the last to the first one, and stop
* when we find a valid port. If we reach the first list element
* (the undefined COM port), no valid port was found.
*/
ULONG ComPort;
for (ComPort = MAX_COM_PORTS; ComPort > 0; ComPort--)
{
if (CpDoesPortExist(UlongToPtr(BaseArray[ComPort])))
{
Address = UlongToPtr(BaseArray[ComPort]);
PortAddress = UlongToPtr(BaseArray[ComPort]);
if (CpDoesPortExist(PortAddress))
break;
}
}
if (ComPort == 0)
return FALSE;
}
else if (ComPort <= MAX_COM_PORTS)
{
if (CpDoesPortExist(UlongToPtr(BaseArray[ComPort])))
Address = UlongToPtr(BaseArray[ComPort]);
else
if (ComPort == 0 || PortAddress == NULL)
return FALSE;
}
else
{
return FALSE;
if (!CpDoesPortExist(PortAddress))
return FALSE;
}
Status = CpInitialize(&Rs232ComPortInfo, Address, BaudRate);
if (!NT_SUCCESS(Status)) return FALSE;
Rs232ComPort = ComPort;
Status = CpInitialize(&Rs232ComPortInfo, PortAddress, BaudRate);
if (!NT_SUCCESS(Status))
return FALSE;
Rs232ComPort = Rs232ComPortInfo.Address;
return TRUE;
}
BOOLEAN Rs232PortGetByte(PUCHAR ByteReceived)
{
if (Rs232ComPort == 0) return FALSE;
if (!Rs232ComPort) return FALSE;
return (CpGetByte(&Rs232ComPortInfo, ByteReceived, TRUE, FALSE) == CP_GET_SUCCESS);
}
/*
BOOLEAN Rs232PortPollByte(PUCHAR ByteReceived)
{
if (Rs232ComPort == 0) return FALSE;
if (!Rs232ComPort) return FALSE;
return (CpGetByte(&Rs232ComPortInfo, ByteReceived, FALSE, FALSE) == CP_GET_SUCCESS);
}
*/
VOID Rs232PortPutByte(UCHAR ByteToSend)
{
if (Rs232ComPort == 0) return;
if (!Rs232ComPort) return;
CpPutByte(&Rs232ComPortInfo, ByteToSend);
}
@@ -117,10 +109,10 @@ VOID Rs232PortPutByte(UCHAR ByteToSend)
BOOLEAN Rs232PortInUse(PUCHAR Base)
{
#if DBG
return ( ((Rs232ComPort != 0) && (Rs232ComPortInfo.Address == Base)) ? TRUE : FALSE );
return (Rs232ComPort && (Rs232ComPortInfo.Address == Base));
#else
return FALSE;
#endif
}
#endif /* not _M_ARM */
#endif /* !_M_ARM */
+19 -11
View File
@@ -44,16 +44,12 @@ ULONG DebugPort = RS232;
/* Serial debug connection */
#include <cportlib/uartinfo.h>
ULONG BaudRate = DEFAULT_DEBUG_BAUD_RATE;
ULONG ComPort = 0; // The COM port initializer chooses the first available port starting from COM4 down to COM1.
ULONG ComPortBaudRate = DEFAULT_DEBUG_BAUD_RATE;
// The COM port initializer chooses the first available port starting from COM4 down to COM1.
PUCHAR ComPortAddress = NULL;
BOOLEAN DebugStartOfLine = TRUE;
#ifdef UEFIBOOT
VOID
ARMWriteToUART(UCHAR Data);
#endif
VOID
DebugInit(
_In_ PCSTR DebugString)
@@ -138,8 +134,20 @@ DebugInit(
DebugPort |= RS232;
/* Set the port to use */
Value = (ULONG)atol(PortString);
if (Value) ComPort = Value;
if (*PortString != ':')
{
/* Read the port and set its address */
Value = (ULONG)atol(PortString);
if (Value > 0 && Value <= MAX_COM_PORTS)
ComPortAddress = UlongToPtr(BaseArray[Value]);
}
else
{
/* Retrieve and set its address */
Value = strtoul(PortString + 1, NULL, 0);
if (Value)
ComPortAddress = UlongToPtr(Value);
}
}
PortString = strstr(PortString, "DEBUGPORT");
@@ -157,7 +165,7 @@ DebugInit(
{
/* Read and set it */
Value = (ULONG)atol(BaudString + 1);
if (Value) BaudRate = Value;
if (Value) ComPortBaudRate = Value;
}
}
@@ -167,7 +175,7 @@ Done:
/* Try to initialize the port; if it fails, remove the corresponding flag */
if (DebugPort & RS232)
{
if (!Rs232PortInitialize(ComPort, BaudRate))
if (!Rs232PortInitialize(ComPortAddress, ComPortBaudRate))
DebugPort &= ~RS232;
}
}
+1 -1
View File
@@ -15,7 +15,7 @@ add_library(kdcom MODULE
set_module_type(kdcom kerneldll ENTRYPOINT 0)
set_subsystem(kdcom native)
add_importlibs(kdcom ntoskrnl hal)
target_link_libraries(kdcom cportlib)
target_link_libraries(kdcom cportlib strtol)
add_dependencies(kdcom psdk bugcodes)
add_pch(kdcom kddll.h SOURCE)
add_cd_file(TARGET kdcom DESTINATION reactos/system32 NO_CAB FOR all)
+36 -26
View File
@@ -94,25 +94,18 @@ KdRestore(IN BOOLEAN SleepTransition)
NTSTATUS
NTAPI
KdpPortInitialize(IN ULONG ComPortNumber,
IN ULONG ComPortBaudRate)
KdpPortInitialize(
_In_ PUCHAR PortAddress,
_In_ ULONG BaudRate)
{
NTSTATUS Status;
KDDBGPRINT("KdpPortInitialize, Port = COM%ld\n", ComPortNumber);
Status = CpInitialize(&KdComPort,
UlongToPtr(BaseArray[ComPortNumber]),
ComPortBaudRate);
Status = CpInitialize(&KdComPort, PortAddress, BaudRate);
if (!NT_SUCCESS(Status))
{
return STATUS_INVALID_PARAMETER;
}
else
{
KdComPortInUse = KdComPort.Address;
return STATUS_SUCCESS;
}
KdComPortInUse = KdComPort.Address;
return STATUS_SUCCESS;
}
/******************************************************************************
@@ -129,6 +122,7 @@ KdDebuggerInitialize0(IN PLOADER_PARAMETER_BLOCK LoaderBlock OPTIONAL)
ULONG ComPortNumber = DEFAULT_DEBUG_PORT;
ULONG ComPortBaudRate = DEFAULT_DEBUG_BAUD_RATE;
PUCHAR ComPortAddress = NULL;
PSTR CommandLine, PortString, BaudString;
ULONG Value;
@@ -161,12 +155,25 @@ KdDebuggerInitialize0(IN PLOADER_PARAMETER_BLOCK LoaderBlock OPTIONAL)
/* Check for a valid serial port */
PortString += CONST_STR_LEN("COM");
Value = (ULONG)atol(PortString);
if (Value > MAX_COM_PORTS)
return STATUS_INVALID_PARAMETER;
// if (Value > 0 && Value <= MAX_COM_PORTS)
/* Set the port to use */
ComPortNumber = Value;
if (*PortString != ':')
{
Value = (ULONG)atol(PortString);
if (Value > MAX_COM_PORTS)
return STATUS_INVALID_PARAMETER;
// if (Value > 0 && Value <= MAX_COM_PORTS)
/* Set the port to use */
ComPortNumber = Value;
}
else
{
/* Retrieve and set its address */
Value = strtoul(PortString + 1, NULL, 0);
if (Value)
{
ComPortNumber = 0;
ComPortAddress = UlongToPtr(Value);
}
}
}
/* Check if we got a baud rate */
@@ -186,6 +193,9 @@ KdDebuggerInitialize0(IN PLOADER_PARAMETER_BLOCK LoaderBlock OPTIONAL)
}
}
if (!ComPortAddress)
ComPortAddress = UlongToPtr(BaseArray[ComPortNumber]);
#ifdef KDDEBUG
/*
* Try to find a free COM port and use it as the KD debugging port.
@@ -197,22 +207,22 @@ KdDebuggerInitialize0(IN PLOADER_PARAMETER_BLOCK LoaderBlock OPTIONAL)
* when we find a valid port. If we reach the first list element
* (the undefined COM port), no valid port was found.
*/
PUCHAR Address = NULL;
ULONG ComPort;
for (ComPort = MAX_COM_PORTS; ComPort > 0; ComPort--)
{
/* Check if the port exist; skip the KD port */
if ((ComPort != ComPortNumber) && CpDoesPortExist(UlongToPtr(BaseArray[ComPort])))
Address = UlongToPtr(BaseArray[ComPort]);
if ((Address != ComPortAddress) && CpDoesPortExist(Address))
break;
}
if (ComPort != 0)
CpInitialize(&KdDebugComPort, UlongToPtr(BaseArray[ComPort]), DEFAULT_BAUD_RATE);
if (ComPort != 0 && Address != NULL)
CpInitialize(&KdDebugComPort, Address, DEFAULT_BAUD_RATE);
}
#endif
KDDBGPRINT("KdDebuggerInitialize0\n");
/* Initialize the port */
return KdpPortInitialize(ComPortNumber, ComPortBaudRate);
return KdpPortInitialize(ComPortAddress, ComPortBaudRate);
}
/******************************************************************************
+1 -1
View File
@@ -25,6 +25,6 @@ add_library(kdcom MODULE
set_module_type(kdcom kerneldll ENTRYPOINT 0)
set_subsystem(kdcom native)
add_importlibs(kdcom ntoskrnl hal)
target_link_libraries(kdcom cportlib)
target_link_libraries(kdcom cportlib strtol)
add_pch(kdcom kdgdb.h SOURCE)
add_cd_file(TARGET kdcom DESTINATION reactos/system32 NO_CAB FOR all)
+33 -17
View File
@@ -92,18 +92,15 @@ KdRestore(IN BOOLEAN SleepTransition)
NTSTATUS
NTAPI
KdpPortInitialize(IN ULONG ComPortNumber,
IN ULONG ComPortBaudRate)
KdpPortInitialize(
_In_ PUCHAR PortAddress,
_In_ ULONG BaudRate)
{
NTSTATUS Status;
Status = CpInitialize(&KdComPort,
UlongToPtr(BaseArray[ComPortNumber]),
ComPortBaudRate);
Status = CpInitialize(&KdComPort, PortAddress, BaudRate);
if (!NT_SUCCESS(Status))
{
return STATUS_INVALID_PARAMETER;
}
KdComPortInUse = KdComPort.Address;
return STATUS_SUCCESS;
@@ -123,6 +120,7 @@ KdDebuggerInitialize0(IN PLOADER_PARAMETER_BLOCK LoaderBlock OPTIONAL)
ULONG ComPortNumber = DEFAULT_DEBUG_PORT;
ULONG ComPortBaudRate = DEFAULT_DEBUG_BAUD_RATE;
PUCHAR ComPortAddress = NULL;
PSTR CommandLine, PortString, BaudString;
ULONG Value;
@@ -155,12 +153,25 @@ KdDebuggerInitialize0(IN PLOADER_PARAMETER_BLOCK LoaderBlock OPTIONAL)
/* Check for a valid serial port */
PortString += CONST_STR_LEN("COM");
Value = (ULONG)atol(PortString);
if (Value > MAX_COM_PORTS)
return STATUS_INVALID_PARAMETER;
// if (Value > 0 && Value <= MAX_COM_PORTS)
/* Set the port to use */
ComPortNumber = Value;
if (*PortString != ':')
{
Value = (ULONG)atol(PortString);
if (Value > MAX_COM_PORTS)
return STATUS_INVALID_PARAMETER;
// if (Value > 0 && Value <= MAX_COM_PORTS)
/* Set the port to use */
ComPortNumber = Value;
}
else
{
/* Retrieve and set its address */
Value = strtoul(PortString + 1, NULL, 0);
if (Value)
{
ComPortNumber = 0;
ComPortAddress = UlongToPtr(Value);
}
}
}
/* Check if we got a baud rate */
@@ -180,6 +191,9 @@ KdDebuggerInitialize0(IN PLOADER_PARAMETER_BLOCK LoaderBlock OPTIONAL)
}
}
if (!ComPortAddress)
ComPortAddress = UlongToPtr(BaseArray[ComPortNumber]);
#ifdef KDDEBUG
/*
* Try to find a free COM port and use it as the KD debugging port.
@@ -191,20 +205,22 @@ KdDebuggerInitialize0(IN PLOADER_PARAMETER_BLOCK LoaderBlock OPTIONAL)
* when we find a valid port. If we reach the first list element
* (the undefined COM port), no valid port was found.
*/
PUCHAR Address = NULL;
ULONG ComPort;
for (ComPort = MAX_COM_PORTS; ComPort > 0; ComPort--)
{
/* Check if the port exist; skip the KD port */
if ((ComPort != ComPortNumber) && CpDoesPortExist(UlongToPtr(BaseArray[ComPort])))
Address = UlongToPtr(BaseArray[ComPort]);
if ((Address != ComPortAddress) && CpDoesPortExist(Address))
break;
}
if (ComPort != 0)
CpInitialize(&KdDebugComPort, UlongToPtr(BaseArray[ComPort]), DEFAULT_BAUD_RATE);
if (ComPort != 0 && Address != NULL)
CpInitialize(&KdDebugComPort, Address, DEFAULT_BAUD_RATE);
}
#endif
/* Initialize the port */
return KdpPortInitialize(ComPortNumber, ComPortBaudRate);
return KdpPortInitialize(ComPortAddress, ComPortBaudRate);
}
/******************************************************************************
+5 -5
View File
@@ -10,6 +10,7 @@
#include <ntoskrnl.h>
#include "kd.h"
#include "kdterminal.h"
#include <cportlib/uartinfo.h>
#define NDEBUG
#include <debug.h>
@@ -71,12 +72,10 @@ KdpGetDebugMode(
if (*p2 != ':')
{
Value = (ULONG)atol(p2);
if (Value > 0 && Value < 5)
if (Value > 0 && Value <= MAX_COM_PORTS)
{
/* Valid port found, enable Serial Debugging */
/* Valid port found, enable it and set the port to use */
KdpDebugMode.Serial = TRUE;
/* Set the port to use */
SerialPortNumber = Value;
}
}
@@ -85,9 +84,10 @@ KdpGetDebugMode(
Value = strtoul(p2 + 1, NULL, 0);
if (Value)
{
/* Valid port found, enable it and set its address */
KdpDebugMode.Serial = TRUE;
SerialPortInfo.Address = UlongToPtr(Value);
SerialPortNumber = 0;
SerialPortInfo.Address = UlongToPtr(Value);
}
}
}