[CMAKE]
[reactos.git] / boot / freeldr / freeldr / windows / headless.c
1 /*
2 * PROJECT: ReactOS Boot Loader
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: boot/freeldr/windows/headless.c
5 * PURPOSE: Provides support for Windows Emergency Management Services
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include <freeldr.h>
12 #include <cportlib/cportlib.h>
13
14 /* Note: Move these to some smbios.h header */
15 #define SYSID_TYPE_UUID "_UUID_"
16 #define SYSID_UUID_DATA_SIZE 16
17 #include <pshpack1.h>
18 typedef struct _SYSID_UUID_ENTRY
19 {
20 UCHAR Type[6];
21 UCHAR Checksum;
22 USHORT Length;
23 UCHAR UUID[SYSID_UUID_DATA_SIZE];
24 } SYSID_UUID_ENTRY, *PSYSID_UUID_ENTRY;
25 #include <poppack.h>
26
27 /* GLOBALS ********************************************************************/
28
29 HEADLESS_LOADER_BLOCK LoaderRedirectionInformation;
30 BOOLEAN WinLdrTerminalConnected;
31 ULONG WinLdrTerminalDeviceId;
32 ULONG WinLdrTerminalDelay;
33
34 CPPORT Port[4] =
35 {
36 {NULL, 0, TRUE},
37 {NULL, 0, TRUE},
38 {NULL, 0, TRUE},
39 {NULL, 0, TRUE}
40 };
41
42 /* FUNCTIONS ******************************************************************/
43
44 VOID
45 WinLdrLoadGUID(OUT PGUID SystemGuid)
46 {
47 PSYSID_UUID_ENTRY CurrentAddress;
48
49 CurrentAddress = (PSYSID_UUID_ENTRY)0xE0000;
50 while (CurrentAddress < (PSYSID_UUID_ENTRY)0x100000)
51 {
52 if (RtlCompareMemory(&CurrentAddress->Type, SYSID_TYPE_UUID, 6) == 6)
53 {
54 RtlCopyMemory(SystemGuid, &CurrentAddress->UUID, SYSID_UUID_DATA_SIZE);
55 return;
56 }
57 CurrentAddress = (PSYSID_UUID_ENTRY)((ULONG_PTR)CurrentAddress + 1);
58 }
59
60 RtlZeroMemory(SystemGuid, SYSID_UUID_DATA_SIZE);
61 }
62
63 BOOLEAN
64 WinLdrPortInitialize(IN ULONG BaudRate,
65 IN ULONG PortNumber,
66 IN PUCHAR PortAddress,
67 IN BOOLEAN TerminalConnected,
68 OUT PULONG PortId)
69 {
70 /* Set default baud rate */
71 if (BaudRate == 0) BaudRate = 19200;
72
73 /* Check if port or address given */
74 if (PortNumber)
75 {
76 /* Pick correct address for port */
77 if (!PortAddress)
78 {
79 switch (PortNumber)
80 {
81 case 1:
82 PortAddress = (PUCHAR)0x3F8;
83 break;
84
85 case 2:
86 PortAddress = (PUCHAR)0x2F8;
87 break;
88
89 case 3:
90 PortAddress = (PUCHAR)0x3E8;
91 break;
92
93 default:
94 PortNumber = 4;
95 PortAddress = (PUCHAR)0x2E8;
96 }
97 }
98 }
99 else
100 {
101 /* Pick correct port for address */
102 PortAddress = (PUCHAR)0x2F8;
103 if (CpDoesPortExist(PortAddress))
104 {
105 PortNumber = 2;
106 }
107 else
108 {
109 PortAddress = (PUCHAR)0x3F8;
110 if (!CpDoesPortExist(PortAddress)) return FALSE;
111 PortNumber = 1;
112 }
113 }
114
115 /* Not yet supported */
116 ASSERT(LoaderRedirectionInformation.IsMMIODevice == FALSE);
117
118 /* Check if port exists */
119 if ((CpDoesPortExist(PortAddress)) || (CpDoesPortExist(PortAddress)))
120 {
121 /* Initialize port for first time, or re-initialize if specified */
122 if (((TerminalConnected) && (Port[PortNumber - 1].Address)) ||
123 !(Port[PortNumber - 1].Address))
124 {
125 /* Initialize the port, return it */
126 CpInitialize(&Port[PortNumber - 1], PortAddress, BaudRate);
127 *PortId = PortNumber - 1;
128 return TRUE;
129 }
130 }
131
132 return FALSE;
133 }
134
135 VOID
136 WinLdrPortPutByte(IN ULONG PortId,
137 IN UCHAR Data)
138 {
139 CpPutByte(&Port[PortId], Data);
140 }
141
142 BOOLEAN
143 WinLdrPortGetByte(IN ULONG PortId,
144 OUT PUCHAR Data)
145 {
146 return CpGetByte(&Port[PortId], Data, TRUE, FALSE) == CP_GET_SUCCESS;
147 }
148
149 BOOLEAN
150 WinLdrPortPollOnly(IN ULONG PortId)
151 {
152 UCHAR Dummy;
153
154 return CpGetByte(&Port[PortId], &Dummy, FALSE, TRUE) == CP_GET_SUCCESS;
155 }
156
157 VOID
158 WinLdrEnableFifo(IN ULONG PortId,
159 IN BOOLEAN Enable)
160 {
161 CpEnableFifo(Port[PortId].Address, Enable);
162 }
163
164 VOID
165 WinLdrInitializeHeadlessPort(VOID)
166 {
167 ULONG PortNumber, BaudRate;
168 PUCHAR PortAddress;
169 PCHAR AnsiReset = "\x1B[m";
170 ULONG i;
171
172 PortNumber = LoaderRedirectionInformation.PortNumber;
173 PortAddress = LoaderRedirectionInformation.PortAddress;
174 BaudRate = LoaderRedirectionInformation.BaudRate;
175
176 /* Pick a port address */
177 if (PortNumber)
178 {
179 if (!PortAddress)
180 {
181 switch (PortNumber)
182 {
183 case 2:
184 LoaderRedirectionInformation.PortAddress = (PUCHAR)0x2F8;
185 break;
186
187 case 3:
188 LoaderRedirectionInformation.PortAddress = (PUCHAR)0x3E8;
189 break;
190
191 case 4:
192 LoaderRedirectionInformation.PortAddress = (PUCHAR)0x2E8;
193 break;
194
195 default:
196 LoaderRedirectionInformation.PortAddress = (PUCHAR)0x3F8;
197 break;
198 }
199 }
200 }
201 else
202 {
203 /* No number, so no EMS */
204 WinLdrTerminalConnected = FALSE;
205 return;
206 }
207
208 /* Call arch code to initialize the port */
209 PortAddress = LoaderRedirectionInformation.PortAddress;
210 WinLdrTerminalConnected = WinLdrPortInitialize(
211 BaudRate,
212 PortNumber,
213 PortAddress,
214 WinLdrTerminalConnected,
215 &WinLdrTerminalDeviceId);
216
217 if (WinLdrTerminalConnected)
218 {
219 /* Port seems usable, set it up and get the BIOS GUID */
220 WinLdrEnableFifo(WinLdrTerminalDeviceId, TRUE);
221
222 WinLdrLoadGUID(&LoaderRedirectionInformation.SystemGUID);
223
224 /* Calculate delay in us based on the baud, assume 9600 if none given */
225 if (!BaudRate)
226 {
227 BaudRate = 9600;
228 LoaderRedirectionInformation.BaudRate = BaudRate;
229 }
230
231 WinLdrTerminalDelay = (10 * 1000 * 1000) / (BaudRate / 10) / 6;
232
233 /* Sent an ANSI reset sequence to get the terminal up and running */
234 for (i = 0; i < strlen(AnsiReset); i++)
235 {
236 WinLdrPortPutByte(WinLdrTerminalDeviceId, AnsiReset[i]);
237 StallExecutionProcessor(WinLdrTerminalDelay);
238 }
239 }
240 }
241
242 VOID
243 WinLdrSetupEms(IN PCHAR BootOptions)
244 {
245 PCHAR RedirectPort;
246
247 /* Start fresh */
248 RtlZeroMemory(&LoaderRedirectionInformation, sizeof(HEADLESS_LOADER_BLOCK));
249
250 /* Use a direction port if one was given, or use ACPI to detect one instead */
251 RedirectPort = strstr(BootOptions, "/redirect=");
252
253 if (RedirectPort)
254 {
255 RedirectPort = strstr(RedirectPort, "com");
256 if (RedirectPort)
257 {
258 RedirectPort += sizeof("com") - 1;
259 LoaderRedirectionInformation.PortNumber = atoi(RedirectPort);
260 }
261 else
262 {
263 RedirectPort = strstr(RedirectPort, "usebiossettings");
264 if (RedirectPort)
265 {
266 UiDrawStatusText("ACPI SRT Table Not Supported...");
267 }
268 else
269 {
270 LoaderRedirectionInformation.PortAddress = (PUCHAR)strtoul(RedirectPort, 0, 16);
271 if (LoaderRedirectionInformation.PortAddress)
272 {
273 LoaderRedirectionInformation.PortNumber = 3;
274 }
275 }
276 }
277 }
278
279 /* Use a direction baudrate if one was given */
280 RedirectPort = strstr(BootOptions, "/redirectbaudrate=");
281 if (RedirectPort)
282 {
283 if (strstr(RedirectPort, "115200"))
284 {
285 LoaderRedirectionInformation.BaudRate = 115200;
286 }
287 else if (strstr(RedirectPort, "57600"))
288 {
289 LoaderRedirectionInformation.BaudRate = 57600;
290 }
291 else if (strstr(RedirectPort, "19200"))
292 {
293 LoaderRedirectionInformation.BaudRate = 19200;
294 }
295 else
296 {
297 LoaderRedirectionInformation.BaudRate = 9600;
298 }
299 }
300
301 /* Enable headless support if parameters were found */
302 if (LoaderRedirectionInformation.PortNumber)
303 {
304 if (!LoaderRedirectionInformation.BaudRate)
305 {
306 LoaderRedirectionInformation.BaudRate = 9600;
307 }
308
309 WinLdrInitializeHeadlessPort();
310 }
311 }