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