[FREELDR] Formatting only.
[reactos.git] / boot / freeldr / freeldr / lib / fs / pxe.c
1 /*
2 * FreeLoader
3 * Copyright (C) 2011 Hervé Poussineau
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <freeldr.h>
21
22 #include <debug.h>
23 DBG_DEFAULT_CHANNEL(FILESYSTEM);
24
25 #define TAG_PXE_FILE 'FexP'
26 #define NO_FILE ((ULONG)-1)
27
28 static IP4 _ServerIP = { 0, };
29 static ULONG _OpenFile = NO_FILE;
30 static CHAR _OpenFileName[128];
31 static ULONG _FileSize = 0;
32 static ULONG _FilePosition = 0;
33 static ULONG _PacketPosition = 0;
34 static UCHAR _Packet[1024]; // Should be a value which can be transferred well in one packet over the network
35 static UCHAR* _CachedFile = NULL;
36 static ULONG _CachedLength = 0;
37
38 static PPXE
39 FindPxeStructure(VOID)
40 {
41 PPXE Ptr;
42 UCHAR Checksum;
43 UCHAR i;
44
45 /* Find the '!PXE' structure */
46 Ptr = (PPXE)0xA0000;
47 while ((ULONG_PTR)Ptr > 0x10000)
48 {
49 Ptr = (PPXE)((ULONG_PTR)Ptr - 0x10);
50
51 /* Look for signature */
52 if (memcmp(Ptr, "!PXE", 4) != 0)
53 continue;
54
55 /* Check size */
56 if (Ptr->StructLength != sizeof(PXE))
57 continue;
58
59 /* Check checksum */
60 Checksum = 0;
61 for (i = 0; i < Ptr->StructLength; i++)
62 Checksum += *((PUCHAR)Ptr + i);
63 if (Checksum != 0)
64 continue;
65
66 TRACE("!PXE structure found at %p\n", Ptr);
67 return Ptr;
68 }
69
70 return NULL;
71 }
72
73 static PPXE GetPxeStructure(VOID)
74 {
75 static PPXE pPxe = NULL;
76 static BOOLEAN bPxeSearched = FALSE;
77 if (!bPxeSearched)
78 {
79 pPxe = FindPxeStructure();
80 bPxeSearched = TRUE;
81 }
82 return pPxe;
83 }
84
85 extern PXENV_EXIT __cdecl PxeCallApi(UINT16 Segment, UINT16 Offset, UINT16 Service, VOID *Parameter);
86 BOOLEAN CallPxe(UINT16 Service, PVOID Parameter)
87 {
88 PPXE pxe;
89 PXENV_EXIT exit;
90
91 pxe = GetPxeStructure();
92 if (!pxe)
93 return FALSE;
94
95 if (Service != PXENV_TFTP_READ)
96 {
97 // HACK: this delay shouldn't be necessary
98 KeStallExecutionProcessor(100 * 1000); // 100 ms
99 TRACE("PxeCall(0x%x, %p)\n", Service, Parameter);
100 }
101
102 exit = PxeCallApi(pxe->EntryPointSP.segment, pxe->EntryPointSP.offset, Service, Parameter);
103 if (exit != PXENV_EXIT_SUCCESS)
104 {
105 ERR("PxeCall(0x%x, %p) failed with exit=%d status=0x%x\n",
106 Service, Parameter, exit, *(PXENV_STATUS*)Parameter);
107 return FALSE;
108 }
109 if (*(PXENV_STATUS*)Parameter != PXENV_STATUS_SUCCESS)
110 {
111 ERR("PxeCall(0x%x, %p) succeeded, but returned error status 0x%x\n",
112 Service, Parameter, *(PXENV_STATUS*)Parameter);
113 return FALSE;
114 }
115 return TRUE;
116 }
117
118 static ARC_STATUS PxeClose(ULONG FileId)
119 {
120 t_PXENV_TFTP_CLOSE closeData;
121
122 if (_OpenFile == NO_FILE || FileId != _OpenFile)
123 return EBADF;
124
125 RtlZeroMemory(&closeData, sizeof(closeData));
126 if (!CallPxe(PXENV_TFTP_CLOSE, &closeData))
127 return EIO;
128
129 _OpenFile = NO_FILE;
130 if (_CachedFile)
131 {
132 FrLdrTempFree(_CachedFile, TAG_PXE_FILE);
133 _CachedFile = NULL;
134 }
135 return ESUCCESS;
136 }
137
138 static ARC_STATUS PxeGetFileInformation(ULONG FileId, FILEINFORMATION* Information)
139 {
140 if (_OpenFile == NO_FILE || FileId != _OpenFile)
141 return EBADF;
142
143 RtlZeroMemory(Information, sizeof(*Information));
144 Information->EndingAddress.LowPart = _FileSize;
145 Information->CurrentAddress.LowPart = _FilePosition;
146
147 TRACE("PxeGetFileInformation(%lu) -> FileSize = %lu, FilePointer = 0x%lx\n",
148 FileId, Information->EndingAddress.LowPart, Information->CurrentAddress.LowPart);
149
150 return ESUCCESS;
151 }
152
153 static ARC_STATUS PxeOpen(CHAR* Path, OPENMODE OpenMode, ULONG* FileId)
154 {
155 t_PXENV_TFTP_GET_FSIZE sizeData;
156 t_PXENV_TFTP_OPEN openData;
157 SIZE_T PathLen, i;
158
159 if (_OpenFile != NO_FILE)
160 return EIO;
161 if (OpenMode != OpenReadOnly)
162 return EACCES;
163
164 /* Retrieve the path length without NULL terminator */
165 PathLen = (Path ? min(strlen(Path), sizeof(_OpenFileName) - 1) : 0);
166
167 /* Lowercase the path and always use slashes as separators */
168 for (i = 0; i < PathLen; i++)
169 {
170 if (Path[i] == '\\')
171 _OpenFileName[i] = '/';
172 else
173 _OpenFileName[i] = tolower(Path[i]);
174 }
175
176 /* Zero out rest of the file name */
177 RtlZeroMemory(_OpenFileName + PathLen, sizeof(_OpenFileName) - PathLen);
178
179 RtlZeroMemory(&sizeData, sizeof(sizeData));
180 sizeData.ServerIPAddress = _ServerIP;
181 RtlCopyMemory(sizeData.FileName, _OpenFileName, sizeof(_OpenFileName));
182 if (!CallPxe(PXENV_TFTP_GET_FSIZE, &sizeData))
183 {
184 ERR("Failed to get '%s' size\n", Path);
185 return EIO;
186 }
187
188 _FileSize = sizeData.FileSize;
189 if (_FileSize < 1024 * 1024)
190 {
191 _CachedFile = FrLdrTempAlloc(_FileSize, TAG_PXE_FILE);
192 // Don't check for allocation failure, we support _CachedFile == NULL
193 }
194 _CachedLength = 0;
195
196 RtlZeroMemory(&openData, sizeof(openData));
197 openData.ServerIPAddress = _ServerIP;
198 RtlCopyMemory(openData.FileName, _OpenFileName, sizeof(_OpenFileName));
199 openData.PacketSize = sizeof(_Packet);
200
201 if (!CallPxe(PXENV_TFTP_OPEN, &openData))
202 {
203 if (_CachedFile)
204 {
205 FrLdrTempFree(_CachedFile, TAG_PXE_FILE);
206 _CachedFile = NULL;
207 }
208 return ENOENT;
209 }
210
211 _FilePosition = 0;
212 _PacketPosition = 0;
213
214 _OpenFile = *FileId;
215 return ESUCCESS;
216 }
217
218 static ARC_STATUS PxeRead(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count)
219 {
220 t_PXENV_TFTP_READ readData;
221 ULONG i;
222
223 *Count = 0;
224
225 if (_OpenFile == NO_FILE || FileId != _OpenFile)
226 return EBADF;
227
228 RtlZeroMemory(&readData, sizeof(readData));
229 readData.Buffer.segment = ((ULONG_PTR)_Packet & 0xf0000) / 16;
230 readData.Buffer.offset = (ULONG_PTR)_Packet & 0xffff;
231
232 // Get new packets as required
233 while (N > 0)
234 {
235 if (N < _CachedLength - _FilePosition)
236 i = N;
237 else
238 i = _CachedLength - _FilePosition;
239 if (_CachedFile)
240 RtlCopyMemory(Buffer, _CachedFile + _FilePosition, i);
241 else
242 RtlCopyMemory(Buffer, _Packet + _FilePosition - _PacketPosition, i);
243 _FilePosition += i;
244 Buffer = (UCHAR*)Buffer + i;
245 *Count += i;
246 N -= i;
247 if (N == 0)
248 break;
249
250 if (!CallPxe(PXENV_TFTP_READ, &readData))
251 return EIO;
252 if (_CachedFile)
253 RtlCopyMemory(_CachedFile + _CachedLength, _Packet, readData.BufferSize);
254 _PacketPosition = _CachedLength;
255 _CachedLength += readData.BufferSize;
256 }
257
258 return ESUCCESS;
259 }
260
261 static ARC_STATUS PxeSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
262 {
263 t_PXENV_TFTP_READ readData;
264
265 if (_OpenFile == NO_FILE || FileId != _OpenFile)
266 return EBADF;
267
268 if (Position->HighPart != 0 || SeekMode != SeekAbsolute)
269 return EINVAL;
270
271 if (!_CachedFile && Position->LowPart < _FilePosition)
272 {
273 // Close and reopen the file to go to position 0
274 if (PxeClose(FileId) != ESUCCESS)
275 return EIO;
276 if (PxeOpen(_OpenFileName, OpenReadOnly, &FileId) != ESUCCESS)
277 return EIO;
278 }
279
280 RtlZeroMemory(&readData, sizeof(readData));
281 readData.Buffer.segment = ((ULONG_PTR)_Packet & 0xf0000) / 16;
282 readData.Buffer.offset = (ULONG_PTR)_Packet & 0xffff;
283
284 // Get new packets as required
285 while (Position->LowPart > _CachedLength)
286 {
287 if (!CallPxe(PXENV_TFTP_READ, &readData))
288 return EIO;
289 if (_CachedFile)
290 {
291 RtlCopyMemory(_CachedFile + _CachedLength, _Packet, readData.BufferSize);
292 }
293 _PacketPosition = _CachedLength;
294 _CachedLength += readData.BufferSize;
295 }
296
297 _FilePosition = Position->LowPart;
298 return ESUCCESS;
299 }
300
301 static const DEVVTBL PxeVtbl = {
302 PxeClose,
303 PxeGetFileInformation,
304 PxeOpen,
305 PxeRead,
306 PxeSeek,
307 };
308
309 const DEVVTBL* PxeMount(ULONG DeviceId)
310 {
311 if (GetPxeStructure() == NULL)
312 return NULL;
313 return &PxeVtbl;
314 }
315
316 static ARC_STATUS PxeDiskClose(ULONG FileId)
317 {
318 // Nothing to do
319 return ESUCCESS;
320 }
321
322 static ARC_STATUS PxeDiskGetFileInformation(ULONG FileId, FILEINFORMATION* Information)
323 {
324 // No disk access in PXE mode
325 return EINVAL;
326 }
327
328 static ARC_STATUS PxeDiskOpen(CHAR* Path, OPENMODE OpenMode, ULONG* FileId)
329 {
330 // Nothing to do
331 return ESUCCESS;
332 }
333
334 static ARC_STATUS PxeDiskRead(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count)
335 {
336 // No disk access in PXE mode
337 return EINVAL;
338 }
339
340 static ARC_STATUS PxeDiskSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
341 {
342 // No disk access in PXE mode
343 return EINVAL;
344 }
345
346 static const DEVVTBL PxeDiskVtbl = {
347 PxeDiskClose,
348 PxeDiskGetFileInformation,
349 PxeDiskOpen,
350 PxeDiskRead,
351 PxeDiskSeek,
352 };
353
354 static BOOLEAN GetCachedInfo(VOID)
355 {
356 t_PXENV_GET_CACHED_INFO Data;
357 BOOLEAN res;
358 UCHAR* Packet;
359
360 RtlZeroMemory(&Data, sizeof(Data));
361 Data.PacketType = PXENV_PACKET_TYPE_CACHED_REPLY;
362
363 res = CallPxe(PXENV_GET_CACHED_INFO, &Data);
364 if (!res)
365 return FALSE;
366 if (Data.BufferSize < 36)
367 return FALSE;
368 Packet = (UCHAR*)((ULONG_PTR)(Data.Buffer.segment << 4) + Data.Buffer.offset);
369 RtlCopyMemory(&_ServerIP, Packet + 20, sizeof(IP4));
370 return TRUE;
371 }
372
373 BOOLEAN PxeInit(VOID)
374 {
375 static BOOLEAN Initialized = FALSE;
376 static BOOLEAN Success = FALSE;
377
378 // Do initialization only once
379 if (Initialized)
380 return Success;
381 Initialized = TRUE;
382
383 // Check if PXE is available
384 if (GetPxeStructure() && GetCachedInfo())
385 {
386 FsRegisterDevice("net(0)", &PxeDiskVtbl);
387 Success = TRUE;
388 }
389
390 return Success;
391 }
392