3 * Copyright (C) 2011 Hervé Poussineau
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.
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.
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.
25 #define TAG_PXE_FILE 'FexP'
26 #define NO_FILE ((ULONG)-1)
28 DBG_DEFAULT_CHANNEL(FILESYSTEM
);
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;
40 FindPxeStructure(VOID
)
46 /* Find the '!PXE' structure */
48 while ((ULONG
)Ptr
> 0x10000)
50 Ptr
= (PPXE
)((ULONG
)Ptr
- 0x10);
52 /* Look for signature */
53 if (memcmp(Ptr
, "!PXE", 4) != 0)
57 if (Ptr
->StructLength
!= sizeof(PXE
))
62 for (i
= 0; i
< Ptr
->StructLength
; i
++)
63 Checksum
+= *((PUCHAR
)Ptr
+ i
);
67 TRACE("!PXE structure found at %p\n", Ptr
);
74 static PPXE
GetPxeStructure(VOID
)
76 static PPXE pPxe
= NULL
;
77 static BOOLEAN bPxeSearched
= FALSE
;
80 pPxe
= FindPxeStructure();
86 extern PXENV_EXIT
PxeCallApi(UINT16 Segment
, UINT16 Offset
, UINT16 Service
, VOID
*Parameter
);
87 BOOLEAN
CallPxe(UINT16 Service
, PVOID Parameter
)
92 pxe
= GetPxeStructure();
96 if (Service
!= PXENV_TFTP_READ
)
98 // HACK: this delay shouldn't be necessary
99 KeStallExecutionProcessor(100 * 1000); // 100 ms
100 TRACE("PxeCall(0x%x, %p)\n", Service
, Parameter
);
103 exit
= PxeCallApi(pxe
->EntryPointSP
.segment
, pxe
->EntryPointSP
.offset
, Service
, Parameter
);
104 if (exit
!= PXENV_EXIT_SUCCESS
)
106 ERR("PxeCall(0x%x, %p) failed with exit=%d status=0x%x\n",
107 Service
, Parameter
, exit
, *(PXENV_STATUS
*)Parameter
);
110 if (*(PXENV_STATUS
*)Parameter
!= PXENV_STATUS_SUCCESS
)
112 ERR("PxeCall(0x%x, %p) succeeded, but returned error status 0x%x\n",
113 Service
, Parameter
, *(PXENV_STATUS
*)Parameter
);
119 static LONG
PxeClose(ULONG FileId
)
121 t_PXENV_TFTP_CLOSE closeData
;
123 if (_OpenFile
== NO_FILE
|| FileId
!= _OpenFile
)
126 RtlZeroMemory(&closeData
, sizeof(closeData
));
127 if (!CallPxe(PXENV_TFTP_CLOSE
, &closeData
))
133 FrLdrTempFree(_CachedFile
, TAG_PXE_FILE
);
139 static LONG
PxeGetFileInformation(ULONG FileId
, FILEINFORMATION
* Information
)
141 if (_OpenFile
== NO_FILE
|| FileId
!= _OpenFile
)
144 RtlZeroMemory(Information
, sizeof(FILEINFORMATION
));
145 Information
->EndingAddress
.LowPart
= _FileSize
;
146 Information
->CurrentAddress
.LowPart
= _FilePosition
;
151 static LONG
PxeOpen(CHAR
* Path
, OPENMODE OpenMode
, ULONG
* FileId
)
153 t_PXENV_TFTP_GET_FSIZE sizeData
;
154 t_PXENV_TFTP_OPEN openData
;
156 if (_OpenFile
!= NO_FILE
)
158 if (OpenMode
!= OpenReadOnly
)
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
))
166 _FileSize
= sizeData
.FileSize
;
167 if (_FileSize
< 1024 * 1024)
169 _CachedFile
= FrLdrTempAlloc(_FileSize
, TAG_PXE_FILE
);
170 // Don't check for allocation failure, we support _CachedFile = NULL
174 RtlZeroMemory(&openData
, sizeof(openData
));
175 openData
.ServerIPAddress
= _ServerIP
;
176 strncpy((CHAR
*)openData
.FileName
, Path
, sizeof(openData
.FileName
));
177 openData
.PacketSize
= sizeof(_Packet
);
179 if (!CallPxe(PXENV_TFTP_OPEN
, &openData
))
183 FrLdrTempFree(_CachedFile
, TAG_PXE_FILE
);
196 static LONG
PxeRead(ULONG FileId
, VOID
* Buffer
, ULONG N
, ULONG
* Count
)
198 t_PXENV_TFTP_READ readData
;
203 if (_OpenFile
== NO_FILE
|| FileId
!= _OpenFile
)
206 RtlZeroMemory(&readData
, sizeof(readData
));
207 readData
.Buffer
.segment
= ((UINT32
)_Packet
& 0xf0000) / 16;
208 readData
.Buffer
.offset
= (UINT32
)_Packet
& 0xffff;
210 // Get new packets as required
213 if (N
< _CachedLength
- _FilePosition
)
216 i
= _CachedLength
- _FilePosition
;
218 RtlCopyMemory(Buffer
, _CachedFile
+ _FilePosition
, i
);
220 RtlCopyMemory(Buffer
, _Packet
+ _FilePosition
- _PacketPosition
, i
);
222 Buffer
= (UCHAR
*)Buffer
+ i
;
228 if (!CallPxe(PXENV_TFTP_READ
, &readData
))
231 RtlCopyMemory(_CachedFile
+ _CachedLength
, _Packet
, readData
.BufferSize
);
232 _PacketPosition
= _CachedLength
;
233 _CachedLength
+= readData
.BufferSize
;
239 static LONG
PxeSeek(ULONG FileId
, LARGE_INTEGER
* Position
, SEEKMODE SeekMode
)
241 t_PXENV_TFTP_READ readData
;
243 if (_OpenFile
== NO_FILE
|| FileId
!= _OpenFile
)
246 if (Position
->HighPart
!= 0 || SeekMode
!= SeekAbsolute
)
249 if (!_CachedFile
&& Position
->LowPart
< _FilePosition
)
250 // We don't support backward seek without caching
253 RtlZeroMemory(&readData
, sizeof(readData
));
254 readData
.Buffer
.segment
= ((UINT32
)_Packet
& 0xf0000) / 16;
255 readData
.Buffer
.offset
= (UINT32
)_Packet
& 0xffff;
257 // Get new packets as required
258 while (Position
->LowPart
> _CachedLength
)
260 if (!CallPxe(PXENV_TFTP_READ
, &readData
))
264 RtlCopyMemory(_CachedFile
+ _CachedLength
, _Packet
, readData
.BufferSize
);
266 _PacketPosition
= _CachedLength
;
267 _CachedLength
+= readData
.BufferSize
;
270 _FilePosition
= Position
->LowPart
;
274 static const DEVVTBL PxeVtbl
= {
276 PxeGetFileInformation
,
282 const DEVVTBL
* PxeMount(ULONG DeviceId
)
284 if (GetPxeStructure() == NULL
)
289 static LONG
PxeDiskClose(ULONG FileId
)
295 static LONG
PxeDiskGetFileInformation(ULONG FileId
, FILEINFORMATION
* Information
)
297 // No disk access in PXE mode
301 static LONG
PxeDiskOpen(CHAR
* Path
, OPENMODE OpenMode
, ULONG
* FileId
)
307 static LONG
PxeDiskRead(ULONG FileId
, VOID
* Buffer
, ULONG N
, ULONG
* Count
)
309 // No disk access in PXE mode
313 static LONG
PxeDiskSeek(ULONG FileId
, LARGE_INTEGER
* Position
, SEEKMODE SeekMode
)
315 // No disk access in PXE mode
319 static const DEVVTBL PxeDiskVtbl
= {
321 PxeDiskGetFileInformation
,
327 static BOOLEAN
GetCachedInfo(VOID
)
329 t_PXENV_GET_CACHED_INFO Data
;
333 RtlZeroMemory(&Data
, sizeof(Data
));
334 Data
.PacketType
= PXENV_PACKET_TYPE_CACHED_REPLY
;
336 res
= CallPxe(PXENV_GET_CACHED_INFO
, &Data
);
339 if (Data
.BufferSize
< 36)
341 Packet
= (UCHAR
*)((UINT32
)(Data
.Buffer
.segment
<< 4) + Data
.Buffer
.offset
);
342 RtlCopyMemory(&_ServerIP
, Packet
+ 20, sizeof(IP4
));
346 BOOLEAN
PxeInit(VOID
)
348 static BOOLEAN Initialized
= FALSE
;
349 static BOOLEAN Status
= FALSE
;
351 // Do initialization only once
356 // Check if PXE is available
357 if (GetPxeStructure() && GetCachedInfo())
359 FsRegisterDevice("net(0)", &PxeDiskVtbl
);