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 CHAR _OpenFileName
[128];
33 static ULONG _FileSize
= 0;
34 static ULONG _FilePosition
= 0;
35 static ULONG _PacketPosition
= 0;
36 static UCHAR _Packet
[4096];
37 static UCHAR
* _CachedFile
= NULL
;
38 static ULONG _CachedLength
= 0;
41 FindPxeStructure(VOID
)
47 /* Find the '!PXE' structure */
49 while ((ULONG
)Ptr
> 0x10000)
51 Ptr
= (PPXE
)((ULONG
)Ptr
- 0x10);
53 /* Look for signature */
54 if (memcmp(Ptr
, "!PXE", 4) != 0)
58 if (Ptr
->StructLength
!= sizeof(PXE
))
63 for (i
= 0; i
< Ptr
->StructLength
; i
++)
64 Checksum
+= *((PUCHAR
)Ptr
+ i
);
68 TRACE("!PXE structure found at %p\n", Ptr
);
75 static PPXE
GetPxeStructure(VOID
)
77 static PPXE pPxe
= NULL
;
78 static BOOLEAN bPxeSearched
= FALSE
;
81 pPxe
= FindPxeStructure();
87 extern PXENV_EXIT __cdecl
PxeCallApi(UINT16 Segment
, UINT16 Offset
, UINT16 Service
, VOID
*Parameter
);
88 BOOLEAN
CallPxe(UINT16 Service
, PVOID Parameter
)
93 pxe
= GetPxeStructure();
97 if (Service
!= PXENV_TFTP_READ
)
99 // HACK: this delay shouldn't be necessary
100 KeStallExecutionProcessor(100 * 1000); // 100 ms
101 TRACE("PxeCall(0x%x, %p)\n", Service
, Parameter
);
104 exit
= PxeCallApi(pxe
->EntryPointSP
.segment
, pxe
->EntryPointSP
.offset
, Service
, Parameter
);
105 if (exit
!= PXENV_EXIT_SUCCESS
)
107 ERR("PxeCall(0x%x, %p) failed with exit=%d status=0x%x\n",
108 Service
, Parameter
, exit
, *(PXENV_STATUS
*)Parameter
);
111 if (*(PXENV_STATUS
*)Parameter
!= PXENV_STATUS_SUCCESS
)
113 ERR("PxeCall(0x%x, %p) succeeded, but returned error status 0x%x\n",
114 Service
, Parameter
, *(PXENV_STATUS
*)Parameter
);
120 static ARC_STATUS
PxeClose(ULONG FileId
)
122 t_PXENV_TFTP_CLOSE closeData
;
124 if (_OpenFile
== NO_FILE
|| FileId
!= _OpenFile
)
127 RtlZeroMemory(&closeData
, sizeof(closeData
));
128 if (!CallPxe(PXENV_TFTP_CLOSE
, &closeData
))
134 FrLdrTempFree(_CachedFile
, TAG_PXE_FILE
);
140 static ARC_STATUS
PxeGetFileInformation(ULONG FileId
, FILEINFORMATION
* Information
)
142 if (_OpenFile
== NO_FILE
|| FileId
!= _OpenFile
)
145 RtlZeroMemory(Information
, sizeof(*Information
));
146 Information
->EndingAddress
.LowPart
= _FileSize
;
147 Information
->CurrentAddress
.LowPart
= _FilePosition
;
152 static ARC_STATUS
PxeOpen(CHAR
* Path
, OPENMODE OpenMode
, ULONG
* FileId
)
154 t_PXENV_TFTP_GET_FSIZE sizeData
;
155 t_PXENV_TFTP_OPEN openData
;
158 if (_OpenFile
!= NO_FILE
)
160 if (OpenMode
!= OpenReadOnly
)
163 /* Retrieve the path length without NULL terminator */
164 PathLen
= (Path
? min(strlen(Path
), sizeof(_OpenFileName
) - 1) : 0);
166 /* Lowercase the path and always use slashes as separators */
167 for (i
= 0; i
< PathLen
; i
++)
170 _OpenFileName
[i
] = '/';
172 _OpenFileName
[i
] = tolower(Path
[i
]);
175 /* Zero out rest of the file name */
176 RtlZeroMemory(_OpenFileName
+ PathLen
, sizeof(_OpenFileName
) - PathLen
);
178 RtlZeroMemory(&sizeData
, sizeof(sizeData
));
179 sizeData
.ServerIPAddress
= _ServerIP
;
180 RtlCopyMemory(sizeData
.FileName
, _OpenFileName
, sizeof(_OpenFileName
));
181 if (!CallPxe(PXENV_TFTP_GET_FSIZE
, &sizeData
))
183 ERR("Failed to get '%s' size\n", Path
);
187 _FileSize
= sizeData
.FileSize
;
188 if (_FileSize
< 1024 * 1024)
190 _CachedFile
= FrLdrTempAlloc(_FileSize
, TAG_PXE_FILE
);
191 // Don't check for allocation failure, we support _CachedFile == NULL
195 RtlZeroMemory(&openData
, sizeof(openData
));
196 openData
.ServerIPAddress
= _ServerIP
;
197 RtlCopyMemory(openData
.FileName
, _OpenFileName
, sizeof(_OpenFileName
));
198 openData
.PacketSize
= sizeof(_Packet
);
200 if (!CallPxe(PXENV_TFTP_OPEN
, &openData
))
204 FrLdrTempFree(_CachedFile
, TAG_PXE_FILE
);
217 static ARC_STATUS
PxeRead(ULONG FileId
, VOID
* Buffer
, ULONG N
, ULONG
* Count
)
219 t_PXENV_TFTP_READ readData
;
224 if (_OpenFile
== NO_FILE
|| FileId
!= _OpenFile
)
227 RtlZeroMemory(&readData
, sizeof(readData
));
228 readData
.Buffer
.segment
= ((UINT32
)_Packet
& 0xf0000) / 16;
229 readData
.Buffer
.offset
= (UINT32
)_Packet
& 0xffff;
231 // Get new packets as required
234 if (N
< _CachedLength
- _FilePosition
)
237 i
= _CachedLength
- _FilePosition
;
239 RtlCopyMemory(Buffer
, _CachedFile
+ _FilePosition
, i
);
241 RtlCopyMemory(Buffer
, _Packet
+ _FilePosition
- _PacketPosition
, i
);
243 Buffer
= (UCHAR
*)Buffer
+ i
;
249 if (!CallPxe(PXENV_TFTP_READ
, &readData
))
252 RtlCopyMemory(_CachedFile
+ _CachedLength
, _Packet
, readData
.BufferSize
);
253 _PacketPosition
= _CachedLength
;
254 _CachedLength
+= readData
.BufferSize
;
260 static ARC_STATUS
PxeSeek(ULONG FileId
, LARGE_INTEGER
* Position
, SEEKMODE SeekMode
)
262 t_PXENV_TFTP_READ readData
;
264 if (_OpenFile
== NO_FILE
|| FileId
!= _OpenFile
)
267 if (Position
->HighPart
!= 0 || SeekMode
!= SeekAbsolute
)
270 if (!_CachedFile
&& Position
->LowPart
< _FilePosition
)
272 // Close and reopen the file to go to position 0
273 if (PxeClose(FileId
) != ESUCCESS
)
275 if (PxeOpen(_OpenFileName
, OpenReadOnly
, &FileId
) != ESUCCESS
)
279 RtlZeroMemory(&readData
, sizeof(readData
));
280 readData
.Buffer
.segment
= ((UINT32
)_Packet
& 0xf0000) / 16;
281 readData
.Buffer
.offset
= (UINT32
)_Packet
& 0xffff;
283 // Get new packets as required
284 while (Position
->LowPart
> _CachedLength
)
286 if (!CallPxe(PXENV_TFTP_READ
, &readData
))
290 RtlCopyMemory(_CachedFile
+ _CachedLength
, _Packet
, readData
.BufferSize
);
292 _PacketPosition
= _CachedLength
;
293 _CachedLength
+= readData
.BufferSize
;
296 _FilePosition
= Position
->LowPart
;
300 static const DEVVTBL PxeVtbl
= {
302 PxeGetFileInformation
,
308 const DEVVTBL
* PxeMount(ULONG DeviceId
)
310 if (GetPxeStructure() == NULL
)
315 static ARC_STATUS
PxeDiskClose(ULONG FileId
)
321 static ARC_STATUS
PxeDiskGetFileInformation(ULONG FileId
, FILEINFORMATION
* Information
)
323 // No disk access in PXE mode
327 static ARC_STATUS
PxeDiskOpen(CHAR
* Path
, OPENMODE OpenMode
, ULONG
* FileId
)
333 static ARC_STATUS
PxeDiskRead(ULONG FileId
, VOID
* Buffer
, ULONG N
, ULONG
* Count
)
335 // No disk access in PXE mode
339 static ARC_STATUS
PxeDiskSeek(ULONG FileId
, LARGE_INTEGER
* Position
, SEEKMODE SeekMode
)
341 // No disk access in PXE mode
345 static const DEVVTBL PxeDiskVtbl
= {
347 PxeDiskGetFileInformation
,
353 static BOOLEAN
GetCachedInfo(VOID
)
355 t_PXENV_GET_CACHED_INFO Data
;
359 RtlZeroMemory(&Data
, sizeof(Data
));
360 Data
.PacketType
= PXENV_PACKET_TYPE_CACHED_REPLY
;
362 res
= CallPxe(PXENV_GET_CACHED_INFO
, &Data
);
365 if (Data
.BufferSize
< 36)
367 Packet
= (UCHAR
*)((UINT32
)(Data
.Buffer
.segment
<< 4) + Data
.Buffer
.offset
);
368 RtlCopyMemory(&_ServerIP
, Packet
+ 20, sizeof(IP4
));
372 BOOLEAN
PxeInit(VOID
)
374 static BOOLEAN Initialized
= FALSE
;
375 static BOOLEAN Success
= FALSE
;
377 // Do initialization only once
382 // Check if PXE is available
383 if (GetPxeStructure() && GetCachedInfo())
385 FsRegisterDevice("net(0)", &PxeDiskVtbl
);