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.
24 #define TAG_PXE_FILE 'FexP'
25 #define NO_FILE ((ULONG)-1)
27 DBG_DEFAULT_CHANNEL(FILESYSTEM
);
29 static IP4 _ServerIP
= { 0, };
30 static ULONG _OpenFile
= NO_FILE
;
31 static CHAR _OpenFileName
[128];
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 __cdecl
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 ARC_STATUS
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 ARC_STATUS
PxeGetFileInformation(ULONG FileId
, FILEINFORMATION
* Information
)
141 if (_OpenFile
== NO_FILE
|| FileId
!= _OpenFile
)
144 RtlZeroMemory(Information
, sizeof(*Information
));
145 Information
->EndingAddress
.LowPart
= _FileSize
;
146 Information
->CurrentAddress
.LowPart
= _FilePosition
;
151 static ARC_STATUS
PxeOpen(CHAR
* Path
, OPENMODE OpenMode
, ULONG
* FileId
)
153 t_PXENV_TFTP_GET_FSIZE sizeData
;
154 t_PXENV_TFTP_OPEN openData
;
157 if (_OpenFile
!= NO_FILE
)
159 if (OpenMode
!= OpenReadOnly
)
162 /* Retrieve the path length without NULL terminator */
163 PathLen
= (Path
? min(strlen(Path
), sizeof(_OpenFileName
) - 1) : 0);
165 /* Lowercase the path and always use slashes as separators */
166 for (i
= 0; i
< PathLen
; i
++)
169 _OpenFileName
[i
] = '/';
171 _OpenFileName
[i
] = tolower(Path
[i
]);
174 /* Zero out rest of the file name */
175 RtlZeroMemory(_OpenFileName
+ PathLen
, sizeof(_OpenFileName
) - PathLen
);
177 RtlZeroMemory(&sizeData
, sizeof(sizeData
));
178 sizeData
.ServerIPAddress
= _ServerIP
;
179 RtlCopyMemory(sizeData
.FileName
, _OpenFileName
, sizeof(_OpenFileName
));
180 if (!CallPxe(PXENV_TFTP_GET_FSIZE
, &sizeData
))
182 ERR("Failed to get '%s' size\n", Path
);
186 _FileSize
= sizeData
.FileSize
;
187 if (_FileSize
< 1024 * 1024)
189 _CachedFile
= FrLdrTempAlloc(_FileSize
, TAG_PXE_FILE
);
190 // Don't check for allocation failure, we support _CachedFile == NULL
194 RtlZeroMemory(&openData
, sizeof(openData
));
195 openData
.ServerIPAddress
= _ServerIP
;
196 RtlCopyMemory(openData
.FileName
, _OpenFileName
, sizeof(_OpenFileName
));
197 openData
.PacketSize
= sizeof(_Packet
);
199 if (!CallPxe(PXENV_TFTP_OPEN
, &openData
))
203 FrLdrTempFree(_CachedFile
, TAG_PXE_FILE
);
216 static ARC_STATUS
PxeRead(ULONG FileId
, VOID
* Buffer
, ULONG N
, ULONG
* Count
)
218 t_PXENV_TFTP_READ readData
;
223 if (_OpenFile
== NO_FILE
|| FileId
!= _OpenFile
)
226 RtlZeroMemory(&readData
, sizeof(readData
));
227 readData
.Buffer
.segment
= ((UINT32
)_Packet
& 0xf0000) / 16;
228 readData
.Buffer
.offset
= (UINT32
)_Packet
& 0xffff;
230 // Get new packets as required
233 if (N
< _CachedLength
- _FilePosition
)
236 i
= _CachedLength
- _FilePosition
;
238 RtlCopyMemory(Buffer
, _CachedFile
+ _FilePosition
, i
);
240 RtlCopyMemory(Buffer
, _Packet
+ _FilePosition
- _PacketPosition
, i
);
242 Buffer
= (UCHAR
*)Buffer
+ i
;
248 if (!CallPxe(PXENV_TFTP_READ
, &readData
))
251 RtlCopyMemory(_CachedFile
+ _CachedLength
, _Packet
, readData
.BufferSize
);
252 _PacketPosition
= _CachedLength
;
253 _CachedLength
+= readData
.BufferSize
;
259 static ARC_STATUS
PxeSeek(ULONG FileId
, LARGE_INTEGER
* Position
, SEEKMODE SeekMode
)
261 t_PXENV_TFTP_READ readData
;
263 if (_OpenFile
== NO_FILE
|| FileId
!= _OpenFile
)
266 if (Position
->HighPart
!= 0 || SeekMode
!= SeekAbsolute
)
269 if (!_CachedFile
&& Position
->LowPart
< _FilePosition
)
271 // Close and reopen the file to go to position 0
272 if (PxeClose(FileId
) != ESUCCESS
)
274 if (PxeOpen(_OpenFileName
, OpenReadOnly
, &FileId
) != ESUCCESS
)
278 RtlZeroMemory(&readData
, sizeof(readData
));
279 readData
.Buffer
.segment
= ((UINT32
)_Packet
& 0xf0000) / 16;
280 readData
.Buffer
.offset
= (UINT32
)_Packet
& 0xffff;
282 // Get new packets as required
283 while (Position
->LowPart
> _CachedLength
)
285 if (!CallPxe(PXENV_TFTP_READ
, &readData
))
289 RtlCopyMemory(_CachedFile
+ _CachedLength
, _Packet
, readData
.BufferSize
);
291 _PacketPosition
= _CachedLength
;
292 _CachedLength
+= readData
.BufferSize
;
295 _FilePosition
= Position
->LowPart
;
299 static const DEVVTBL PxeVtbl
= {
301 PxeGetFileInformation
,
307 const DEVVTBL
* PxeMount(ULONG DeviceId
)
309 if (GetPxeStructure() == NULL
)
314 static ARC_STATUS
PxeDiskClose(ULONG FileId
)
320 static ARC_STATUS
PxeDiskGetFileInformation(ULONG FileId
, FILEINFORMATION
* Information
)
322 // No disk access in PXE mode
326 static ARC_STATUS
PxeDiskOpen(CHAR
* Path
, OPENMODE OpenMode
, ULONG
* FileId
)
332 static ARC_STATUS
PxeDiskRead(ULONG FileId
, VOID
* Buffer
, ULONG N
, ULONG
* Count
)
334 // No disk access in PXE mode
338 static ARC_STATUS
PxeDiskSeek(ULONG FileId
, LARGE_INTEGER
* Position
, SEEKMODE SeekMode
)
340 // No disk access in PXE mode
344 static const DEVVTBL PxeDiskVtbl
= {
346 PxeDiskGetFileInformation
,
352 static BOOLEAN
GetCachedInfo(VOID
)
354 t_PXENV_GET_CACHED_INFO Data
;
358 RtlZeroMemory(&Data
, sizeof(Data
));
359 Data
.PacketType
= PXENV_PACKET_TYPE_CACHED_REPLY
;
361 res
= CallPxe(PXENV_GET_CACHED_INFO
, &Data
);
364 if (Data
.BufferSize
< 36)
366 Packet
= (UCHAR
*)((UINT32
)(Data
.Buffer
.segment
<< 4) + Data
.Buffer
.offset
);
367 RtlCopyMemory(&_ServerIP
, Packet
+ 20, sizeof(IP4
));
371 BOOLEAN
PxeInit(VOID
)
373 static BOOLEAN Initialized
= FALSE
;
374 static BOOLEAN Success
= FALSE
;
376 // Do initialization only once
381 // Check if PXE is available
382 if (GetPxeStructure() && GetCachedInfo())
384 FsRegisterDevice("net(0)", &PxeDiskVtbl
);