From 6c532e771e8d54e55e40f010125c9aaa3c596f9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herm=C3=A8s=20B=C3=A9lusca-Ma=C3=AFto?= Date: Wed, 18 Mar 2026 21:48:42 +0100 Subject: [PATCH] [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). --- boot/freeldr/freeldr/arch/arm/debug.c | 7 +-- boot/freeldr/freeldr/include/comm.h | 6 ++- boot/freeldr/freeldr/lib/comm/rs232.c | 56 +++++++++++------------- boot/freeldr/freeldr/lib/debug.c | 30 ++++++++----- drivers/base/kdcom/CMakeLists.txt | 2 +- drivers/base/kdcom/kdcom.c | 62 ++++++++++++++++----------- drivers/base/kdgdb/CMakeLists.txt | 2 +- drivers/base/kdgdb/kdcom.c | 50 +++++++++++++-------- ntoskrnl/kd/kdmain.c | 10 ++--- 9 files changed, 128 insertions(+), 97 deletions(-) diff --git a/boot/freeldr/freeldr/arch/arm/debug.c b/boot/freeldr/freeldr/arch/arm/debug.c index ac1f13350db..38c056ee288 100644 --- a/boot/freeldr/freeldr/arch/arm/debug.c +++ b/boot/freeldr/freeldr/arch/arm/debug.c @@ -9,11 +9,12 @@ #include #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; } diff --git a/boot/freeldr/freeldr/include/comm.h b/boot/freeldr/freeldr/include/comm.h index d3ffb40f9e5..abfac0f2657 100644 --- a/boot/freeldr/freeldr/include/comm.h +++ b/boot/freeldr/freeldr/include/comm.h @@ -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); diff --git a/boot/freeldr/freeldr/lib/comm/rs232.c b/boot/freeldr/freeldr/lib/comm/rs232.c index 5aa56a2a09f..077f073ce4c 100644 --- a/boot/freeldr/freeldr/lib/comm/rs232.c +++ b/boot/freeldr/freeldr/lib/comm/rs232.c @@ -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 */ diff --git a/boot/freeldr/freeldr/lib/debug.c b/boot/freeldr/freeldr/lib/debug.c index 5be90faf1c0..d1e49172fc3 100644 --- a/boot/freeldr/freeldr/lib/debug.c +++ b/boot/freeldr/freeldr/lib/debug.c @@ -44,16 +44,12 @@ ULONG DebugPort = RS232; /* Serial debug connection */ #include -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; } } diff --git a/drivers/base/kdcom/CMakeLists.txt b/drivers/base/kdcom/CMakeLists.txt index f754c215200..84279b811a1 100644 --- a/drivers/base/kdcom/CMakeLists.txt +++ b/drivers/base/kdcom/CMakeLists.txt @@ -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) diff --git a/drivers/base/kdcom/kdcom.c b/drivers/base/kdcom/kdcom.c index 77bdc555152..2d8c1b55519 100644 --- a/drivers/base/kdcom/kdcom.c +++ b/drivers/base/kdcom/kdcom.c @@ -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); } /****************************************************************************** diff --git a/drivers/base/kdgdb/CMakeLists.txt b/drivers/base/kdgdb/CMakeLists.txt index 05c2f8f6e2e..28e88c2103a 100644 --- a/drivers/base/kdgdb/CMakeLists.txt +++ b/drivers/base/kdgdb/CMakeLists.txt @@ -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) diff --git a/drivers/base/kdgdb/kdcom.c b/drivers/base/kdgdb/kdcom.c index 83dde289b64..7dec0556af5 100644 --- a/drivers/base/kdgdb/kdcom.c +++ b/drivers/base/kdgdb/kdcom.c @@ -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); } /****************************************************************************** diff --git a/ntoskrnl/kd/kdmain.c b/ntoskrnl/kd/kdmain.c index 3978cc62c1d..41f82b90f0c 100644 --- a/ntoskrnl/kd/kdmain.c +++ b/ntoskrnl/kd/kdmain.c @@ -10,6 +10,7 @@ #include #include "kd.h" #include "kdterminal.h" +#include #define NDEBUG #include @@ -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); } } }