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