1 /*
2 * PROJECT: ReactOS KDBG Kernel Debugger
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: IO interface for KDBG. Provides local KDBG versions
5 * of KdpPrintString, KdpPromptString and KdpDprintf.
6 * COPYRIGHT: Copyright 2023 Hermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #include "kdb.h"
13
14 /* FUNCTIONS *****************************************************************/
15
16 static KD_CONTEXT KdbgKdContext;
17
18 #undef KdSendPacket
19 #define pKdSendPacket KdSendPacket
20
21 #undef KdReceivePacket
22 #define pKdReceivePacket KdReceivePacket
23
24 static VOID
KdbPrintStringWorker(_In_ const CSTRING * Output,_In_ ULONG ApiNumber,_Inout_ PDBGKD_DEBUG_IO DebugIo,_Inout_ PSTRING Header,_Inout_ PSTRING Data)25 KdbPrintStringWorker(
26 _In_ const CSTRING* Output,
27 _In_ ULONG ApiNumber,
28 _Inout_ PDBGKD_DEBUG_IO DebugIo,
29 _Inout_ PSTRING Header,
30 _Inout_ PSTRING Data)
31 {
32 USHORT Length;
33 C_ASSERT(PACKET_MAX_SIZE >= sizeof(*DebugIo));
34
35 ASSERT((ApiNumber == DbgKdPrintStringApi) ||
36 (ApiNumber == DbgKdGetStringApi));
37
38 /* Make sure we don't exceed the KD Packet size */
39 Length = min(Output->Length, PACKET_MAX_SIZE - sizeof(*DebugIo));
40
41 /* Build the packet header */
42 DebugIo->ApiNumber = ApiNumber;
43 DebugIo->ProcessorLevel = 0; // (USHORT)KeProcessorLevel;
44 DebugIo->Processor = KeGetCurrentPrcb()->Number;
45
46 if (ApiNumber == DbgKdPrintStringApi)
47 DebugIo->u.PrintString.LengthOfString = Length;
48 else // if (ApiNumber == DbgKdGetStringApi)
49 DebugIo->u.GetString.LengthOfPromptString = Length;
50
51 Header->Length = sizeof(*DebugIo);
52 Header->Buffer = (PCHAR)DebugIo;
53
54 /* Build the data */
55 Data->Length = Length;
56 Data->Buffer = (PCHAR)Output->Buffer;
57
58 /* Send the packet */
59 /* IO packet: call KdTerm */
60 pKdSendPacket(PACKET_TYPE_KD_DEBUG_IO, Header, Data, &KdbgKdContext);
61 }
62
63 VOID
KdbPrintString(_In_ const CSTRING * Output)64 KdbPrintString(
65 _In_ const CSTRING* Output)
66 {
67 DBGKD_DEBUG_IO DebugIo;
68 STRING Header, Data;
69
70 KdbPrintStringWorker(Output, DbgKdPrintStringApi,
71 &DebugIo, &Header, &Data);
72 }
73
74 static BOOLEAN
KdbPromptStringWorker(_In_ const CSTRING * PromptString,_Inout_ PSTRING ResponseString)75 KdbPromptStringWorker(
76 _In_ const CSTRING* PromptString,
77 _Inout_ PSTRING ResponseString)
78 {
79 DBGKD_DEBUG_IO DebugIo;
80 STRING Header, Data;
81 ULONG Length;
82 KDSTATUS Status;
83
84 /* Print the prompt */
85 // DebugIo.u.GetString.LengthOfPromptString = Length;
86 DebugIo.u.GetString.LengthOfStringRead = ResponseString->MaximumLength;
87 KdbPrintStringWorker(PromptString, DbgKdGetStringApi,
88 &DebugIo, &Header, &Data);
89
90 /* Set the maximum lengths for the receive */
91 Header.MaximumLength = sizeof(DebugIo);
92 Data.MaximumLength = ResponseString->MaximumLength;
93 /* Build the data */
94 Data.Buffer = ResponseString->Buffer;
95
96 /* Enter receive loop */
97 do
98 {
99 /* Get our reply */
100 /* IO packet: call KdTerm */
101 Status = pKdReceivePacket(PACKET_TYPE_KD_DEBUG_IO,
102 &Header,
103 &Data,
104 &Length,
105 &KdbgKdContext);
106
107 /* Return TRUE if we need to resend */
108 if (Status == KdPacketNeedsResend)
109 return TRUE;
110
111 /* Loop until we succeed */
112 } while (Status != KdPacketReceived);
113
114 /* Don't copy back a larger response than there is room for */
115 Length = min(Length, ResponseString->MaximumLength);
116
117 /* We've got the string back; return the length */
118 ResponseString->Length = (USHORT)Length;
119
120 /* Success; we don't need to resend */
121 return FALSE;
122 }
123
124 USHORT
KdbPromptString(_In_ const CSTRING * PromptString,_Inout_ PSTRING ResponseString)125 KdbPromptString(
126 _In_ const CSTRING* PromptString,
127 _Inout_ PSTRING ResponseString)
128 {
129 /* Enter prompt loop: send the prompt and receive the response */
130 ResponseString->Length = 0;
131 while (KdbPromptStringWorker(PromptString, ResponseString))
132 {
133 /* Loop while we need to resend */
134 }
135 return ResponseString->Length;
136 }
137
138
139 VOID
KdbPutsN(_In_ PCCH String,_In_ USHORT Length)140 KdbPutsN(
141 _In_ PCCH String,
142 _In_ USHORT Length)
143 {
144 CSTRING Output;
145
146 Output.Buffer = String;
147 Output.Length = Output.MaximumLength = Length;
148 KdbPrintString(&Output);
149 }
150
151 VOID
KdbPuts(_In_ PCSTR String)152 KdbPuts(
153 _In_ PCSTR String)
154 {
155 KdbPutsN(String, (USHORT)strnlen(String, MAXUSHORT - sizeof(ANSI_NULL)));
156 }
157
158 VOID
159 __cdecl
KdbPrintf(_In_ PCSTR Format,...)160 KdbPrintf(
161 _In_ PCSTR Format,
162 ...)
163 {
164 va_list ap;
165 SIZE_T Length;
166 CHAR Buffer[1024];
167
168 /* Format the string */
169 va_start(ap, Format);
170 Length = _vsnprintf(Buffer,
171 sizeof(Buffer),
172 Format,
173 ap);
174 Length = min(Length, MAXUSHORT - sizeof(ANSI_NULL));
175 va_end(ap);
176
177 /* Send it to the debugger directly */
178 KdbPutsN(Buffer, (USHORT)Length);
179 }
180
181 SIZE_T
KdbPrompt(_In_ PCSTR Prompt,_Out_ PCHAR Buffer,_In_ SIZE_T Size)182 KdbPrompt(
183 _In_ PCSTR Prompt,
184 _Out_ PCHAR Buffer,
185 _In_ SIZE_T Size)
186 {
187 CSTRING PromptString;
188 STRING ResponseBuffer;
189
190 PromptString.Buffer = Prompt;
191 PromptString.Length = PromptString.MaximumLength =
192 (USHORT)strnlen(Prompt, MAXUSHORT - sizeof(ANSI_NULL));
193
194 ResponseBuffer.Buffer = Buffer;
195 ResponseBuffer.Length = 0;
196 ResponseBuffer.MaximumLength = (USHORT)min(Size, MAXUSHORT);
197
198 return KdbPromptString(&PromptString, &ResponseBuffer);
199 }
200
201 /* EOF */
202