]> git.proxmox.com Git - mirror_edk2.git/blob - EmulatorPkg/Unix/Host/BlockIo.c
EmulatorPkg/Unix: Fix various typos
[mirror_edk2.git] / EmulatorPkg / Unix / Host / BlockIo.c
1 /**@file
2
3 Copyright (c) 2004 - 2009, Intel Corporation. All rights reserved.<BR>
4 SPDX-License-Identifier: BSD-2-Clause-Patent
5
6 **/
7
8 #include "Host.h"
9
10 #define EMU_BLOCK_IO_PRIVATE_SIGNATURE SIGNATURE_32 ('E', 'M', 'b', 'k')
11 typedef struct {
12 UINTN Signature;
13
14 EMU_IO_THUNK_PROTOCOL *Thunk;
15
16 char *Filename;
17 UINTN ReadMode;
18 UINTN Mode;
19
20 int fd;
21
22 BOOLEAN RemovableMedia;
23 BOOLEAN WriteProtected;
24
25 UINT64 NumberOfBlocks;
26 UINT32 BlockSize;
27
28 EMU_BLOCK_IO_PROTOCOL EmuBlockIo;
29 EFI_BLOCK_IO_MEDIA *Media;
30
31 } EMU_BLOCK_IO_PRIVATE;
32
33 #define EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS(a) \
34 CR(a, EMU_BLOCK_IO_PRIVATE, EmuBlockIo, EMU_BLOCK_IO_PRIVATE_SIGNATURE)
35
36
37
38 EFI_STATUS
39 EmuBlockIoReset (
40 IN EMU_BLOCK_IO_PROTOCOL *This,
41 IN BOOLEAN ExtendedVerification
42 );
43
44
45 /*++
46
47 This function extends the capability of SetFilePointer to accept 64 bit parameters
48
49 **/
50 EFI_STATUS
51 SetFilePointer64 (
52 IN EMU_BLOCK_IO_PRIVATE *Private,
53 IN INT64 DistanceToMove,
54 OUT UINT64 *NewFilePointer,
55 IN INT32 MoveMethod
56 )
57 {
58 EFI_STATUS Status;
59 off_t res;
60 off_t offset = DistanceToMove;
61
62 Status = EFI_SUCCESS;
63 res = lseek (Private->fd, offset, (int)MoveMethod);
64 if (res == -1) {
65 Status = EFI_INVALID_PARAMETER;
66 }
67
68 if (NewFilePointer != NULL) {
69 *NewFilePointer = res;
70 }
71
72 return Status;
73 }
74
75
76 EFI_STATUS
77 EmuBlockIoOpenDevice (
78 IN EMU_BLOCK_IO_PRIVATE *Private
79 )
80 {
81 EFI_STATUS Status;
82 UINT64 FileSize;
83 struct statfs buf;
84
85
86 //
87 // If the device is already opened, close it
88 //
89 if (Private->fd >= 0) {
90 EmuBlockIoReset (&Private->EmuBlockIo, FALSE);
91 }
92
93 //
94 // Open the device
95 //
96 Private->fd = open (Private->Filename, Private->Mode, 0644);
97 if (Private->fd < 0) {
98 printf ("EmuOpenBlock: Could not open %s: %s\n", Private->Filename, strerror(errno));
99 Private->Media->MediaPresent = FALSE;
100 Status = EFI_NO_MEDIA;
101 goto Done;
102 }
103
104 if (!Private->Media->MediaPresent) {
105 //
106 // BugBug: try to emulate if a CD appears - notify drivers to check it out
107 //
108 Private->Media->MediaPresent = TRUE;
109 }
110
111 //
112 // get the size of the file
113 //
114 Status = SetFilePointer64 (Private, 0, &FileSize, SEEK_END);
115 if (EFI_ERROR (Status)) {
116 printf ("EmuOpenBlock: Could not get filesize of %s\n", Private->Filename);
117 Status = EFI_UNSUPPORTED;
118 goto Done;
119 }
120
121 if (FileSize == 0) {
122 // lseek fails on a real device. ioctl calls are OS specific
123 #if __APPLE__
124 {
125 UINT32 BlockSize;
126
127 if (ioctl (Private->fd, DKIOCGETBLOCKSIZE, &BlockSize) == 0) {
128 Private->Media->BlockSize = BlockSize;
129 }
130 if (ioctl (Private->fd, DKIOCGETBLOCKCOUNT, &Private->NumberOfBlocks) == 0) {
131 if ((Private->NumberOfBlocks == 0) && (BlockSize == 0x800)) {
132 // A DVD is ~ 4.37 GB so make up a number
133 Private->Media->LastBlock = (0x100000000ULL/0x800) - 1;
134 } else {
135 Private->Media->LastBlock = Private->NumberOfBlocks - 1;
136 }
137 }
138 ioctl (Private->fd, DKIOCGETMAXBLOCKCOUNTWRITE, &Private->Media->OptimalTransferLengthGranularity);
139 }
140 #else
141 {
142 size_t BlockSize;
143 UINT64 DiskSize;
144
145 if (ioctl (Private->fd, BLKSSZGET, &BlockSize) == 0) {
146 Private->Media->BlockSize = BlockSize;
147 }
148 if (ioctl (Private->fd, BLKGETSIZE64, &DiskSize) == 0) {
149 Private->NumberOfBlocks = DivU64x32 (DiskSize, (UINT32)BlockSize);
150 Private->Media->LastBlock = Private->NumberOfBlocks - 1;
151 }
152 }
153 #endif
154
155 } else {
156 Private->Media->BlockSize = Private->BlockSize;
157 Private->NumberOfBlocks = DivU64x32 (FileSize, Private->Media->BlockSize);
158 Private->Media->LastBlock = Private->NumberOfBlocks - 1;
159
160 if (fstatfs (Private->fd, &buf) == 0) {
161 #if __APPLE__
162 Private->Media->OptimalTransferLengthGranularity = buf.f_iosize/buf.f_bsize;
163 #else
164 Private->Media->OptimalTransferLengthGranularity = buf.f_bsize/buf.f_bsize;
165 #endif
166 }
167 }
168
169 DEBUG ((EFI_D_INIT, "%HEmuOpenBlock: opened %a%N\n", Private->Filename));
170 Status = EFI_SUCCESS;
171
172 Done:
173 if (EFI_ERROR (Status)) {
174 if (Private->fd >= 0) {
175 EmuBlockIoReset (&Private->EmuBlockIo, FALSE);
176 }
177 }
178
179 return Status;
180 }
181
182
183 EFI_STATUS
184 EmuBlockIoCreateMapping (
185 IN EMU_BLOCK_IO_PROTOCOL *This,
186 IN EFI_BLOCK_IO_MEDIA *Media
187 )
188 {
189 EFI_STATUS Status;
190 EMU_BLOCK_IO_PRIVATE *Private;
191
192 Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This);
193
194 Private->Media = Media;
195
196 Media->MediaId = 0;
197 Media->RemovableMedia = Private->RemovableMedia;
198 Media->MediaPresent = TRUE;
199 Media->LogicalPartition = FALSE;
200 Media->ReadOnly = Private->WriteProtected;
201 Media->WriteCaching = FALSE;
202 Media->IoAlign = 1;
203 Media->LastBlock = 0; // Filled in by OpenDevice
204
205 // EFI_BLOCK_IO_PROTOCOL_REVISION2
206 Media->LowestAlignedLba = 0;
207 Media->LogicalBlocksPerPhysicalBlock = 0;
208
209
210 // EFI_BLOCK_IO_PROTOCOL_REVISION3
211 Media->OptimalTransferLengthGranularity = 0;
212
213 Status = EmuBlockIoOpenDevice (Private);
214
215
216 return Status;
217 }
218
219
220 EFI_STATUS
221 EmuBlockIoError (
222 IN EMU_BLOCK_IO_PRIVATE *Private
223 )
224 {
225 EFI_STATUS Status;
226 BOOLEAN ReinstallBlockIoFlag;
227
228
229 switch (errno) {
230
231 case EAGAIN:
232 Status = EFI_NO_MEDIA;
233 Private->Media->ReadOnly = FALSE;
234 Private->Media->MediaPresent = FALSE;
235 ReinstallBlockIoFlag = FALSE;
236 break;
237
238 case EACCES:
239 Private->Media->ReadOnly = FALSE;
240 Private->Media->MediaPresent = TRUE;
241 Private->Media->MediaId += 1;
242 ReinstallBlockIoFlag = TRUE;
243 Status = EFI_MEDIA_CHANGED;
244 break;
245
246 case EROFS:
247 Private->Media->ReadOnly = TRUE;
248 ReinstallBlockIoFlag = FALSE;
249 Status = EFI_WRITE_PROTECTED;
250 break;
251
252 default:
253 ReinstallBlockIoFlag = FALSE;
254 Status = EFI_DEVICE_ERROR;
255 break;
256 }
257 return Status;
258 }
259
260
261 EFI_STATUS
262 EmuBlockIoReadWriteCommon (
263 IN EMU_BLOCK_IO_PRIVATE *Private,
264 IN UINT32 MediaId,
265 IN EFI_LBA Lba,
266 IN UINTN BufferSize,
267 IN VOID *Buffer,
268 IN CHAR8 *CallerName
269 )
270 {
271 EFI_STATUS Status;
272 UINTN BlockSize;
273 UINT64 LastBlock;
274 INT64 DistanceToMove;
275 UINT64 DistanceMoved;
276
277 if (Private->fd < 0) {
278 Status = EmuBlockIoOpenDevice (Private);
279 if (EFI_ERROR (Status)) {
280 return Status;
281 }
282 }
283
284 if (!Private->Media->MediaPresent) {
285 DEBUG ((EFI_D_INIT, "%s: No Media\n", CallerName));
286 return EFI_NO_MEDIA;
287 }
288
289 if (Private->Media->MediaId != MediaId) {
290 return EFI_MEDIA_CHANGED;
291 }
292
293 if ((UINTN) Buffer % Private->Media->IoAlign != 0) {
294 return EFI_INVALID_PARAMETER;
295 }
296
297 //
298 // Verify buffer size
299 //
300 BlockSize = Private->Media->BlockSize;
301 if (BufferSize == 0) {
302 DEBUG ((EFI_D_INIT, "%s: Zero length read\n", CallerName));
303 return EFI_SUCCESS;
304 }
305
306 if ((BufferSize % BlockSize) != 0) {
307 DEBUG ((EFI_D_INIT, "%s: Invalid read size\n", CallerName));
308 return EFI_BAD_BUFFER_SIZE;
309 }
310
311 LastBlock = Lba + (BufferSize / BlockSize) - 1;
312 if (LastBlock > Private->Media->LastBlock) {
313 DEBUG ((EFI_D_INIT, "ReadBlocks: Attempted to read off end of device\n"));
314 return EFI_INVALID_PARAMETER;
315 }
316 //
317 // Seek to End of File
318 //
319 DistanceToMove = MultU64x32 (Lba, BlockSize);
320 Status = SetFilePointer64 (Private, DistanceToMove, &DistanceMoved, SEEK_SET);
321
322 if (EFI_ERROR (Status)) {
323 DEBUG ((EFI_D_INIT, "WriteBlocks: SetFilePointer failed\n"));
324 return EmuBlockIoError (Private);
325 }
326
327 return EFI_SUCCESS;
328 }
329
330
331 /**
332 Read BufferSize bytes from Lba into Buffer.
333
334 This function reads the requested number of blocks from the device. All the
335 blocks are read, or an error is returned.
336 If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_or EFI_MEDIA_CHANGED is returned and
337 non-blocking I/O is being used, the Event associated with this request will
338 not be signaled.
339
340 @param[in] This Indicates a pointer to the calling context.
341 @param[in] MediaId Id of the media, changes every time the media is
342 replaced.
343 @param[in] Lba The starting Logical Block Address to read from.
344 @param[in, out] Token A pointer to the token associated with the transaction.
345 @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
346 @param[out] Buffer A pointer to the destination buffer for the data. The
347 caller is responsible for either having implicit or
348 explicit ownership of the buffer.
349
350 @retval EFI_SUCCESS The read request was queued if Token->Event is
351 not NULL.The data was read correctly from the
352 device if the Token->Event is NULL.
353 @retval EFI_DEVICE_ERROR The device reported an error while performing
354 the read.
355 @retval EFI_NO_MEDIA There is no media in the device.
356 @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
357 @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the
358 intrinsic block size of the device.
359 @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
360 or the buffer is not on proper alignment.
361 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
362 of resources.
363 **/
364 EFI_STATUS
365 EmuBlockIoReadBlocks (
366 IN EMU_BLOCK_IO_PROTOCOL *This,
367 IN UINT32 MediaId,
368 IN EFI_LBA LBA,
369 IN OUT EFI_BLOCK_IO2_TOKEN *Token,
370 IN UINTN BufferSize,
371 OUT VOID *Buffer
372 )
373 {
374 EFI_STATUS Status;
375 EMU_BLOCK_IO_PRIVATE *Private;
376 ssize_t len;
377
378 Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This);
379
380 Status = EmuBlockIoReadWriteCommon (Private, MediaId, LBA, BufferSize, Buffer, "UnixReadBlocks");
381 if (EFI_ERROR (Status)) {
382 goto Done;
383 }
384
385 len = read (Private->fd, Buffer, BufferSize);
386 if (len != BufferSize) {
387 DEBUG ((EFI_D_INIT, "ReadBlocks: ReadFile failed.\n"));
388 Status = EmuBlockIoError (Private);
389 goto Done;
390 }
391
392 //
393 // If we read then media is present.
394 //
395 Private->Media->MediaPresent = TRUE;
396 Status = EFI_SUCCESS;
397
398 Done:
399 if (Token != NULL) {
400 if (Token->Event != NULL) {
401 // Caller is responsible for signaling EFI Event
402 Token->TransactionStatus = Status;
403 return EFI_SUCCESS;
404 }
405 }
406 return Status;
407 }
408
409
410 /**
411 Write BufferSize bytes from Lba into Buffer.
412
413 This function writes the requested number of blocks to the device. All blocks
414 are written, or an error is returned.If EFI_DEVICE_ERROR, EFI_NO_MEDIA,
415 EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED is returned and non-blocking I/O is
416 being used, the Event associated with this request will not be signaled.
417
418 @param[in] This Indicates a pointer to the calling context.
419 @param[in] MediaId The media ID that the write request is for.
420 @param[in] Lba The starting logical block address to be written. The
421 caller is responsible for writing to only legitimate
422 locations.
423 @param[in, out] Token A pointer to the token associated with the transaction.
424 @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
425 @param[in] Buffer A pointer to the source buffer for the data.
426
427 @retval EFI_SUCCESS The write request was queued if Event is not NULL.
428 The data was written correctly to the device if
429 the Event is NULL.
430 @retval EFI_WRITE_PROTECTED The device can not be written to.
431 @retval EFI_NO_MEDIA There is no media in the device.
432 @retval EFI_MEDIA_CHANGED The MediaId does not match the current device.
433 @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
434 @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
435 @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,
436 or the buffer is not on proper alignment.
437 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
438 of resources.
439
440 **/
441 EFI_STATUS
442 EmuBlockIoWriteBlocks (
443 IN EMU_BLOCK_IO_PROTOCOL *This,
444 IN UINT32 MediaId,
445 IN EFI_LBA LBA,
446 IN OUT EFI_BLOCK_IO2_TOKEN *Token,
447 IN UINTN BufferSize,
448 IN VOID *Buffer
449 )
450 {
451 EMU_BLOCK_IO_PRIVATE *Private;
452 ssize_t len;
453 EFI_STATUS Status;
454
455
456 Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This);
457
458 Status = EmuBlockIoReadWriteCommon (Private, MediaId, LBA, BufferSize, Buffer, "UnixWriteBlocks");
459 if (EFI_ERROR (Status)) {
460 goto Done;
461 }
462
463 len = write (Private->fd, Buffer, BufferSize);
464 if (len != BufferSize) {
465 DEBUG ((EFI_D_INIT, "ReadBlocks: WriteFile failed.\n"));
466 Status = EmuBlockIoError (Private);
467 goto Done;
468 }
469
470 //
471 // If the write succeeded, we are not write protected and media is present.
472 //
473 Private->Media->MediaPresent = TRUE;
474 Private->Media->ReadOnly = FALSE;
475 Status = EFI_SUCCESS;
476
477 Done:
478 if (Token != NULL) {
479 if (Token->Event != NULL) {
480 // Caller is responsible for signaling EFI Event
481 Token->TransactionStatus = Status;
482 return EFI_SUCCESS;
483 }
484 }
485
486 return Status;
487 }
488
489
490 /**
491 Flush the Block Device.
492
493 If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED
494 is returned and non-blocking I/O is being used, the Event associated with
495 this request will not be signaled.
496
497 @param[in] This Indicates a pointer to the calling context.
498 @param[in,out] Token A pointer to the token associated with the transaction
499
500 @retval EFI_SUCCESS The flush request was queued if Event is not NULL.
501 All outstanding data was written correctly to the
502 device if the Event is NULL.
503 @retval EFI_DEVICE_ERROR The device reported an error while writing back
504 the data.
505 @retval EFI_WRITE_PROTECTED The device cannot be written to.
506 @retval EFI_NO_MEDIA There is no media in the device.
507 @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
508 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
509 of resources.
510
511 **/
512 EFI_STATUS
513 EmuBlockIoFlushBlocks (
514 IN EMU_BLOCK_IO_PROTOCOL *This,
515 IN OUT EFI_BLOCK_IO2_TOKEN *Token
516 )
517 {
518 EMU_BLOCK_IO_PRIVATE *Private;
519
520 Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This);
521
522 if (Private->fd >= 0) {
523 fsync (Private->fd);
524 #if __APPLE__
525 fcntl (Private->fd, F_FULLFSYNC);
526 #endif
527 }
528
529
530 if (Token != NULL) {
531 if (Token->Event != NULL) {
532 // Caller is responsible for signaling EFI Event
533 Token->TransactionStatus = EFI_SUCCESS;
534 return EFI_SUCCESS;
535 }
536 }
537
538 return EFI_SUCCESS;
539 }
540
541
542 /**
543 Reset the block device hardware.
544
545 @param[in] This Indicates a pointer to the calling context.
546 @param[in] ExtendedVerification Indicates that the driver may perform a more
547 exhaustive verification operation of the device
548 during reset.
549
550 @retval EFI_SUCCESS The device was reset.
551 @retval EFI_DEVICE_ERROR The device is not functioning properly and could
552 not be reset.
553
554 **/
555 EFI_STATUS
556 EmuBlockIoReset (
557 IN EMU_BLOCK_IO_PROTOCOL *This,
558 IN BOOLEAN ExtendedVerification
559 )
560 {
561 EMU_BLOCK_IO_PRIVATE *Private;
562
563 Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This);
564
565 if (Private->fd >= 0) {
566 close (Private->fd);
567 Private->fd = -1;
568 }
569
570 return EFI_SUCCESS;
571 }
572
573
574 char *
575 StdDupUnicodeToAscii (
576 IN CHAR16 *Str
577 )
578 {
579 UINTN Size;
580 char *Ascii;
581 char *Ptr;
582
583 Size = StrLen (Str) + 1;
584 Ascii = malloc (Size);
585 if (Ascii == NULL) {
586 return NULL;
587 }
588
589 for (Ptr = Ascii; *Str != '\0'; Ptr++, Str++) {
590 *Ptr = *Str;
591 }
592 *Ptr = 0;
593
594 return Ascii;
595 }
596
597
598 EMU_BLOCK_IO_PROTOCOL gEmuBlockIoProtocol = {
599 GasketEmuBlockIoReset,
600 GasketEmuBlockIoReadBlocks,
601 GasketEmuBlockIoWriteBlocks,
602 GasketEmuBlockIoFlushBlocks,
603 GasketEmuBlockIoCreateMapping
604 };
605
606 EFI_STATUS
607 EmuBlockIoThunkOpen (
608 IN EMU_IO_THUNK_PROTOCOL *This
609 )
610 {
611 EMU_BLOCK_IO_PRIVATE *Private;
612 char *Str;
613
614 if (This->Private != NULL) {
615 return EFI_ALREADY_STARTED;
616 }
617
618 if (!CompareGuid (This->Protocol, &gEmuBlockIoProtocolGuid)) {
619 return EFI_UNSUPPORTED;
620 }
621
622 Private = malloc (sizeof (EMU_BLOCK_IO_PRIVATE));
623 if (Private == NULL) {
624 return EFI_OUT_OF_RESOURCES;
625 }
626
627
628 Private->Signature = EMU_BLOCK_IO_PRIVATE_SIGNATURE;
629 Private->Thunk = This;
630 CopyMem (&Private->EmuBlockIo, &gEmuBlockIoProtocol, sizeof (gEmuBlockIoProtocol));
631 Private->fd = -1;
632 Private->BlockSize = 512;
633
634 Private->Filename = StdDupUnicodeToAscii (This->ConfigString);
635 if (Private->Filename == NULL) {
636 return EFI_OUT_OF_RESOURCES;
637 }
638
639 Str = strstr (Private->Filename, ":");
640 if (Str == NULL) {
641 Private->RemovableMedia = FALSE;
642 Private->WriteProtected = FALSE;
643 } else {
644 for (*Str++ = '\0'; *Str != 0; Str++) {
645 if (*Str == 'R' || *Str == 'F') {
646 Private->RemovableMedia = (BOOLEAN) (*Str == 'R');
647 }
648 if (*Str == 'O' || *Str == 'W') {
649 Private->WriteProtected = (BOOLEAN) (*Str == 'O');
650 }
651 if (*Str == ':') {
652 Private->BlockSize = strtol (++Str, NULL, 0);
653 break;
654 }
655 }
656 }
657
658 Private->Mode = Private->WriteProtected ? O_RDONLY : O_RDWR;
659
660 This->Interface = &Private->EmuBlockIo;
661 This->Private = Private;
662 return EFI_SUCCESS;
663 }
664
665
666 EFI_STATUS
667 EmuBlockIoThunkClose (
668 IN EMU_IO_THUNK_PROTOCOL *This
669 )
670 {
671 EMU_BLOCK_IO_PRIVATE *Private;
672
673 if (!CompareGuid (This->Protocol, &gEmuBlockIoProtocolGuid)) {
674 return EFI_UNSUPPORTED;
675 }
676
677 Private = This->Private;
678
679 if (This->Private != NULL) {
680 if (Private->Filename != NULL) {
681 free (Private->Filename);
682 }
683 free (This->Private);
684 This->Private = NULL;
685 }
686
687 return EFI_SUCCESS;
688 }
689
690
691
692 EMU_IO_THUNK_PROTOCOL gBlockIoThunkIo = {
693 &gEmuBlockIoProtocolGuid,
694 NULL,
695 NULL,
696 0,
697 GasketBlockIoThunkOpen,
698 GasketBlockIoThunkClose,
699 NULL
700 };
701
702