SemihostFs.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. /** @file
  2. Support a Semi Host file system over a debuggers JTAG
  3. Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
  4. Portions copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.<BR>
  5. This program and the accompanying materials
  6. are licensed and made available under the terms and conditions of the BSD License
  7. which accompanies this distribution. The full text of the license may be found at
  8. http://opensource.org/licenses/bsd-license.php
  9. THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
  11. **/
  12. #include <Uefi.h>
  13. #include <Guid/FileInfo.h>
  14. #include <Guid/FileSystemInfo.h>
  15. #include <Guid/FileSystemVolumeLabelInfo.h>
  16. #include <Library/BaseLib.h>
  17. #include <Library/BaseMemoryLib.h>
  18. #include <Library/DebugLib.h>
  19. #include <Library/MemoryAllocationLib.h>
  20. #include <Library/SemihostLib.h>
  21. #include <Library/UefiBootServicesTableLib.h>
  22. #include <Library/UefiLib.h>
  23. #include <Protocol/DevicePath.h>
  24. #include <Protocol/SimpleFileSystem.h>
  25. #include "SemihostFs.h"
  26. #define DEFAULT_SEMIHOST_FS_LABEL L"SemihostFs"
  27. STATIC CHAR16 *mSemihostFsLabel;
  28. EFI_SIMPLE_FILE_SYSTEM_PROTOCOL gSemihostFs = {
  29. EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION,
  30. VolumeOpen
  31. };
  32. EFI_FILE gSemihostFsFile = {
  33. EFI_FILE_PROTOCOL_REVISION,
  34. FileOpen,
  35. FileClose,
  36. FileDelete,
  37. FileRead,
  38. FileWrite,
  39. FileGetPosition,
  40. FileSetPosition,
  41. FileGetInfo,
  42. FileSetInfo,
  43. FileFlush
  44. };
  45. //
  46. // Device path for SemiHosting. It contains our autogened Caller ID GUID.
  47. //
  48. typedef struct {
  49. VENDOR_DEVICE_PATH Guid;
  50. EFI_DEVICE_PATH_PROTOCOL End;
  51. } SEMIHOST_DEVICE_PATH;
  52. SEMIHOST_DEVICE_PATH gDevicePath = {
  53. {
  54. { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH), 0 } },
  55. EFI_CALLER_ID_GUID
  56. },
  57. { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 } }
  58. };
  59. typedef struct {
  60. LIST_ENTRY Link;
  61. UINT64 Signature;
  62. EFI_FILE File;
  63. CHAR8 *FileName;
  64. UINT64 OpenMode;
  65. UINT32 Position;
  66. UINTN SemihostHandle;
  67. BOOLEAN IsRoot;
  68. EFI_FILE_INFO Info;
  69. } SEMIHOST_FCB;
  70. #define SEMIHOST_FCB_SIGNATURE SIGNATURE_32( 'S', 'H', 'F', 'C' )
  71. #define SEMIHOST_FCB_FROM_THIS(a) CR(a, SEMIHOST_FCB, File, SEMIHOST_FCB_SIGNATURE)
  72. #define SEMIHOST_FCB_FROM_LINK(a) CR(a, SEMIHOST_FCB, Link, SEMIHOST_FCB_SIGNATURE);
  73. EFI_HANDLE gInstallHandle = NULL;
  74. LIST_ENTRY gFileList = INITIALIZE_LIST_HEAD_VARIABLE (gFileList);
  75. SEMIHOST_FCB *
  76. AllocateFCB (
  77. VOID
  78. )
  79. {
  80. SEMIHOST_FCB *Fcb = AllocateZeroPool (sizeof (SEMIHOST_FCB));
  81. if (Fcb != NULL) {
  82. CopyMem (&Fcb->File, &gSemihostFsFile, sizeof (gSemihostFsFile));
  83. Fcb->Signature = SEMIHOST_FCB_SIGNATURE;
  84. }
  85. return Fcb;
  86. }
  87. VOID
  88. FreeFCB (
  89. IN SEMIHOST_FCB *Fcb
  90. )
  91. {
  92. // Remove Fcb from gFileList.
  93. RemoveEntryList (&Fcb->Link);
  94. // To help debugging...
  95. Fcb->Signature = 0;
  96. FreePool (Fcb);
  97. }
  98. EFI_STATUS
  99. VolumeOpen (
  100. IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
  101. OUT EFI_FILE **Root
  102. )
  103. {
  104. SEMIHOST_FCB *RootFcb = NULL;
  105. if (Root == NULL) {
  106. return EFI_INVALID_PARAMETER;
  107. }
  108. RootFcb = AllocateFCB ();
  109. if (RootFcb == NULL) {
  110. return EFI_OUT_OF_RESOURCES;
  111. }
  112. RootFcb->IsRoot = TRUE;
  113. RootFcb->Info.Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY;
  114. InsertTailList (&gFileList, &RootFcb->Link);
  115. *Root = &RootFcb->File;
  116. return EFI_SUCCESS;
  117. }
  118. EFI_STATUS
  119. FileOpen (
  120. IN EFI_FILE *File,
  121. OUT EFI_FILE **NewHandle,
  122. IN CHAR16 *FileName,
  123. IN UINT64 OpenMode,
  124. IN UINT64 Attributes
  125. )
  126. {
  127. SEMIHOST_FCB *FileFcb = NULL;
  128. EFI_STATUS Status = EFI_SUCCESS;
  129. UINTN SemihostHandle;
  130. CHAR8 *AsciiFileName;
  131. UINT32 SemihostMode;
  132. BOOLEAN IsRoot;
  133. UINTN Length;
  134. if ((FileName == NULL) || (NewHandle == NULL)) {
  135. return EFI_INVALID_PARAMETER;
  136. }
  137. // Semihosting does not support directories
  138. if (Attributes & EFI_FILE_DIRECTORY) {
  139. return EFI_UNSUPPORTED;
  140. }
  141. // Semihost interface requires ASCII filenames
  142. AsciiFileName = AllocatePool ((StrLen (FileName) + 1) * sizeof (CHAR8));
  143. if (AsciiFileName == NULL) {
  144. return EFI_OUT_OF_RESOURCES;
  145. }
  146. UnicodeStrToAsciiStr (FileName, AsciiFileName);
  147. if ((AsciiStrCmp (AsciiFileName, "\\") == 0) ||
  148. (AsciiStrCmp (AsciiFileName, "/") == 0) ||
  149. (AsciiStrCmp (AsciiFileName, "") == 0) ||
  150. (AsciiStrCmp (AsciiFileName, ".") == 0)) {
  151. // Opening '/', '\', '.', or the NULL pathname is trying to open the root directory
  152. IsRoot = TRUE;
  153. // Root directory node doesn't have a name.
  154. FreePool (AsciiFileName);
  155. AsciiFileName = NULL;
  156. } else {
  157. // Translate EFI_FILE_MODE into Semihosting mode
  158. if (OpenMode & EFI_FILE_MODE_WRITE) {
  159. SemihostMode = SEMIHOST_FILE_MODE_WRITE | SEMIHOST_FILE_MODE_BINARY;
  160. } else if (OpenMode & EFI_FILE_MODE_READ) {
  161. SemihostMode = SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY;
  162. } else {
  163. return EFI_UNSUPPORTED;
  164. }
  165. // Add the creation flag if necessary
  166. if (OpenMode & EFI_FILE_MODE_CREATE) {
  167. SemihostMode |= SEMIHOST_FILE_MODE_CREATE;
  168. }
  169. // Call the semihosting interface to open the file.
  170. Status = SemihostFileOpen (AsciiFileName, SemihostMode, &SemihostHandle);
  171. if (EFI_ERROR(Status)) {
  172. return Status;
  173. }
  174. IsRoot = FALSE;
  175. }
  176. // Allocate a control block and fill it
  177. FileFcb = AllocateFCB ();
  178. if (FileFcb == NULL) {
  179. return EFI_OUT_OF_RESOURCES;
  180. }
  181. FileFcb->FileName = AsciiFileName;
  182. FileFcb->SemihostHandle = SemihostHandle;
  183. FileFcb->Position = 0;
  184. FileFcb->IsRoot = IsRoot;
  185. FileFcb->OpenMode = OpenMode;
  186. if (!IsRoot) {
  187. Status = SemihostFileLength (SemihostHandle, &Length);
  188. if (EFI_ERROR(Status)) {
  189. return Status;
  190. }
  191. FileFcb->Info.FileSize = Length;
  192. FileFcb->Info.PhysicalSize = Length;
  193. FileFcb->Info.Attribute = Attributes;
  194. }
  195. InsertTailList (&gFileList, &FileFcb->Link);
  196. *NewHandle = &FileFcb->File;
  197. return Status;
  198. }
  199. EFI_STATUS
  200. FileClose (
  201. IN EFI_FILE *File
  202. )
  203. {
  204. SEMIHOST_FCB *Fcb = NULL;
  205. EFI_STATUS Status = EFI_SUCCESS;
  206. Fcb = SEMIHOST_FCB_FROM_THIS(File);
  207. if (Fcb->IsRoot == TRUE) {
  208. FreeFCB (Fcb);
  209. Status = EFI_SUCCESS;
  210. } else {
  211. Status = SemihostFileClose (Fcb->SemihostHandle);
  212. if (!EFI_ERROR(Status)) {
  213. FreePool (Fcb->FileName);
  214. FreeFCB (Fcb);
  215. }
  216. }
  217. return Status;
  218. }
  219. EFI_STATUS
  220. FileDelete (
  221. IN EFI_FILE *File
  222. )
  223. {
  224. SEMIHOST_FCB *Fcb = NULL;
  225. EFI_STATUS Status;
  226. CHAR8 *FileName;
  227. UINTN NameSize;
  228. Fcb = SEMIHOST_FCB_FROM_THIS(File);
  229. if (!Fcb->IsRoot) {
  230. // Get the filename from the Fcb
  231. NameSize = AsciiStrLen (Fcb->FileName);
  232. FileName = AllocatePool (NameSize + 1);
  233. AsciiStrCpy (FileName, Fcb->FileName);
  234. // Close the file if it's open. Disregard return status,
  235. // since it might give an error if the file isn't open.
  236. File->Close (File);
  237. // Call the semihost interface to delete the file.
  238. Status = SemihostFileRemove (FileName);
  239. if (EFI_ERROR(Status)) {
  240. Status = EFI_WARN_DELETE_FAILURE;
  241. }
  242. } else {
  243. Status = EFI_WARN_DELETE_FAILURE;
  244. }
  245. return Status;
  246. }
  247. EFI_STATUS
  248. FileRead (
  249. IN EFI_FILE *File,
  250. IN OUT UINTN *BufferSize,
  251. OUT VOID *Buffer
  252. )
  253. {
  254. SEMIHOST_FCB *Fcb = NULL;
  255. EFI_STATUS Status;
  256. Fcb = SEMIHOST_FCB_FROM_THIS(File);
  257. if (Fcb->IsRoot == TRUE) {
  258. // By design, the Semihosting feature does not allow to list files on the host machine.
  259. Status = EFI_UNSUPPORTED;
  260. } else {
  261. Status = SemihostFileRead (Fcb->SemihostHandle, BufferSize, Buffer);
  262. if (!EFI_ERROR (Status)) {
  263. Fcb->Position += *BufferSize;
  264. }
  265. }
  266. return Status;
  267. }
  268. EFI_STATUS
  269. FileWrite (
  270. IN EFI_FILE *File,
  271. IN OUT UINTN *BufferSize,
  272. IN VOID *Buffer
  273. )
  274. {
  275. SEMIHOST_FCB *Fcb = NULL;
  276. EFI_STATUS Status;
  277. UINTN WriteSize = *BufferSize;
  278. Fcb = SEMIHOST_FCB_FROM_THIS(File);
  279. // We cannot write a read-only file
  280. if ((Fcb->Info.Attribute & EFI_FILE_READ_ONLY)
  281. || !(Fcb->OpenMode & EFI_FILE_MODE_WRITE)) {
  282. return EFI_ACCESS_DENIED;
  283. }
  284. Status = SemihostFileWrite (Fcb->SemihostHandle, &WriteSize, Buffer);
  285. if (!EFI_ERROR(Status)) {
  286. // Semihost write return the number of bytes *NOT* written.
  287. *BufferSize -= WriteSize;
  288. Fcb->Position += *BufferSize;
  289. }
  290. return Status;
  291. }
  292. EFI_STATUS
  293. FileGetPosition (
  294. IN EFI_FILE *File,
  295. OUT UINT64 *Position
  296. )
  297. {
  298. SEMIHOST_FCB *Fcb = NULL;
  299. if (Position == NULL) {
  300. return EFI_INVALID_PARAMETER;
  301. }
  302. Fcb = SEMIHOST_FCB_FROM_THIS(File);
  303. *Position = Fcb->Position;
  304. return EFI_SUCCESS;
  305. }
  306. EFI_STATUS
  307. FileSetPosition (
  308. IN EFI_FILE *File,
  309. IN UINT64 Position
  310. )
  311. {
  312. SEMIHOST_FCB *Fcb = NULL;
  313. UINTN Length;
  314. EFI_STATUS Status;
  315. Fcb = SEMIHOST_FCB_FROM_THIS(File);
  316. if (!Fcb->IsRoot) {
  317. Status = SemihostFileLength (Fcb->SemihostHandle, &Length);
  318. if (!EFI_ERROR(Status) && (Length < Position)) {
  319. Position = Length;
  320. }
  321. Status = SemihostFileSeek (Fcb->SemihostHandle, (UINT32)Position);
  322. if (!EFI_ERROR(Status)) {
  323. Fcb->Position = Position;
  324. }
  325. } else {
  326. Fcb->Position = Position;
  327. Status = EFI_SUCCESS;
  328. }
  329. return Status;
  330. }
  331. STATIC
  332. EFI_STATUS
  333. GetFileInfo (
  334. IN SEMIHOST_FCB *Fcb,
  335. IN OUT UINTN *BufferSize,
  336. OUT VOID *Buffer
  337. )
  338. {
  339. EFI_FILE_INFO *Info = NULL;
  340. UINTN NameSize = 0;
  341. UINTN ResultSize;
  342. UINTN Index;
  343. if (Fcb->IsRoot == TRUE) {
  344. ResultSize = SIZE_OF_EFI_FILE_INFO + sizeof(CHAR16);
  345. } else {
  346. NameSize = AsciiStrLen (Fcb->FileName) + 1;
  347. ResultSize = SIZE_OF_EFI_FILE_INFO + NameSize * sizeof (CHAR16);
  348. }
  349. if (*BufferSize < ResultSize) {
  350. *BufferSize = ResultSize;
  351. return EFI_BUFFER_TOO_SMALL;
  352. }
  353. Info = Buffer;
  354. // Copy the current file info
  355. CopyMem (Info, &Fcb->Info, SIZE_OF_EFI_FILE_INFO);
  356. // Fill in the structure
  357. Info->Size = ResultSize;
  358. if (Fcb->IsRoot == TRUE) {
  359. Info->FileName[0] = L'\0';
  360. } else {
  361. for (Index = 0; Index < NameSize; Index++) {
  362. Info->FileName[Index] = Fcb->FileName[Index];
  363. }
  364. }
  365. *BufferSize = ResultSize;
  366. return EFI_SUCCESS;
  367. }
  368. STATIC
  369. EFI_STATUS
  370. GetFilesystemInfo (
  371. IN SEMIHOST_FCB *Fcb,
  372. IN OUT UINTN *BufferSize,
  373. OUT VOID *Buffer
  374. )
  375. {
  376. EFI_FILE_SYSTEM_INFO *Info = NULL;
  377. EFI_STATUS Status;
  378. UINTN ResultSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (mSemihostFsLabel);
  379. if (*BufferSize >= ResultSize) {
  380. ZeroMem (Buffer, ResultSize);
  381. Status = EFI_SUCCESS;
  382. Info = Buffer;
  383. Info->Size = ResultSize;
  384. Info->ReadOnly = FALSE;
  385. Info->VolumeSize = 0;
  386. Info->FreeSpace = 0;
  387. Info->BlockSize = 0;
  388. StrCpy (Info->VolumeLabel, mSemihostFsLabel);
  389. } else {
  390. Status = EFI_BUFFER_TOO_SMALL;
  391. }
  392. *BufferSize = ResultSize;
  393. return Status;
  394. }
  395. EFI_STATUS
  396. FileGetInfo (
  397. IN EFI_FILE *File,
  398. IN EFI_GUID *InformationType,
  399. IN OUT UINTN *BufferSize,
  400. OUT VOID *Buffer
  401. )
  402. {
  403. SEMIHOST_FCB *Fcb;
  404. EFI_STATUS Status;
  405. UINTN ResultSize;
  406. Fcb = SEMIHOST_FCB_FROM_THIS(File);
  407. if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid) != 0) {
  408. Status = GetFilesystemInfo (Fcb, BufferSize, Buffer);
  409. } else if (CompareGuid (InformationType, &gEfiFileInfoGuid) != 0) {
  410. Status = GetFileInfo (Fcb, BufferSize, Buffer);
  411. } else if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid) != 0) {
  412. ResultSize = StrSize (mSemihostFsLabel);
  413. if (*BufferSize >= ResultSize) {
  414. StrCpy (Buffer, mSemihostFsLabel);
  415. Status = EFI_SUCCESS;
  416. } else {
  417. Status = EFI_BUFFER_TOO_SMALL;
  418. }
  419. *BufferSize = ResultSize;
  420. } else {
  421. Status = EFI_UNSUPPORTED;
  422. }
  423. return Status;
  424. }
  425. EFI_STATUS
  426. FileSetInfo (
  427. IN EFI_FILE *File,
  428. IN EFI_GUID *InformationType,
  429. IN UINTN BufferSize,
  430. IN VOID *Buffer
  431. )
  432. {
  433. EFI_STATUS Status;
  434. if (Buffer == NULL) {
  435. return EFI_INVALID_PARAMETER;
  436. }
  437. Status = EFI_UNSUPPORTED;
  438. if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid) != 0) {
  439. //Status = SetFilesystemInfo (Fcb, BufferSize, Buffer);
  440. } else if (CompareGuid (InformationType, &gEfiFileInfoGuid) != 0) {
  441. // Semihosting does not give us access to setting file info, but
  442. // if we fail here we cannot create new files.
  443. Status = EFI_SUCCESS;
  444. } else if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid) != 0) {
  445. if (StrSize (Buffer) > 0) {
  446. FreePool (mSemihostFsLabel);
  447. mSemihostFsLabel = AllocateCopyPool (StrSize (Buffer), Buffer);
  448. Status = EFI_SUCCESS;
  449. }
  450. }
  451. return Status;
  452. }
  453. EFI_STATUS
  454. FileFlush (
  455. IN EFI_FILE *File
  456. )
  457. {
  458. SEMIHOST_FCB *Fcb;
  459. Fcb = SEMIHOST_FCB_FROM_THIS(File);
  460. if (Fcb->IsRoot) {
  461. return EFI_SUCCESS;
  462. } else {
  463. if ((Fcb->Info.Attribute & EFI_FILE_READ_ONLY)
  464. || !(Fcb->OpenMode & EFI_FILE_MODE_WRITE)) {
  465. return EFI_ACCESS_DENIED;
  466. } else {
  467. return EFI_SUCCESS;
  468. }
  469. }
  470. }
  471. EFI_STATUS
  472. SemihostFsEntryPoint (
  473. IN EFI_HANDLE ImageHandle,
  474. IN EFI_SYSTEM_TABLE *SystemTable
  475. )
  476. {
  477. EFI_STATUS Status;
  478. Status = EFI_NOT_FOUND;
  479. if (SemihostConnectionSupported ()) {
  480. mSemihostFsLabel = AllocateCopyPool (StrSize (DEFAULT_SEMIHOST_FS_LABEL), DEFAULT_SEMIHOST_FS_LABEL);
  481. if (mSemihostFsLabel == NULL) {
  482. return EFI_OUT_OF_RESOURCES;
  483. }
  484. Status = gBS->InstallMultipleProtocolInterfaces (
  485. &gInstallHandle,
  486. &gEfiSimpleFileSystemProtocolGuid, &gSemihostFs,
  487. &gEfiDevicePathProtocolGuid, &gDevicePath,
  488. NULL
  489. );
  490. if (EFI_ERROR(Status)) {
  491. FreePool (mSemihostFsLabel);
  492. }
  493. }
  494. return Status;
  495. }