]> git.proxmox.com Git - mirror_edk2.git/blob - InOsEmuPkg/Unix/Sec/BlockIo.c
InOsEmuPkg: Add support for mounting CD-ROM images.
[mirror_edk2.git] / InOsEmuPkg / Unix / Sec / 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 Private->Media->OptimalTransferLengthGranularity = buf.f_iosize/buf.f_bsize;
168 }
169 }
170
171 DEBUG ((EFI_D_INIT, "%HEmuOpenBlock: opened %a%N\n", Private->Filename));
172 Status = EFI_SUCCESS;
173
174 Done:
175 if (EFI_ERROR (Status)) {
176 if (Private->fd >= 0) {
177 EmuBlockIoReset (&Private->EmuBlockIo, FALSE);
178 }
179 }
180
181 return Status;
182 }
183
184
185 EFI_STATUS
186 EmuBlockIoCreateMapping (
187 IN EMU_BLOCK_IO_PROTOCOL *This,
188 IN EFI_BLOCK_IO_MEDIA *Media
189 )
190 {
191 EFI_STATUS Status;
192 EMU_BLOCK_IO_PRIVATE *Private;
193
194 Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This);
195
196 Private->Media = Media;
197
198 Media->MediaId = 0;
199 Media->RemovableMedia = Private->RemovableMedia;
200 Media->MediaPresent = TRUE;
201 Media->LogicalPartition = FALSE;
202 Media->ReadOnly = Private->WriteProtected;
203 Media->WriteCaching = FALSE;
204 Media->IoAlign = 1;
205 Media->LastBlock = 0; // Filled in by OpenDevice
206
207 // EFI_BLOCK_IO_PROTOCOL_REVISION2
208 Media->LowestAlignedLba = 0;
209 Media->LogicalBlocksPerPhysicalBlock = 0;
210
211
212 // EFI_BLOCK_IO_PROTOCOL_REVISION3
213 Media->OptimalTransferLengthGranularity = 0;
214
215 Status = EmuBlockIoOpenDevice (Private);
216
217
218 return Status;
219 }
220
221
222 EFI_STATUS
223 EmuBlockIoError (
224 IN EMU_BLOCK_IO_PRIVATE *Private
225 )
226 {
227 EFI_STATUS Status;
228 BOOLEAN ReinstallBlockIoFlag;
229
230
231 switch (errno) {
232
233 case EAGAIN:
234 Status = EFI_NO_MEDIA;
235 Private->Media->ReadOnly = FALSE;
236 Private->Media->MediaPresent = FALSE;
237 ReinstallBlockIoFlag = FALSE;
238 break;
239
240 case EACCES:
241 Private->Media->ReadOnly = FALSE;
242 Private->Media->MediaPresent = TRUE;
243 Private->Media->MediaId += 1;
244 ReinstallBlockIoFlag = TRUE;
245 Status = EFI_MEDIA_CHANGED;
246 break;
247
248 case EROFS:
249 Private->Media->ReadOnly = TRUE;
250 ReinstallBlockIoFlag = FALSE;
251 Status = EFI_WRITE_PROTECTED;
252 break;
253
254 default:
255 ReinstallBlockIoFlag = FALSE;
256 Status = EFI_DEVICE_ERROR;
257 break;
258 }
259 return Status;
260 }
261
262
263 EFI_STATUS
264 EmuBlockIoReadWriteCommon (
265 IN EMU_BLOCK_IO_PRIVATE *Private,
266 IN UINT32 MediaId,
267 IN EFI_LBA Lba,
268 IN UINTN BufferSize,
269 IN VOID *Buffer,
270 IN CHAR8 *CallerName
271 )
272 {
273 EFI_STATUS Status;
274 UINTN BlockSize;
275 UINT64 LastBlock;
276 INT64 DistanceToMove;
277 UINT64 DistanceMoved;
278
279 if (Private->fd < 0) {
280 Status = EmuBlockIoOpenDevice (Private);
281 if (EFI_ERROR (Status)) {
282 return Status;
283 }
284 }
285
286 if (!Private->Media->MediaPresent) {
287 DEBUG ((EFI_D_INIT, "%s: No Media\n", CallerName));
288 return EFI_NO_MEDIA;
289 }
290
291 if (Private->Media->MediaId != MediaId) {
292 return EFI_MEDIA_CHANGED;
293 }
294
295 if ((UINTN) Buffer % Private->Media->IoAlign != 0) {
296 return EFI_INVALID_PARAMETER;
297 }
298
299 //
300 // Verify buffer size
301 //
302 BlockSize = Private->Media->BlockSize;
303 if (BufferSize == 0) {
304 DEBUG ((EFI_D_INIT, "%s: Zero length read\n", CallerName));
305 return EFI_SUCCESS;
306 }
307
308 if ((BufferSize % BlockSize) != 0) {
309 DEBUG ((EFI_D_INIT, "%s: Invalid read size\n", CallerName));
310 return EFI_BAD_BUFFER_SIZE;
311 }
312
313 LastBlock = Lba + (BufferSize / BlockSize) - 1;
314 if (LastBlock > Private->Media->LastBlock) {
315 DEBUG ((EFI_D_INIT, "ReadBlocks: Attempted to read off end of device\n"));
316 return EFI_INVALID_PARAMETER;
317 }
318 //
319 // Seek to End of File
320 //
321 DistanceToMove = MultU64x32 (Lba, BlockSize);
322 Status = SetFilePointer64 (Private, DistanceToMove, &DistanceMoved, SEEK_SET);
323
324 if (EFI_ERROR (Status)) {
325 DEBUG ((EFI_D_INIT, "WriteBlocks: SetFilePointer failed\n"));
326 return EmuBlockIoError (Private);
327 }
328
329 return EFI_SUCCESS;
330 }
331
332
333 /**
334 Read BufferSize bytes from Lba into Buffer.
335
336 This function reads the requested number of blocks from the device. All the
337 blocks are read, or an error is returned.
338 If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_or EFI_MEDIA_CHANGED is returned and
339 non-blocking I/O is being used, the Event associated with this request will
340 not be signaled.
341
342 @param[in] This Indicates a pointer to the calling context.
343 @param[in] MediaId Id of the media, changes every time the media is
344 replaced.
345 @param[in] Lba The starting Logical Block Address to read from.
346 @param[in, out] Token A pointer to the token associated with the transaction.
347 @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
348 @param[out] Buffer A pointer to the destination buffer for the data. The
349 caller is responsible for either having implicit or
350 explicit ownership of the buffer.
351
352 @retval EFI_SUCCESS The read request was queued if Token->Event is
353 not NULL.The data was read correctly from the
354 device if the Token->Event is NULL.
355 @retval EFI_DEVICE_ERROR The device reported an error while performing
356 the read.
357 @retval EFI_NO_MEDIA There is no media in the device.
358 @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
359 @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of the
360 intrinsic block size of the device.
361 @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
362 or the buffer is not on proper alignment.
363 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
364 of resources.
365 **/
366 EFI_STATUS
367 EmuBlockIoReadBlocks (
368 IN EMU_BLOCK_IO_PROTOCOL *This,
369 IN UINT32 MediaId,
370 IN EFI_LBA LBA,
371 IN OUT EFI_BLOCK_IO2_TOKEN *Token,
372 IN UINTN BufferSize,
373 OUT VOID *Buffer
374 )
375 {
376 EFI_STATUS Status;
377 EMU_BLOCK_IO_PRIVATE *Private;
378 ssize_t len;
379
380 Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This);
381
382 Status = EmuBlockIoReadWriteCommon (Private, MediaId, LBA, BufferSize, Buffer, "UnixReadBlocks");
383 if (EFI_ERROR (Status)) {
384 goto Done;
385 }
386
387 len = read (Private->fd, Buffer, BufferSize);
388 if (len != BufferSize) {
389 DEBUG ((EFI_D_INIT, "ReadBlocks: ReadFile failed.\n"));
390 Status = EmuBlockIoError (Private);
391 goto Done;
392 }
393
394 //
395 // If we read then media is present.
396 //
397 Private->Media->MediaPresent = TRUE;
398 Status = EFI_SUCCESS;
399
400 Done:
401 if (Token != NULL) {
402 if (Token->Event != NULL) {
403 // Caller is responcible for signaling EFI Event
404 Token->TransactionStatus = Status;
405 return EFI_SUCCESS;
406 }
407 }
408 return Status;
409 }
410
411
412 /**
413 Write BufferSize bytes from Lba into Buffer.
414
415 This function writes the requested number of blocks to the device. All blocks
416 are written, or an error is returned.If EFI_DEVICE_ERROR, EFI_NO_MEDIA,
417 EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED is returned and non-blocking I/O is
418 being used, the Event associated with this request will not be signaled.
419
420 @param[in] This Indicates a pointer to the calling context.
421 @param[in] MediaId The media ID that the write request is for.
422 @param[in] Lba The starting logical block address to be written. The
423 caller is responsible for writing to only legitimate
424 locations.
425 @param[in, out] Token A pointer to the token associated with the transaction.
426 @param[in] BufferSize Size of Buffer, must be a multiple of device block size.
427 @param[in] Buffer A pointer to the source buffer for the data.
428
429 @retval EFI_SUCCESS The write request was queued if Event is not NULL.
430 The data was written correctly to the device if
431 the Event is NULL.
432 @retval EFI_WRITE_PROTECTED The device can not be written to.
433 @retval EFI_NO_MEDIA There is no media in the device.
434 @retval EFI_MEDIA_CHNAGED The MediaId does not matched the current device.
435 @retval EFI_DEVICE_ERROR The device reported an error while performing the write.
436 @retval EFI_BAD_BUFFER_SIZE The Buffer was not a multiple of the block size of the device.
437 @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,
438 or the buffer is not on proper alignment.
439 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
440 of resources.
441
442 **/
443 EFI_STATUS
444 EmuBlockIoWriteBlocks (
445 IN EMU_BLOCK_IO_PROTOCOL *This,
446 IN UINT32 MediaId,
447 IN EFI_LBA LBA,
448 IN OUT EFI_BLOCK_IO2_TOKEN *Token,
449 IN UINTN BufferSize,
450 IN VOID *Buffer
451 )
452 {
453 EMU_BLOCK_IO_PRIVATE *Private;
454 ssize_t len;
455 EFI_STATUS Status;
456
457
458 Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This);
459
460 Status = EmuBlockIoReadWriteCommon (Private, MediaId, LBA, BufferSize, Buffer, "UnixWriteBlocks");
461 if (EFI_ERROR (Status)) {
462 goto Done;
463 }
464
465 len = write (Private->fd, Buffer, BufferSize);
466 if (len != BufferSize) {
467 DEBUG ((EFI_D_INIT, "ReadBlocks: WriteFile failed.\n"));
468 Status = EmuBlockIoError (Private);
469 goto Done;
470 }
471
472 //
473 // If the write succeeded, we are not write protected and media is present.
474 //
475 Private->Media->MediaPresent = TRUE;
476 Private->Media->ReadOnly = FALSE;
477 Status = EFI_SUCCESS;
478
479 Done:
480 if (Token != NULL) {
481 if (Token->Event != NULL) {
482 // Caller is responcible for signaling EFI Event
483 Token->TransactionStatus = Status;
484 return EFI_SUCCESS;
485 }
486 }
487
488 return Status;
489 }
490
491
492 /**
493 Flush the Block Device.
494
495 If EFI_DEVICE_ERROR, EFI_NO_MEDIA,_EFI_WRITE_PROTECTED or EFI_MEDIA_CHANGED
496 is returned and non-blocking I/O is being used, the Event associated with
497 this request will not be signaled.
498
499 @param[in] This Indicates a pointer to the calling context.
500 @param[in,out] Token A pointer to the token associated with the transaction
501
502 @retval EFI_SUCCESS The flush request was queued if Event is not NULL.
503 All outstanding data was written correctly to the
504 device if the Event is NULL.
505 @retval EFI_DEVICE_ERROR The device reported an error while writting back
506 the data.
507 @retval EFI_WRITE_PROTECTED The device cannot be written to.
508 @retval EFI_NO_MEDIA There is no media in the device.
509 @retval EFI_MEDIA_CHANGED The MediaId is not for the current media.
510 @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack
511 of resources.
512
513 **/
514 EFI_STATUS
515 EmuBlockIoFlushBlocks (
516 IN EMU_BLOCK_IO_PROTOCOL *This,
517 IN OUT EFI_BLOCK_IO2_TOKEN *Token
518 )
519 {
520 EMU_BLOCK_IO_PRIVATE *Private;
521 int Res;
522
523 Private = EMU_BLOCK_IO_PRIVATE_DATA_FROM_THIS (This);
524
525 if (Private->fd >= 0) {
526 Res = fcntl (Private->fd, F_FULLFSYNC);
527 }
528
529
530 if (Token != NULL) {
531 if (Token->Event != NULL) {
532 // Caller is responcible 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 exhausive verfication 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 This->Interface = &Private->EmuBlockIo;
659 This->Private = Private;
660 return EFI_SUCCESS;
661 }
662
663
664 EFI_STATUS
665 EmuBlockIoThunkClose (
666 IN EMU_IO_THUNK_PROTOCOL *This
667 )
668 {
669 EMU_BLOCK_IO_PRIVATE *Private;
670
671 if (!CompareGuid (This->Protocol, &gEmuBlockIoProtocolGuid)) {
672 return EFI_UNSUPPORTED;
673 }
674
675 Private = This->Private;
676
677 if (This->Private != NULL) {
678 if (Private->Filename != NULL) {
679 free (Private->Filename);
680 }
681 free (This->Private);
682 This->Private = NULL;
683 }
684
685 return EFI_SUCCESS;
686 }
687
688
689
690 EMU_IO_THUNK_PROTOCOL gBlockIoThunkIo = {
691 &gEmuBlockIoProtocolGuid,
692 NULL,
693 NULL,
694 0,
695 GasketBlockIoThunkOpen,
696 GasketBlockIoThunkClose,
697 NULL
698 };
699
700