diskio.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. /*++
  2. Copyright (c) 2014 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. diskio.c
  5. Abstract:
  6. This module implements the UEFI disk I/O protocol.
  7. Author:
  8. Evan Green 20-Mar-2014
  9. Environment:
  10. Firmware
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include "ueficore.h"
  16. #include <minoca/uefi/protocol/diskio.h>
  17. #include <minoca/uefi/protocol/blockio.h>
  18. #include <minoca/uefi/protocol/drvbind.h>
  19. //
  20. // --------------------------------------------------------------------- Macros
  21. //
  22. //
  23. // This macro returns a pointer to the disk I/O data given a pointer to the
  24. // block I/O protocol instance.
  25. //
  26. #define EFI_DISK_IO_DATA_FROM_THIS(_DiskIo) \
  27. PARENT_STRUCTURE(_DiskIo, EFI_DISK_IO_DATA, DiskIo)
  28. //
  29. // ---------------------------------------------------------------- Definitions
  30. //
  31. #define EFI_DISK_IO_DATA_MAGIC 0x6B736944 // 'ksiD'
  32. //
  33. // ------------------------------------------------------ Data Type Definitions
  34. //
  35. /*++
  36. Structure Description:
  37. This structure stores the disk I/O protocol's private context.
  38. Members:
  39. Magic - Stores the magic constand EFI_DISK_IO_DATA_MAGIC.
  40. DiskIo - Stores the disk I/O protocol.
  41. BlockIo - Stores a pointer to the block I/O protocol.
  42. --*/
  43. typedef struct _EFI_DISK_IO_DATA {
  44. UINT32 Magic;
  45. EFI_DISK_IO_PROTOCOL DiskIo;
  46. EFI_BLOCK_IO_PROTOCOL *BlockIo;
  47. } EFI_DISK_IO_DATA, *PEFI_DISK_IO_DATA;
  48. //
  49. // ----------------------------------------------- Internal Function Prototypes
  50. //
  51. EFIAPI
  52. EFI_STATUS
  53. EfiDiskIoSupported (
  54. EFI_DRIVER_BINDING_PROTOCOL *This,
  55. EFI_HANDLE ControllerHandle,
  56. EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
  57. );
  58. EFIAPI
  59. EFI_STATUS
  60. EfiDiskIoStart (
  61. EFI_DRIVER_BINDING_PROTOCOL *This,
  62. EFI_HANDLE ControllerHandle,
  63. EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
  64. );
  65. EFIAPI
  66. EFI_STATUS
  67. EfiDiskIoStop (
  68. EFI_DRIVER_BINDING_PROTOCOL *This,
  69. EFI_HANDLE ControllerHandle,
  70. UINTN NumberOfChildren,
  71. EFI_HANDLE *ChildHandleBuffer
  72. );
  73. EFIAPI
  74. EFI_STATUS
  75. EfiDiskIoRead (
  76. EFI_DISK_IO_PROTOCOL *This,
  77. UINT32 MediaId,
  78. UINT64 Offset,
  79. UINTN BufferSize,
  80. VOID *Buffer
  81. );
  82. EFIAPI
  83. EFI_STATUS
  84. EfiDiskIoWrite (
  85. EFI_DISK_IO_PROTOCOL *This,
  86. UINT32 MediaId,
  87. UINT64 Offset,
  88. UINTN BufferSize,
  89. VOID *Buffer
  90. );
  91. //
  92. // -------------------------------------------------------------------- Globals
  93. //
  94. EFI_DRIVER_BINDING_PROTOCOL EfiDiskIoDriverBinding = {
  95. EfiDiskIoSupported,
  96. EfiDiskIoStart,
  97. EfiDiskIoStop,
  98. 0xA,
  99. NULL,
  100. NULL
  101. };
  102. EFI_DISK_IO_DATA EfiDiskIoDataTemplate = {
  103. EFI_DISK_IO_DATA_MAGIC,
  104. {
  105. EFI_DISK_IO_PROTOCOL_REVISION,
  106. EfiDiskIoRead,
  107. EfiDiskIoWrite
  108. },
  109. NULL
  110. };
  111. EFI_GUID EfiDiskIoProtocolGuid = EFI_DISK_IO_PROTOCOL_GUID;
  112. //
  113. // ------------------------------------------------------------------ Functions
  114. //
  115. EFIAPI
  116. EFI_STATUS
  117. EfiDiskIoDriverEntry (
  118. EFI_HANDLE ImageHandle,
  119. EFI_SYSTEM_TABLE *SystemTable
  120. )
  121. /*++
  122. Routine Description:
  123. This routine is the entry point into the disk I/O driver.
  124. Arguments:
  125. ImageHandle - Supplies the driver image handle.
  126. SystemTable - Supplies a pointer to the EFI system table.
  127. Return Value:
  128. EFI status code.
  129. --*/
  130. {
  131. EFI_STATUS Status;
  132. EfiDiskIoDriverBinding.ImageHandle = ImageHandle;
  133. EfiDiskIoDriverBinding.DriverBindingHandle = ImageHandle;
  134. Status = EfiInstallMultipleProtocolInterfaces(
  135. &(EfiDiskIoDriverBinding.DriverBindingHandle),
  136. &EfiDriverBindingProtocolGuid,
  137. &EfiDiskIoDriverBinding,
  138. NULL);
  139. return Status;
  140. }
  141. //
  142. // --------------------------------------------------------- Internal Functions
  143. //
  144. EFIAPI
  145. EFI_STATUS
  146. EfiDiskIoSupported (
  147. EFI_DRIVER_BINDING_PROTOCOL *This,
  148. EFI_HANDLE ControllerHandle,
  149. EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
  150. )
  151. /*++
  152. Routine Description:
  153. This routine tests to see if the disk I/O driver supports this new
  154. controller handle. Any controller handle that contains a block protocol is
  155. supported.
  156. Arguments:
  157. This - Supplies a pointer to the driver binding instance.
  158. ControllerHandle - Supplies the new controller handle to test.
  159. RemainingDevicePath - Supplies an optional parameter to pick a specific
  160. child device to start.
  161. Return Value:
  162. EFI status code.
  163. --*/
  164. {
  165. EFI_BLOCK_IO_PROTOCOL *BlockIo;
  166. EFI_STATUS Status;
  167. Status = EfiOpenProtocol(ControllerHandle,
  168. &EfiBlockIoProtocolGuid,
  169. (VOID **)&BlockIo,
  170. This->DriverBindingHandle,
  171. ControllerHandle,
  172. EFI_OPEN_PROTOCOL_BY_DRIVER);
  173. if (EFI_ERROR(Status)) {
  174. return Status;
  175. }
  176. EfiCloseProtocol(ControllerHandle,
  177. &EfiBlockIoProtocolGuid,
  178. This->DriverBindingHandle,
  179. ControllerHandle);
  180. return EFI_SUCCESS;
  181. }
  182. EFIAPI
  183. EFI_STATUS
  184. EfiDiskIoStart (
  185. EFI_DRIVER_BINDING_PROTOCOL *This,
  186. EFI_HANDLE ControllerHandle,
  187. EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
  188. )
  189. /*++
  190. Routine Description:
  191. This routine starts a disk I/O driver on a raw Block I/O device.
  192. Arguments:
  193. This - Supplies a pointer to the driver binding protocol instance.
  194. ControllerHandle - Supplies the handle of the controller to start. This
  195. handle must support a protocol interface that supplies an I/O
  196. abstraction to the driver.
  197. RemainingDevicePath - Supplies an optional pointer to the remaining
  198. portion of a device path.
  199. Return Value:
  200. EFI_SUCCESS if the device was started.
  201. EFI_DEVICE_ERROR if the device could not be started due to a device error.
  202. EFI_OUT_OF_RESOURCES if an allocation failed.
  203. Other error codes if the driver failed to start the device.
  204. --*/
  205. {
  206. PEFI_DISK_IO_DATA Instance;
  207. EFI_TPL OldTpl;
  208. BOOLEAN ProtocolOpen;
  209. EFI_STATUS Status;
  210. Instance = NULL;
  211. ProtocolOpen = FALSE;
  212. OldTpl = EfiRaiseTPL(TPL_CALLBACK);
  213. //
  214. // Connect to the block I/O interface.
  215. //
  216. Status = EfiOpenProtocol(ControllerHandle,
  217. &EfiBlockIoProtocolGuid,
  218. (VOID **)&(EfiDiskIoDataTemplate.BlockIo),
  219. This->DriverBindingHandle,
  220. ControllerHandle,
  221. EFI_OPEN_PROTOCOL_BY_DRIVER);
  222. if (EFI_ERROR(Status)) {
  223. goto DiskIoStartEnd;
  224. }
  225. ProtocolOpen = TRUE;
  226. Instance = EfiCoreAllocateBootPool(sizeof(EFI_DISK_IO_DATA));
  227. if (Instance == NULL) {
  228. Status = EFI_OUT_OF_RESOURCES;
  229. goto DiskIoStartEnd;
  230. }
  231. EfiCopyMem(Instance, &EfiDiskIoDataTemplate, sizeof(EFI_DISK_IO_DATA));
  232. Status = EfiInstallMultipleProtocolInterfaces(&ControllerHandle,
  233. &EfiDiskIoProtocolGuid,
  234. &(Instance->DiskIo),
  235. NULL);
  236. if (EFI_ERROR(Status)) {
  237. goto DiskIoStartEnd;
  238. }
  239. DiskIoStartEnd:
  240. if (EFI_ERROR(Status)) {
  241. if (Instance != NULL) {
  242. EfiFreePool(Instance);
  243. }
  244. if (ProtocolOpen != FALSE) {
  245. EfiCloseProtocol(ControllerHandle,
  246. &EfiBlockIoProtocolGuid,
  247. This->DriverBindingHandle,
  248. ControllerHandle);
  249. }
  250. }
  251. EfiRestoreTPL(OldTpl);
  252. return Status;
  253. }
  254. EFIAPI
  255. EFI_STATUS
  256. EfiDiskIoStop (
  257. EFI_DRIVER_BINDING_PROTOCOL *This,
  258. EFI_HANDLE ControllerHandle,
  259. UINTN NumberOfChildren,
  260. EFI_HANDLE *ChildHandleBuffer
  261. )
  262. /*++
  263. Routine Description:
  264. This routine stops a disk I/O driver device, stopping any child handles
  265. created by this driver.
  266. Arguments:
  267. This - Supplies a pointer to the driver binding protocol instance.
  268. ControllerHandle - Supplies the handle of the device being stopped. The
  269. handle must support a bus specific I/O protocol for the driver to use
  270. to stop the device.
  271. NumberOfChildren - Supplies the number of child devices in the child handle
  272. buffer.
  273. ChildHandleBuffer - Supplies an optional array of child device handles to
  274. be freed. This can be NULL if the number of children specified is zero.
  275. Return Value:
  276. EFI_SUCCESS if the device was stopped.
  277. EFI_DEVICE_ERROR if the device could not be stopped due to a device error.
  278. --*/
  279. {
  280. EFI_DISK_IO_PROTOCOL *DiskIo;
  281. PEFI_DISK_IO_DATA Instance;
  282. EFI_STATUS Status;
  283. //
  284. // Get the context back.
  285. //
  286. Status = EfiOpenProtocol(ControllerHandle,
  287. &EfiDiskIoProtocolGuid,
  288. (VOID **)&DiskIo,
  289. This->DriverBindingHandle,
  290. ControllerHandle,
  291. EFI_OPEN_PROTOCOL_GET_PROTOCOL);
  292. if (EFI_ERROR(Status)) {
  293. return Status;
  294. }
  295. Instance = EFI_DISK_IO_DATA_FROM_THIS(DiskIo);
  296. Status = EfiUninstallMultipleProtocolInterfaces(ControllerHandle,
  297. &EfiDiskIoProtocolGuid,
  298. &(Instance->DiskIo),
  299. NULL);
  300. if (!EFI_ERROR(Status)) {
  301. Status = EfiCloseProtocol(ControllerHandle,
  302. &EfiBlockIoProtocolGuid,
  303. This->DriverBindingHandle,
  304. ControllerHandle);
  305. ASSERT(!EFI_ERROR(Status));
  306. EfiFreePool(Instance);
  307. }
  308. return Status;
  309. }
  310. EFIAPI
  311. EFI_STATUS
  312. EfiDiskIoRead (
  313. EFI_DISK_IO_PROTOCOL *This,
  314. UINT32 MediaId,
  315. UINT64 Offset,
  316. UINTN BufferSize,
  317. VOID *Buffer
  318. )
  319. /*++
  320. Routine Description:
  321. This routine reads bytes from the disk.
  322. Arguments:
  323. This - Supplies the protocol instance.
  324. MediaId - Supplies the ID of the media, which changes every time the media
  325. is replaced.
  326. Offset - Supplies the starting byte offset to read from.
  327. BufferSize - Supplies the size of the given buffer.
  328. Buffer - Supplies a pointer where the read data will be returned.
  329. Return Value:
  330. EFI_SUCCESS if all data was successfully read.
  331. EFI_DEVICE_ERROR if a hardware error occurred while performing the
  332. operation.
  333. EFI_NO_MEDIA if there is no media in the device.
  334. EFI_MEDIA_CHANGED if the current media ID doesn't match the one passed in.
  335. EFI_INVALID_PARAMETER if the offset is invalid.
  336. --*/
  337. {
  338. EFI_BLOCK_IO_PROTOCOL *BlockIo;
  339. EFI_LBA BlockOffset;
  340. UINTN BlockOffsetRemainder;
  341. UINT32 BlockSize;
  342. VOID *BounceBuffer;
  343. VOID *BounceBufferAllocation;
  344. UINTN BounceBufferSize;
  345. PEFI_DISK_IO_DATA Instance;
  346. UINT32 IoAlign;
  347. UINTN IoSize;
  348. EFI_STATUS Status;
  349. Instance = EFI_DISK_IO_DATA_FROM_THIS(This);
  350. ASSERT(Instance->Magic == EFI_DISK_IO_DATA_MAGIC);
  351. BlockIo = Instance->BlockIo;
  352. if (BlockIo->Media->MediaPresent == FALSE) {
  353. return EFI_NO_MEDIA;
  354. }
  355. if (BufferSize == 0) {
  356. return EFI_INVALID_PARAMETER;
  357. }
  358. ASSERT(BufferSize != 0);
  359. //
  360. // Pass it down directly if it all lines up.
  361. //
  362. BlockSize = BlockIo->Media->BlockSize;
  363. IoAlign = BlockIo->Media->IoAlign;
  364. if (((Offset % BlockSize) == 0) &&
  365. ((IoAlign == 0) || (((UINTN)Buffer % IoAlign) == 0))) {
  366. Status = BlockIo->ReadBlocks(BlockIo,
  367. BlockIo->Media->MediaId,
  368. Offset / BlockSize,
  369. BufferSize,
  370. Buffer);
  371. return Status;
  372. }
  373. //
  374. // Allocate a bounce buffer for the read. The I/O size must be a multiple
  375. // of the block size. The buffer must be aligned, so allocate enough
  376. // space to scoot it up by the alignment.
  377. //
  378. BlockOffset = Offset / BlockSize;
  379. BlockOffsetRemainder = Offset - (Offset * BlockSize);
  380. IoSize = (BufferSize + BlockOffsetRemainder + (BlockSize - 1)) / BlockSize;
  381. BounceBufferSize = IoSize + IoAlign;
  382. BounceBufferAllocation = EfiCoreAllocateBootPool(BounceBufferSize);
  383. if (BounceBufferAllocation == NULL) {
  384. return EFI_OUT_OF_RESOURCES;
  385. }
  386. BounceBuffer = ALIGN_POINTER(BounceBufferAllocation, IoAlign);
  387. //
  388. // Perform the read.
  389. //
  390. Status = BlockIo->ReadBlocks(BlockIo,
  391. BlockIo->Media->MediaId,
  392. BlockOffset,
  393. IoSize,
  394. BounceBuffer);
  395. //
  396. // If nothing went wrong, copy the result in to the final buffer.
  397. //
  398. if (!EFI_ERROR(Status)) {
  399. EfiCopyMem(Buffer, BounceBuffer + BlockOffsetRemainder, BufferSize);
  400. } else {
  401. EfiDebugPrint("IO Read Error block 0x%I64x Size %x: %x\n",
  402. BlockOffset,
  403. IoSize,
  404. Status);
  405. }
  406. EfiFreePool(BounceBufferAllocation);
  407. return Status;
  408. }
  409. EFIAPI
  410. EFI_STATUS
  411. EfiDiskIoWrite (
  412. EFI_DISK_IO_PROTOCOL *This,
  413. UINT32 MediaId,
  414. UINT64 Offset,
  415. UINTN BufferSize,
  416. VOID *Buffer
  417. )
  418. /*++
  419. Routine Description:
  420. This routine writes bytes to the disk.
  421. Arguments:
  422. This - Supplies the protocol instance.
  423. MediaId - Supplies the ID of the media, which changes every time the media
  424. is replaced.
  425. Offset - Supplies the starting byte offset to write to.
  426. BufferSize - Supplies the size of the given buffer.
  427. Buffer - Supplies a pointer containing the data to write.
  428. Return Value:
  429. EFI_SUCCESS if all data was successfully written.
  430. EFI_WRITE_PROTECTED if the device cannot be written to.
  431. EFI_DEVICE_ERROR if a hardware error occurred while performing the
  432. operation.
  433. EFI_NO_MEDIA if there is no media in the device.
  434. EFI_MEDIA_CHANGED if the current media ID doesn't match the one passed in.
  435. EFI_INVALID_PARAMETER if the offset is invalid.
  436. --*/
  437. {
  438. EFI_BLOCK_IO_PROTOCOL *BlockIo;
  439. EFI_LBA BlockOffset;
  440. UINTN BlockOffsetRemainder;
  441. UINT32 BlockSize;
  442. VOID *BounceBuffer;
  443. VOID *BounceBufferAllocation;
  444. UINTN BounceBufferSize;
  445. PEFI_DISK_IO_DATA Instance;
  446. UINT32 IoAlign;
  447. UINTN IoSize;
  448. EFI_STATUS Status;
  449. Instance = EFI_DISK_IO_DATA_FROM_THIS(This);
  450. ASSERT(Instance->Magic == EFI_DISK_IO_DATA_MAGIC);
  451. BlockIo = Instance->BlockIo;
  452. if (BlockIo->Media->MediaPresent == FALSE) {
  453. return STATUS_NO_MEDIA;
  454. }
  455. if (BufferSize == 0) {
  456. return EFI_INVALID_PARAMETER;
  457. }
  458. ASSERT(BufferSize != 0);
  459. //
  460. // Pass it down directly if it all lines up.
  461. //
  462. BlockSize = BlockIo->Media->BlockSize;
  463. IoAlign = BlockIo->Media->IoAlign;
  464. if (((Offset % BlockSize) == 0) &&
  465. ((IoAlign == 0) || (((UINTN)Buffer % IoAlign) == 0))) {
  466. Status = BlockIo->WriteBlocks(BlockIo,
  467. BlockIo->Media->MediaId,
  468. Offset / BlockSize,
  469. BufferSize,
  470. Buffer);
  471. return Status;
  472. }
  473. //
  474. // Allocate a bounce buffer for the write. The I/O size must be a multiple
  475. // of the block size. The buffer must be aligned, so allocate enough
  476. // space to scoot it up by the alignment.
  477. //
  478. BlockOffset = Offset / BlockSize;
  479. BlockOffsetRemainder = Offset - (Offset * BlockSize);
  480. IoSize = (BufferSize + BlockOffsetRemainder + (BlockSize - 1)) / BlockSize;
  481. BounceBufferSize = IoSize + IoAlign;
  482. BounceBufferAllocation = EfiCoreAllocateBootPool(BounceBufferSize);
  483. if (BounceBufferAllocation == NULL) {
  484. return EFI_OUT_OF_RESOURCES;
  485. }
  486. BounceBuffer = ALIGN_POINTER(BounceBufferAllocation, IoAlign);
  487. //
  488. // Perform the read to get the original block data.
  489. //
  490. Status = BlockIo->ReadBlocks(BlockIo,
  491. BlockIo->Media->MediaId,
  492. BlockOffset,
  493. IoSize,
  494. BounceBuffer);
  495. //
  496. // If nothing went wrong, copy the result in to the final buffer.
  497. //
  498. if (EFI_ERROR(Status)) {
  499. EfiDebugPrint("IO Read Error block 0x%I64x Size %x: %x\n",
  500. BlockOffset,
  501. IoSize,
  502. Status);
  503. goto DiskIoWriteEnd;
  504. }
  505. //
  506. // Write the data in.
  507. //
  508. EfiCopyMem(BounceBuffer + BlockOffsetRemainder, Buffer, BufferSize);
  509. //
  510. // Now write the blocks.
  511. //
  512. Status = BlockIo->WriteBlocks(BlockIo,
  513. BlockIo->Media->MediaId,
  514. BlockOffset,
  515. IoSize,
  516. BounceBuffer);
  517. if (EFI_ERROR(Status)) {
  518. EfiDebugPrint("IO Write Error block 0x%I64x Size %x: %x\n",
  519. BlockOffset,
  520. IoSize,
  521. Status);
  522. goto DiskIoWriteEnd;
  523. }
  524. DiskIoWriteEnd:
  525. EfiFreePool(BounceBufferAllocation);
  526. return Status;
  527. }