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