Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

765 lines
17 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. netboot.c
  5. Abstract:
  6. This module implements the net boot file system used by the operating
  7. system loader.
  8. Author:
  9. Chuck Lenzmeier (chuckl) 09-Jan-1997
  10. Revision History:
  11. --*/
  12. #include "bootlib.h"
  13. #include "stdio.h"
  14. #ifdef UINT16
  15. #undef UINT16
  16. #endif
  17. #ifdef INT16
  18. #undef INT16
  19. #endif
  20. #include <dhcp.h>
  21. #include <netfs.h>
  22. #include <pxe_cmn.h>
  23. #include <pxe_api.h>
  24. #include <udp_api.h>
  25. #include <tftp_api.h>
  26. #if defined(_IA64_)
  27. #include "bootia64.h"
  28. #else
  29. #include "bootx86.h"
  30. #endif
  31. #ifndef BOOL
  32. typedef int BOOL;
  33. #endif
  34. #ifndef FALSE
  35. #define FALSE 0
  36. #endif
  37. #ifndef TRUE
  38. #define TRUE 1
  39. #endif
  40. #ifndef BYTE
  41. typedef unsigned char BYTE;
  42. #endif
  43. #ifndef LPBYTE
  44. typedef BYTE *LPBYTE;
  45. #endif
  46. #define MAX_PATH 260
  47. //
  48. // Define global data.
  49. //
  50. BOOLEAN BlBootingFromNet = FALSE;
  51. BOOLEAN NetBootInitialized = FALSE;
  52. PARC_OPEN_ROUTINE NetRealArcOpenRoutine;
  53. PARC_CLOSE_ROUTINE NetRealArcCloseRoutine;
  54. BL_DEVICE_ENTRY_TABLE NetDeviceEntryTable;
  55. BOOTFS_INFO NetBootFsInfo={L"net"};
  56. #if defined(REMOTE_BOOT_SECURITY)
  57. ULONG TftpSecurityHandle = 0;
  58. #endif // defined(REMOTE_BOOT_SECURITY)
  59. BOOLEAN NetBootTftpUsedPassword2;
  60. //
  61. // We cache the last file opened, in case we get a request to open it again.
  62. // We don't save a copy of the data, just a pointer to the data read by that
  63. // open. So if the original open is closed before the next open for the
  64. // same file comes in, we won't get a cache hit. But this system works for
  65. // reading compressed files, which is the situation we care about. In that
  66. // case a file is opened once and then re-opened twice more before the
  67. // original open is closed.
  68. //
  69. ULONG CachedFileDeviceId = 0;
  70. UCHAR CachedFilePath[MAX_PATH];
  71. ULONG CachedFileSize = 0;
  72. PUCHAR CachedFileData = NULL;
  73. extern ARC_STATUS
  74. GetParametersFromRom (
  75. VOID
  76. );
  77. PBL_DEVICE_ENTRY_TABLE
  78. IsNetFileStructure (
  79. IN ULONG DeviceId,
  80. IN PVOID StructureContext
  81. )
  82. /*++
  83. Routine Description:
  84. This routine determines if the partition on the specified channel
  85. contains a net file system volume.
  86. Arguments:
  87. DeviceId - Supplies the file table index for the device on which
  88. read operations are to be performed.
  89. StructureContext - Supplies a pointer to a net file structure context.
  90. Return Value:
  91. A pointer to the net entry table is returned if the partition is
  92. recognized as containing a net volume. Otherwise, NULL is returned.
  93. --*/
  94. {
  95. PNET_STRUCTURE_CONTEXT NetStructureContext;
  96. DPRINT( TRACE, ("IsNetFileStructure\n") );
  97. if ( !BlBootingFromNet || (DeviceId != NET_DEVICE_ID) ) {
  98. return NULL;
  99. }
  100. //
  101. // Clear the file system context block for the specified channel and
  102. // establish a pointer to the context structure that can be used by other
  103. // routines
  104. //
  105. NetStructureContext = (PNET_STRUCTURE_CONTEXT)StructureContext;
  106. RtlZeroMemory(NetStructureContext, sizeof(NET_STRUCTURE_CONTEXT));
  107. //
  108. // Return the address of the table.
  109. //
  110. return &NetDeviceEntryTable;
  111. } // IsNetFileStructure
  112. ARC_STATUS
  113. NetInitialize (
  114. VOID
  115. )
  116. /*++
  117. Routine Description:
  118. This routine initializes the net boot filesystem.
  119. Arguments:
  120. None.
  121. Return Value:
  122. ESUCCESS.
  123. --*/
  124. {
  125. NTSTATUS status;
  126. DPRINT( TRACE, ("NetInitialize\n") );
  127. //DbgBreakPoint( );
  128. if( NetBootInitialized ) {
  129. return ESUCCESS;
  130. }
  131. //
  132. // Initialize the file entry table. Note that we need to do
  133. // this even if we aren't booting from the net because we may
  134. // use the 'Net' I/O functions to lay on top of any files that
  135. // we download through the debugger port. So for that case,
  136. // we need access to all these functions here (see bd\file.c)
  137. //
  138. NetDeviceEntryTable.Close = NetClose;
  139. NetDeviceEntryTable.Mount = NetMount;
  140. NetDeviceEntryTable.Open = NetOpen;
  141. NetDeviceEntryTable.Read = NetRead;
  142. NetDeviceEntryTable.GetReadStatus = NetGetReadStatus;
  143. NetDeviceEntryTable.Seek = NetSeek;
  144. NetDeviceEntryTable.Write = NetWrite;
  145. NetDeviceEntryTable.GetFileInformation = NetGetFileInformation;
  146. NetDeviceEntryTable.SetFileInformation = NetSetFileInformation;
  147. NetDeviceEntryTable.Rename = NetRename;
  148. NetDeviceEntryTable.GetDirectoryEntry = NetGetDirectoryEntry;
  149. NetDeviceEntryTable.BootFsInfo = &NetBootFsInfo;
  150. if( !BlBootingFromNet ) {
  151. return ESUCCESS;
  152. }
  153. NetBootInitialized = TRUE;
  154. DPRINT( LOUD, ("NetInitialize: booting from net\n") );
  155. //DPRINT( LOUD, (" NetInitialize at %08x\n", NetInitialize) );
  156. //DPRINT( LOUD, (" NetOpen at %08x\n", NetOpen) );
  157. //DbgBreakPoint( );
  158. //
  159. // Hook the ArcOpen and ArcClose routines.
  160. //
  161. NetRealArcOpenRoutine = SYSTEM_BLOCK->FirmwareVector[OpenRoutine];
  162. SYSTEM_BLOCK->FirmwareVector[OpenRoutine] = NetArcOpen;
  163. NetRealArcCloseRoutine = SYSTEM_BLOCK->FirmwareVector[CloseRoutine];
  164. SYSTEM_BLOCK->FirmwareVector[CloseRoutine] = NetArcClose;
  165. //
  166. // Get boot parameters from the boot ROM.
  167. //
  168. status = GetParametersFromRom( );
  169. if ( status != ESUCCESS ) {
  170. return status;
  171. }
  172. return ESUCCESS;
  173. }
  174. VOID
  175. NetTerminate (
  176. VOID
  177. )
  178. /*++
  179. Routine Description:
  180. This routine shuts down the net boot filesystem.
  181. Arguments:
  182. None.
  183. Return Value:
  184. ESUCCESS.
  185. --*/
  186. {
  187. #if defined(_X86_)
  188. #if defined(REMOTE_BOOT_SECURITY)
  189. if ( TftpSecurityHandle != 0 ) {
  190. TftpLogoff(NetServerIpAddress, TftpSecurityHandle);
  191. TftpSecurityHandle = 0;
  192. }
  193. #endif // defined(REMOTE_BOOT_SECURITY)
  194. //
  195. // let's not set the receive status if the card isn't active.
  196. //
  197. RomSetReceiveStatus( 0 );
  198. #endif // defined(_X86_)
  199. #ifdef EFI
  200. extern VOID EfiNetTerminate();
  201. EfiNetTerminate();
  202. #endif
  203. return;
  204. } // NetTerminate
  205. ARC_STATUS
  206. NetArcClose (
  207. IN ULONG FileId
  208. )
  209. {
  210. DPRINT( TRACE, ("NetArcClose\n") );
  211. if ( FileId != NET_DEVICE_ID ) {
  212. return NetRealArcCloseRoutine( FileId );
  213. }
  214. return ESUCCESS;
  215. } // NetArcClose
  216. ARC_STATUS
  217. NetArcOpen (
  218. IN CHAR * FIRMWARE_PTR OpenPath,
  219. IN OPEN_MODE OpenMode,
  220. OUT ULONG * FIRMWARE_PTR FileId
  221. )
  222. {
  223. DPRINT( TRACE, ("NetArcOpen\n") );
  224. if ( _strnicmp(OpenPath, "net(", 4) != 0 ) {
  225. return NetRealArcOpenRoutine( OpenPath, OpenMode, FileId );
  226. }
  227. *FileId = NET_DEVICE_ID;
  228. return ESUCCESS;
  229. } // NetArcOpen
  230. ARC_STATUS
  231. NetClose (
  232. IN ULONG FileId
  233. )
  234. {
  235. PBL_FILE_TABLE fileTableEntry;
  236. DPRINT( TRACE, ("NetClose FileId = %d\n", FileId) );
  237. fileTableEntry = &BlFileTable[FileId];
  238. {
  239. DPRINT( REAL_LOUD, ("NetClose: id %d, freeing memory at 0x%08x, %d bytes\n",
  240. FileId,
  241. fileTableEntry->u.NetFileContext.InMemoryCopy,
  242. fileTableEntry->u.NetFileContext.FileSize) );
  243. BlFreeDescriptor( (ULONG)((ULONG_PTR)fileTableEntry->u.NetFileContext.InMemoryCopy >> PAGE_SHIFT ));
  244. //
  245. // If the data read for this specific open was what was cached,
  246. // then mark the cache empty.
  247. //
  248. if (fileTableEntry->u.NetFileContext.InMemoryCopy == CachedFileData) {
  249. CachedFileData = NULL;
  250. CachedFilePath[0] = '\0';
  251. }
  252. }
  253. fileTableEntry->Flags.Open = 0;
  254. return EROFS;
  255. } // NetClose
  256. ARC_STATUS
  257. NetMount (
  258. IN CHAR * FIRMWARE_PTR MountPath,
  259. IN MOUNT_OPERATION Operation
  260. )
  261. {
  262. DPRINT( TRACE, ("NetMount\n") );
  263. return EROFS;
  264. } // NetMount
  265. ARC_STATUS
  266. NetOpen (
  267. IN CHAR * FIRMWARE_PTR OpenPath,
  268. IN OPEN_MODE OpenMode,
  269. OUT ULONG * FIRMWARE_PTR FileId
  270. )
  271. {
  272. NTSTATUS ntStatus;
  273. ARC_STATUS arcStatus; // holds temp values, not the function return value
  274. PBL_FILE_TABLE fileTableEntry;
  275. TFTP_REQUEST request;
  276. ULONG oldBase;
  277. ULONG oldLimit;
  278. PCHAR p;
  279. #if defined(REMOTE_BOOT_SECURITY)
  280. static BOOLEAN NetBootTryTftpSecurity = FALSE;
  281. #endif // defined(REMOTE_BOOT_SECURITY)
  282. DPRINT( TRACE, ("NetOpen FileId = %d\n", *FileId) );
  283. DPRINT( LOUD, ("NetOpen: opening %s, id %d, mode %d\n", OpenPath, *FileId, OpenMode) );
  284. fileTableEntry = &BlFileTable[*FileId];
  285. if ( OpenMode != ArcOpenReadOnly ) {
  286. DPRINT( LOUD, ("NetOpen: invalid OpenMode\n") );
  287. return EROFS;
  288. }
  289. fileTableEntry->Flags.Open = 1; // Prevent GetCSCFileNameFromUNCPath using our entry
  290. #if defined(REMOTE_BOOT_SECURITY)
  291. //
  292. // Login if we don't have a valid handle, using the on-disk secret.
  293. //
  294. if ((TftpSecurityHandle == 0) &&
  295. NetBootTryTftpSecurity) {
  296. ULONG FileId;
  297. RI_SECRET Secret;
  298. UCHAR Domain[RI_SECRET_DOMAIN_SIZE + 1];
  299. UCHAR User[RI_SECRET_USER_SIZE + 1];
  300. struct {
  301. UCHAR Owf[LM_OWF_PASSWORD_SIZE+NT_OWF_PASSWORD_SIZE];
  302. } Passwords[2];
  303. UCHAR Sid[RI_SECRET_SID_SIZE];
  304. arcStatus = BlOpenRawDisk(&FileId);
  305. if (arcStatus == ESUCCESS) {
  306. arcStatus = BlReadSecret(FileId, &Secret);
  307. if (arcStatus == ESUCCESS) {
  308. BlParseSecret(
  309. Domain,
  310. User,
  311. Passwords[0].Owf,
  312. Passwords[0].Owf + LM_OWF_PASSWORD_SIZE,
  313. Passwords[1].Owf,
  314. Passwords[1].Owf + LM_OWF_PASSWORD_SIZE,
  315. Sid,
  316. &Secret);
  317. DPRINT(LOUD, ("Logging on to <%s><%s>\n", Domain, User));
  318. //
  319. // Try logging on with the first password, if that fails
  320. // then try the second.
  321. //
  322. ntStatus = TftpLogin(
  323. Domain,
  324. User,
  325. Passwords[0].Owf,
  326. NetServerIpAddress,
  327. &TftpSecurityHandle);
  328. if (!NT_SUCCESS(ntStatus)) {
  329. DPRINT(LOUD, ("TftpLogin using password 2\n"));
  330. ntStatus = TftpLogin(
  331. Domain,
  332. User,
  333. Passwords[1].Owf,
  334. NetServerIpAddress,
  335. &TftpSecurityHandle);
  336. if (NT_SUCCESS(ntStatus)) {
  337. NetBootTftpUsedPassword2 = TRUE;
  338. }
  339. }
  340. } else {
  341. ntStatus = STATUS_OBJECT_PATH_NOT_FOUND;
  342. }
  343. arcStatus = BlCloseRawDisk(FileId);
  344. //
  345. // We are inside the if() for successfully opening the raw
  346. // disk, so we are not diskless. On these machines we must
  347. // fail the open at this point.
  348. //
  349. if (!NT_SUCCESS(ntStatus)) {
  350. DPRINT( ERROR, ("TftpLogin failed %lx\n", ntStatus) );
  351. return EACCES;
  352. }
  353. } else {
  354. NetBootTryTftpSecurity = FALSE; // so we don't try to open it again
  355. }
  356. }
  357. #endif // defined(REMOTE_BOOT_SECURITY)
  358. DPRINT( LOUD, ("NetOpen: opening %s\n", OpenPath) );
  359. oldBase = BlUsableBase;
  360. oldLimit = BlUsableLimit;
  361. BlUsableBase = BL_DRIVER_RANGE_LOW;
  362. BlUsableLimit = BL_DRIVER_RANGE_HIGH;
  363. //
  364. // If this request matches the cached file, then just copy that data.
  365. //
  366. if ((fileTableEntry->DeviceId == CachedFileDeviceId) &&
  367. (strcmp(OpenPath, CachedFilePath) == 0) &&
  368. (CachedFileData != NULL)) {
  369. ULONG basePage;
  370. arcStatus = BlAllocateAlignedDescriptor(
  371. LoaderFirmwareTemporary,
  372. 0,
  373. (CachedFileSize + PAGE_SIZE - 1) >> PAGE_SHIFT,
  374. 0,
  375. &basePage
  376. );
  377. BlUsableBase = oldBase;
  378. BlUsableLimit = oldLimit;
  379. if ( arcStatus != ESUCCESS ) {
  380. fileTableEntry->Flags.Open = 0; // Free entry we didn't use
  381. return EROFS;
  382. }
  383. DPRINT( REAL_LOUD, ("NetOpen: Using cache for file %s\n", CachedFilePath) );
  384. fileTableEntry->u.NetFileContext.InMemoryCopy = (PUCHAR)ULongToPtr( (basePage << PAGE_SHIFT) );
  385. memcpy(fileTableEntry->u.NetFileContext.InMemoryCopy, CachedFileData, CachedFileSize);
  386. fileTableEntry->u.NetFileContext.FileSize = CachedFileSize;
  387. } else {
  388. request.RemoteFileName = OpenPath;
  389. request.ServerIpAddress = NetServerIpAddress;
  390. request.MemoryAddress = NULL;
  391. request.MaximumLength = 0;
  392. request.BytesTransferred = 0xbadf00d;
  393. request.Operation = TFTP_RRQ;
  394. request.MemoryType = LoaderFirmwareTemporary;
  395. #if defined(REMOTE_BOOT_SECURITY)
  396. request.SecurityHandle = TftpSecurityHandle;
  397. #endif // defined(REMOTE_BOOT_SECURITY)
  398. request.ShowProgress = FALSE;
  399. ntStatus = TftpGetPut( &request );
  400. DPRINT( REAL_LOUD, ("NetOpen: TftpGetPut(get) status: %x, bytes: %x\n", ntStatus, request.BytesTransferred) );
  401. BlUsableBase = oldBase;
  402. BlUsableLimit = oldLimit;
  403. if ( !NT_SUCCESS(ntStatus) ) {
  404. if ( request.MemoryAddress != NULL ) {
  405. DPRINT( REAL_LOUD, ("NetOpen: freeing memory at 0x%08x, %d bytes\n",
  406. request.MemoryAddress, request.MaximumLength) );
  407. BlFreeDescriptor( (ULONG)((ULONG_PTR)request.MemoryAddress >> PAGE_SHIFT ));
  408. }
  409. fileTableEntry->Flags.Open = 0; // Free entry we didn't use
  410. if ( ntStatus == STATUS_INSUFFICIENT_RESOURCES ) {
  411. return ENOMEM;
  412. }
  413. return EROFS;
  414. }
  415. fileTableEntry->u.NetFileContext.FileSize = request.BytesTransferred;
  416. fileTableEntry->u.NetFileContext.InMemoryCopy = request.MemoryAddress;
  417. //
  418. // We always cache the last file that was actually read from
  419. // the network.
  420. //
  421. strcpy(CachedFilePath, OpenPath);
  422. CachedFileDeviceId = fileTableEntry->DeviceId;
  423. CachedFileSize = request.BytesTransferred;
  424. CachedFileData = request.MemoryAddress;
  425. }
  426. fileTableEntry->Position.QuadPart = 0;
  427. fileTableEntry->Flags.Read = 1;
  428. return ESUCCESS;
  429. } // NetOpen
  430. ARC_STATUS
  431. NetRead (
  432. IN ULONG FileId,
  433. OUT VOID * FIRMWARE_PTR Buffer,
  434. IN ULONG Length,
  435. OUT ULONG * FIRMWARE_PTR Count
  436. )
  437. {
  438. PBL_FILE_TABLE fileTableEntry;
  439. PNET_FILE_CONTEXT context;
  440. PUCHAR source;
  441. fileTableEntry = &BlFileTable[FileId];
  442. context = &fileTableEntry->u.NetFileContext;
  443. {
  444. source = context->InMemoryCopy + fileTableEntry->Position.LowPart;
  445. if ( (fileTableEntry->Position.LowPart + Length) > context->FileSize ) {
  446. Length = context->FileSize - fileTableEntry->Position.LowPart;
  447. }
  448. RtlCopyMemory( Buffer, source, Length );
  449. *Count = Length;
  450. fileTableEntry->Position.LowPart += Length;
  451. }
  452. DPRINT( REAL_LOUD, ("NetRead: id %d, length %d, count %d, new pos %x\n",
  453. FileId, Length, *Count, fileTableEntry->Position.LowPart) );
  454. return ESUCCESS;
  455. } // NetRead
  456. ARC_STATUS
  457. NetGetReadStatus (
  458. IN ULONG FileId
  459. )
  460. {
  461. DPRINT( TRACE, ("NetGetReadStatus\n") );
  462. return ESUCCESS;
  463. } // NetGetReadStatus
  464. ARC_STATUS
  465. NetSeek (
  466. IN ULONG FileId,
  467. IN LARGE_INTEGER * FIRMWARE_PTR Offset,
  468. IN SEEK_MODE SeekMode
  469. )
  470. {
  471. PBL_FILE_TABLE fileTableEntry;
  472. LARGE_INTEGER newPosition;
  473. //DPRINT( TRACE, ("NetSeek\n") );
  474. fileTableEntry = &BlFileTable[FileId];
  475. {
  476. if ( SeekMode == SeekAbsolute ) {
  477. newPosition = *Offset;
  478. } else if ( SeekMode == SeekRelative ) {
  479. newPosition.QuadPart =
  480. fileTableEntry->Position.QuadPart + Offset->QuadPart;
  481. } else {
  482. return EROFS;
  483. }
  484. DPRINT( REAL_LOUD, ("NetSeek: id %d, mode %d, offset %x, new pos %x\n",
  485. FileId, SeekMode, Offset->LowPart, newPosition.LowPart) );
  486. if ( newPosition.QuadPart > fileTableEntry->u.NetFileContext.FileSize ) {
  487. return EROFS;
  488. }
  489. fileTableEntry->Position = newPosition;
  490. }
  491. return ESUCCESS;
  492. } // NetSeek
  493. ARC_STATUS
  494. NetWrite (
  495. IN ULONG FileId,
  496. IN VOID * FIRMWARE_PTR Buffer,
  497. IN ULONG Length,
  498. OUT ULONG * FIRMWARE_PTR Count
  499. )
  500. {
  501. DPRINT( TRACE, ("NetWrite\n") );
  502. return EROFS;
  503. } // NetWrite
  504. ARC_STATUS
  505. NetGetFileInformation (
  506. IN ULONG FileId,
  507. OUT FILE_INFORMATION * FIRMWARE_PTR Buffer
  508. )
  509. {
  510. PBL_FILE_TABLE fileTableEntry;
  511. //DPRINT( TRACE, ("NetGetFileInformation\n") );
  512. fileTableEntry = &BlFileTable[FileId];
  513. {
  514. Buffer->EndingAddress.QuadPart = fileTableEntry->u.NetFileContext.FileSize;
  515. Buffer->CurrentPosition.QuadPart = fileTableEntry->Position.QuadPart;
  516. DPRINT( REAL_LOUD, ("NetGetFileInformation returning size %x, position %x\n",
  517. Buffer->EndingAddress.LowPart, Buffer->CurrentPosition.LowPart) );
  518. return ESUCCESS;
  519. }
  520. } // NetGetFileInformation
  521. ARC_STATUS
  522. NetSetFileInformation (
  523. IN ULONG FileId,
  524. IN ULONG AttributeFlags,
  525. IN ULONG AttributeMask
  526. )
  527. {
  528. DPRINT( TRACE, ("NetSetFileInformation\n") );
  529. return EROFS;
  530. } // NetSetFileInformation
  531. ARC_STATUS
  532. NetRename (
  533. IN ULONG FileId,
  534. IN CHAR * FIRMWARE_PTR NewFileName
  535. )
  536. {
  537. DPRINT( TRACE, ("NetRename\n") );
  538. return EROFS;
  539. } // NetRename
  540. ARC_STATUS
  541. NetGetDirectoryEntry (
  542. IN ULONG FileId,
  543. IN DIRECTORY_ENTRY * FIRMWARE_PTR DirEntry,
  544. IN ULONG NumberDir,
  545. OUT ULONG * FIRMWARE_PTR CountDir
  546. )
  547. {
  548. DPRINT( TRACE, ("NetGetDirectoryEntry\n") );
  549. return EROFS;
  550. } // NetGetDirectoryEntry