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