77c083547d5f35173a970405105021a578673db9
[reactos.git] / reactos / boot / freeldr / freeldr / 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 #define NDEBUG
23 #include <debug.h>
24
25 #define TAG_PXE_FILE 'FexP'
26 #define NO_FILE ((ULONG)-1)
27
28 DBG_DEFAULT_CHANNEL(FILESYSTEM);
29
30 static IP4 _ServerIP = { 0, };
31 static ULONG _OpenFile = NO_FILE;
32 static ULONG _FileSize = 0;
33 static ULONG _FilePosition = 0;
34 static ULONG _PacketPosition = 0;
35 static UCHAR _Packet[4096];
36 static UCHAR* _CachedFile = NULL;
37 static ULONG _CachedLength = 0;
38
39 static PPXE
40 FindPxeStructure(VOID)
41 {
42 PPXE Ptr;
43 UCHAR Checksum;
44 UCHAR i;
45
46 /* Find the '!PXE' structure */
47 Ptr = (PPXE)0xA0000;
48 while ((ULONG)Ptr > 0x10000)
49 {
50 Ptr = (PPXE)((ULONG)Ptr - 0x10);
51
52 /* Look for signature */
53 if (memcmp(Ptr, "!PXE", 4) != 0)
54 continue;
55
56 /* Check size */
57 if (Ptr->StructLength != sizeof(PXE))
58 continue;
59
60 /* Check checksum */
61 Checksum = 0;
62 for (i = 0; i < Ptr->StructLength; i++)
63 Checksum += *((PUCHAR)Ptr + i);
64 if (Checksum != 0)
65 continue;
66
67 TRACE("!PXE structure found at %p\n", Ptr);
68 return Ptr;
69 }
70
71 return NULL;
72 }
73
74 static PPXE GetPxeStructure(VOID)
75 {
76 static PPXE pPxe = NULL;
77 static BOOLEAN bPxeSearched = FALSE;
78 if (!bPxeSearched)
79 {
80 pPxe = FindPxeStructure();
81 bPxeSearched = TRUE;
82 }
83 return pPxe;
84 }
85
86 extern PXENV_EXIT PxeCallApi(UINT16 Segment, UINT16 Offset, UINT16 Service, VOID *Parameter);
87 BOOLEAN CallPxe(UINT16 Service, PVOID Parameter)
88 {
89 PPXE pxe;
90 PXENV_EXIT exit;
91
92 pxe = GetPxeStructure();
93 if (!pxe)
94 return FALSE;
95
96 if (Service != PXENV_TFTP_READ)
97 {
98 // HACK: this delay shouldn't be necessary
99 KeStallExecutionProcessor(100 * 1000); // 100 ms
100 TRACE("PxeCall(0x%x, %p)\n", Service, Parameter);
101 }
102
103 exit = PxeCallApi(pxe->EntryPointSP.segment, pxe->EntryPointSP.offset, Service, Parameter);
104 if (exit != PXENV_EXIT_SUCCESS)
105 {
106 ERR("PxeCall(0x%x, %p) failed with exit=%d status=0x%x\n",
107 Service, Parameter, exit, *(PXENV_STATUS*)Parameter);
108 return FALSE;
109 }
110 if (*(PXENV_STATUS*)Parameter != PXENV_STATUS_SUCCESS)
111 {
112 ERR("PxeCall(0x%x, %p) succeeded, but returned error status 0x%x\n",
113 Service, Parameter, *(PXENV_STATUS*)Parameter);
114 return FALSE;
115 }
116 return TRUE;
117 }
118
119 static LONG PxeClose(ULONG FileId)
120 {
121 t_PXENV_TFTP_CLOSE closeData;
122
123 if (_OpenFile == NO_FILE || FileId != _OpenFile)
124 return EBADF;
125
126 RtlZeroMemory(&closeData, sizeof(closeData));
127 if (!CallPxe(PXENV_TFTP_CLOSE, &closeData))
128 return EIO;
129
130 _OpenFile = NO_FILE;
131 if (_CachedFile)
132 {
133 FrLdrTempFree(_CachedFile, TAG_PXE_FILE);
134 _CachedFile = NULL;
135 }
136 return ESUCCESS;
137 }
138
139 static LONG PxeGetFileInformation(ULONG FileId, FILEINFORMATION* Information)
140 {
141 if (_OpenFile == NO_FILE || FileId != _OpenFile)
142 return EBADF;
143
144 RtlZeroMemory(Information, sizeof(FILEINFORMATION));
145 Information->EndingAddress.LowPart = _FileSize;
146 Information->CurrentAddress.LowPart = _FilePosition;
147
148 return ESUCCESS;
149 }
150
151 static LONG PxeOpen(CHAR* Path, OPENMODE OpenMode, ULONG* FileId)
152 {
153 t_PXENV_TFTP_GET_FSIZE sizeData;
154 t_PXENV_TFTP_OPEN openData;
155
156 if (_OpenFile != NO_FILE)
157 return EIO;
158 if (OpenMode != OpenReadOnly)
159 return EACCES;
160
161 RtlZeroMemory(&sizeData, sizeof(sizeData));
162 sizeData.ServerIPAddress = _ServerIP;
163 strncpy((CHAR*)sizeData.FileName, Path, sizeof(sizeData.FileName));
164 if (!CallPxe(PXENV_TFTP_GET_FSIZE, &sizeData))
165 return EIO;
166 _FileSize = sizeData.FileSize;
167 if (_FileSize < 1024 * 1024)
168 {
169 _CachedFile = FrLdrTempAlloc(_FileSize, TAG_PXE_FILE);
170 // Don't check for allocation failure, we support _CachedFile = NULL
171 }
172 _CachedLength = 0;
173
174 RtlZeroMemory(&openData, sizeof(openData));
175 openData.ServerIPAddress = _ServerIP;
176 strncpy((CHAR*)openData.FileName, Path, sizeof(openData.FileName));
177 openData.PacketSize = sizeof(_Packet);
178
179 if (!CallPxe(PXENV_TFTP_OPEN, &openData))
180 {
181 if (_CachedFile)
182 {
183 FrLdrTempFree(_CachedFile, TAG_PXE_FILE);
184 _CachedFile = NULL;
185 }
186 return ENOENT;
187 }
188
189 _FilePosition = 0;
190 _PacketPosition = 0;
191
192 _OpenFile = *FileId;
193 return ESUCCESS;
194 }
195
196 static LONG PxeRead(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count)
197 {
198 t_PXENV_TFTP_READ readData;
199 ULONG i;
200
201 *Count = 0;
202
203 if (_OpenFile == NO_FILE || FileId != _OpenFile)
204 return EBADF;
205
206 RtlZeroMemory(&readData, sizeof(readData));
207 readData.Buffer.segment = ((UINT32)_Packet & 0xf0000) / 16;
208 readData.Buffer.offset = (UINT32)_Packet & 0xffff;
209
210 // Get new packets as required
211 while (N > 0)
212 {
213 if (N < _CachedLength - _FilePosition)
214 i = N;
215 else
216 i = _CachedLength - _FilePosition;
217 if (_CachedFile)
218 RtlCopyMemory(Buffer, _CachedFile + _FilePosition, i);
219 else
220 RtlCopyMemory(Buffer, _Packet + _FilePosition - _PacketPosition, i);
221 _FilePosition += i;
222 Buffer = (UCHAR*)Buffer + i;
223 *Count += i;
224 N -= i;
225 if (N == 0)
226 break;
227
228 if (!CallPxe(PXENV_TFTP_READ, &readData))
229 return EIO;
230 if (_CachedFile)
231 RtlCopyMemory(_CachedFile + _CachedLength, _Packet, readData.BufferSize);
232 _PacketPosition = _CachedLength;
233 _CachedLength += readData.BufferSize;
234 }
235
236 return ESUCCESS;
237 }
238
239 static LONG PxeSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
240 {
241 t_PXENV_TFTP_READ readData;
242
243 if (_OpenFile == NO_FILE || FileId != _OpenFile)
244 return EBADF;
245
246 if (Position->HighPart != 0 || SeekMode != SeekAbsolute)
247 return EINVAL;
248
249 if (!_CachedFile && Position->LowPart < _FilePosition)
250 // We don't support backward seek without caching
251 return EINVAL;
252
253 RtlZeroMemory(&readData, sizeof(readData));
254 readData.Buffer.segment = ((UINT32)_Packet & 0xf0000) / 16;
255 readData.Buffer.offset = (UINT32)_Packet & 0xffff;
256
257 // Get new packets as required
258 while (Position->LowPart > _CachedLength)
259 {
260 if (!CallPxe(PXENV_TFTP_READ, &readData))
261 return EIO;
262 if (_CachedFile)
263 {
264 RtlCopyMemory(_CachedFile + _CachedLength, _Packet, readData.BufferSize);
265 }
266 _PacketPosition = _CachedLength;
267 _CachedLength += readData.BufferSize;
268 }
269
270 _FilePosition = Position->LowPart;
271 return ESUCCESS;
272 }
273
274 static const DEVVTBL PxeVtbl = {
275 PxeClose,
276 PxeGetFileInformation,
277 PxeOpen,
278 PxeRead,
279 PxeSeek,
280 };
281
282 const DEVVTBL* PxeMount(ULONG DeviceId)
283 {
284 if (GetPxeStructure() == NULL)
285 return NULL;
286 return &PxeVtbl;
287 }
288
289 static LONG PxeDiskClose(ULONG FileId)
290 {
291 // Nothing to do
292 return ESUCCESS;
293 }
294
295 static LONG PxeDiskGetFileInformation(ULONG FileId, FILEINFORMATION* Information)
296 {
297 // No disk access in PXE mode
298 return EINVAL;
299 }
300
301 static LONG PxeDiskOpen(CHAR* Path, OPENMODE OpenMode, ULONG* FileId)
302 {
303 // Nothing to do
304 return ESUCCESS;
305 }
306
307 static LONG PxeDiskRead(ULONG FileId, VOID* Buffer, ULONG N, ULONG* Count)
308 {
309 // No disk access in PXE mode
310 return EINVAL;
311 }
312
313 static LONG PxeDiskSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode)
314 {
315 // No disk access in PXE mode
316 return EINVAL;
317 }
318
319 static const DEVVTBL PxeDiskVtbl = {
320 PxeDiskClose,
321 PxeDiskGetFileInformation,
322 PxeDiskOpen,
323 PxeDiskRead,
324 PxeDiskSeek,
325 };
326
327 static BOOLEAN GetCachedInfo(VOID)
328 {
329 t_PXENV_GET_CACHED_INFO Data;
330 BOOLEAN res;
331 UCHAR* Packet;
332
333 RtlZeroMemory(&Data, sizeof(Data));
334 Data.PacketType = PXENV_PACKET_TYPE_CACHED_REPLY;
335
336 res = CallPxe(PXENV_GET_CACHED_INFO, &Data);
337 if (!res)
338 return FALSE;
339 if (Data.BufferSize < 36)
340 return FALSE;
341 Packet = (UCHAR*)((UINT32)(Data.Buffer.segment << 4) + Data.Buffer.offset);
342 RtlCopyMemory(&_ServerIP, Packet + 20, sizeof(IP4));
343 return TRUE;
344 }
345
346 BOOLEAN PxeInit(VOID)
347 {
348 static BOOLEAN Initialized = FALSE;
349 static BOOLEAN Status = FALSE;
350
351 // Do initialization only once
352 if (Initialized)
353 return Status;
354 Initialized = TRUE;
355
356 // Check if PXE is available
357 if (GetPxeStructure() && GetCachedInfo())
358 {
359 FsRegisterDevice("net(0)", &PxeDiskVtbl);
360 Status = TRUE;
361 }
362
363 return Status;
364 }
365