]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Bus/Usb/UsbBotPei/UsbBotPeim.c
MdeModulePkg/UsbBotPei: Correct wrong media type detection logic in UsbBotPei
[mirror_edk2.git] / MdeModulePkg / Bus / Usb / UsbBotPei / UsbBotPeim.c
1 /** @file
2
3 Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
4
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions
7 of the BSD License which accompanies this distribution. The
8 full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16 #include "UsbBotPeim.h"
17 #include "BotPeim.h"
18
19 //
20 // Global function
21 //
22 EFI_PEI_NOTIFY_DESCRIPTOR mNotifyList = {
23 EFI_PEI_PPI_DESCRIPTOR_NOTIFY_DISPATCH | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
24 &gPeiUsbIoPpiGuid,
25 NotifyOnUsbIoPpi
26 };
27
28 EFI_PEI_RECOVERY_BLOCK_IO_PPI mRecoveryBlkIoPpi = {
29 BotGetNumberOfBlockDevices,
30 BotGetMediaInfo,
31 BotReadBlocks
32 };
33
34 EFI_PEI_PPI_DESCRIPTOR mPpiList = {
35 EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,
36 &gEfiPeiVirtualBlockIoPpiGuid,
37 NULL
38 };
39
40 /**
41 Detect whether the removable media is present and whether it has changed.
42
43 @param[in] PeiServices General-purpose services that are available to every
44 PEIM.
45 @param[in] PeiBotDev Indicates the PEI_BOT_DEVICE instance.
46
47 @retval EFI_SUCCESS The media status is successfully checked.
48 @retval Other Failed to detect media.
49
50 **/
51 EFI_STATUS
52 PeiBotDetectMedia (
53 IN EFI_PEI_SERVICES **PeiServices,
54 IN PEI_BOT_DEVICE *PeiBotDev
55 );
56
57 /**
58 Initializes the Usb Bot.
59
60 @param FileHandle Handle of the file being invoked.
61 @param PeiServices Describes the list of possible PEI Services.
62
63 @retval EFI_SUCCESS Usb bot driver is successfully initialized.
64 @retval EFI_OUT_OF_RESOURCES Can't initialize the driver.
65
66 **/
67 EFI_STATUS
68 EFIAPI
69 PeimInitializeUsbBot (
70 IN EFI_PEI_FILE_HANDLE FileHandle,
71 IN CONST EFI_PEI_SERVICES **PeiServices
72 )
73 {
74 EFI_STATUS Status;
75 UINTN UsbIoPpiInstance;
76 EFI_PEI_PPI_DESCRIPTOR *TempPpiDescriptor;
77 PEI_USB_IO_PPI *UsbIoPpi;
78
79 //
80 // Shadow this PEIM to run from memory
81 //
82 if (!EFI_ERROR (PeiServicesRegisterForShadow (FileHandle))) {
83 return EFI_SUCCESS;
84 }
85
86 //
87 // locate all usb io PPIs
88 //
89 for (UsbIoPpiInstance = 0; UsbIoPpiInstance < PEI_FAT_MAX_USB_IO_PPI; UsbIoPpiInstance++) {
90
91 Status = PeiServicesLocatePpi (
92 &gPeiUsbIoPpiGuid,
93 UsbIoPpiInstance,
94 &TempPpiDescriptor,
95 (VOID **) &UsbIoPpi
96 );
97 if (EFI_ERROR (Status)) {
98 break;
99 }
100 }
101 //
102 // Register a notify function
103 //
104 return PeiServicesNotifyPpi (&mNotifyList);
105 }
106
107 /**
108 UsbIo installation notification function.
109
110 This function finds out all the current USB IO PPIs in the system and add them
111 into private data.
112
113 @param PeiServices Indirect reference to the PEI Services Table.
114 @param NotifyDesc Address of the notification descriptor data structure.
115 @param InvokePpi Address of the PPI that was invoked.
116
117 @retval EFI_SUCCESS The function completes successfully.
118
119 **/
120 EFI_STATUS
121 EFIAPI
122 NotifyOnUsbIoPpi (
123 IN EFI_PEI_SERVICES **PeiServices,
124 IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDesc,
125 IN VOID *InvokePpi
126 )
127 {
128 PEI_USB_IO_PPI *UsbIoPpi;
129
130 UsbIoPpi = (PEI_USB_IO_PPI *) InvokePpi;
131
132 InitUsbBot (PeiServices, UsbIoPpi);
133
134 return EFI_SUCCESS;
135 }
136
137 /**
138 Initialize the usb bot device.
139
140 @param[in] PeiServices General-purpose services that are available to every
141 PEIM.
142 @param[in] UsbIoPpi Indicates the PEI_USB_IO_PPI instance.
143
144 @retval EFI_SUCCESS The usb bot device is initialized successfully.
145 @retval Other Failed to initialize media.
146
147 **/
148 EFI_STATUS
149 InitUsbBot (
150 IN EFI_PEI_SERVICES **PeiServices,
151 IN PEI_USB_IO_PPI *UsbIoPpi
152 )
153 {
154 PEI_BOT_DEVICE *PeiBotDevice;
155 EFI_STATUS Status;
156 EFI_USB_INTERFACE_DESCRIPTOR *InterfaceDesc;
157 UINTN MemPages;
158 EFI_PHYSICAL_ADDRESS AllocateAddress;
159 EFI_USB_ENDPOINT_DESCRIPTOR *EndpointDesc;
160 UINT8 Index;
161
162 //
163 // Check its interface
164 //
165 Status = UsbIoPpi->UsbGetInterfaceDescriptor (
166 PeiServices,
167 UsbIoPpi,
168 &InterfaceDesc
169 );
170 if (EFI_ERROR (Status)) {
171 return Status;
172 }
173 //
174 // Check if it is the BOT device we support
175 //
176 if ((InterfaceDesc->InterfaceClass != 0x08) || (InterfaceDesc->InterfaceProtocol != 0x50)) {
177
178 return EFI_NOT_FOUND;
179 }
180
181 MemPages = sizeof (PEI_BOT_DEVICE) / EFI_PAGE_SIZE + 1;
182 Status = PeiServicesAllocatePages (
183 EfiBootServicesCode,
184 MemPages,
185 &AllocateAddress
186 );
187 if (EFI_ERROR (Status)) {
188 return Status;
189 }
190
191 PeiBotDevice = (PEI_BOT_DEVICE *) ((UINTN) AllocateAddress);
192
193 PeiBotDevice->Signature = PEI_BOT_DEVICE_SIGNATURE;
194 PeiBotDevice->UsbIoPpi = UsbIoPpi;
195 PeiBotDevice->AllocateAddress = (UINTN) AllocateAddress;
196 PeiBotDevice->BotInterface = InterfaceDesc;
197
198 //
199 // Default value
200 //
201 PeiBotDevice->Media.DeviceType = UsbMassStorage;
202 PeiBotDevice->Media.BlockSize = 0x200;
203
204 //
205 // Check its Bulk-in/Bulk-out endpoint
206 //
207 for (Index = 0; Index < 2; Index++) {
208 Status = UsbIoPpi->UsbGetEndpointDescriptor (
209 PeiServices,
210 UsbIoPpi,
211 Index,
212 &EndpointDesc
213 );
214
215 if (EFI_ERROR (Status)) {
216 return Status;
217 }
218
219 if ((EndpointDesc->EndpointAddress & 0x80) != 0) {
220 PeiBotDevice->BulkInEndpoint = EndpointDesc;
221 } else {
222 PeiBotDevice->BulkOutEndpoint = EndpointDesc;
223 }
224 }
225
226 CopyMem (
227 &(PeiBotDevice->BlkIoPpi),
228 &mRecoveryBlkIoPpi,
229 sizeof (EFI_PEI_RECOVERY_BLOCK_IO_PPI)
230 );
231 CopyMem (
232 &(PeiBotDevice->BlkIoPpiList),
233 &mPpiList,
234 sizeof (EFI_PEI_PPI_DESCRIPTOR)
235 );
236 PeiBotDevice->BlkIoPpiList.Ppi = &PeiBotDevice->BlkIoPpi;
237
238 Status = PeiUsbInquiry (PeiServices, PeiBotDevice);
239 if (EFI_ERROR (Status)) {
240 return Status;
241 }
242
243 Status = PeiServicesAllocatePages (
244 EfiBootServicesCode,
245 1,
246 &AllocateAddress
247 );
248 if (EFI_ERROR (Status)) {
249 return Status;
250 }
251
252 PeiBotDevice->SensePtr = (ATAPI_REQUEST_SENSE_DATA *) ((UINTN) AllocateAddress);
253
254 Status = PeiServicesInstallPpi (&PeiBotDevice->BlkIoPpiList);
255
256 if (EFI_ERROR (Status)) {
257 return Status;
258 }
259
260 return EFI_SUCCESS;
261 }
262
263 /**
264 Gets the count of block I/O devices that one specific block driver detects.
265
266 This function is used for getting the count of block I/O devices that one
267 specific block driver detects. To the PEI ATAPI driver, it returns the number
268 of all the detected ATAPI devices it detects during the enumeration process.
269 To the PEI legacy floppy driver, it returns the number of all the legacy
270 devices it finds during its enumeration process. If no device is detected,
271 then the function will return zero.
272
273 @param[in] PeiServices General-purpose services that are available
274 to every PEIM.
275 @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI
276 instance.
277 @param[out] NumberBlockDevices The number of block I/O devices discovered.
278
279 @retval EFI_SUCCESS Operation performed successfully.
280
281 **/
282 EFI_STATUS
283 EFIAPI
284 BotGetNumberOfBlockDevices (
285 IN EFI_PEI_SERVICES **PeiServices,
286 IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
287 OUT UINTN *NumberBlockDevices
288 )
289 {
290 //
291 // For Usb devices, this value should be always 1
292 //
293 *NumberBlockDevices = 1;
294 return EFI_SUCCESS;
295 }
296
297 /**
298 Gets a block device's media information.
299
300 This function will provide the caller with the specified block device's media
301 information. If the media changes, calling this function will update the media
302 information accordingly.
303
304 @param[in] PeiServices General-purpose services that are available to every
305 PEIM
306 @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
307 @param[in] DeviceIndex Specifies the block device to which the function wants
308 to talk. Because the driver that implements Block I/O
309 PPIs will manage multiple block devices, the PPIs that
310 want to talk to a single device must specify the
311 device index that was assigned during the enumeration
312 process. This index is a number from one to
313 NumberBlockDevices.
314 @param[out] MediaInfo The media information of the specified block media.
315 The caller is responsible for the ownership of this
316 data structure.
317
318 @retval EFI_SUCCESS Media information about the specified block device
319 was obtained successfully.
320 @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware
321 error.
322
323 **/
324 EFI_STATUS
325 EFIAPI
326 BotGetMediaInfo (
327 IN EFI_PEI_SERVICES **PeiServices,
328 IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
329 IN UINTN DeviceIndex,
330 OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
331 )
332 {
333 PEI_BOT_DEVICE *PeiBotDev;
334 EFI_STATUS Status;
335
336 PeiBotDev = PEI_BOT_DEVICE_FROM_THIS (This);
337
338 //
339 // First test unit ready
340 //
341 PeiUsbTestUnitReady (
342 PeiServices,
343 PeiBotDev
344 );
345
346 Status = PeiBotDetectMedia (
347 PeiServices,
348 PeiBotDev
349 );
350
351 if (EFI_ERROR (Status)) {
352 return EFI_DEVICE_ERROR;
353 }
354
355 CopyMem (
356 MediaInfo,
357 &(PeiBotDev->Media),
358 sizeof (EFI_PEI_BLOCK_IO_MEDIA)
359 );
360
361 return EFI_SUCCESS;
362 }
363
364 /**
365 Reads the requested number of blocks from the specified block device.
366
367 The function reads the requested number of blocks from the device. All the
368 blocks are read, or an error is returned. If there is no media in the device,
369 the function returns EFI_NO_MEDIA.
370
371 @param[in] PeiServices General-purpose services that are available to
372 every PEIM.
373 @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
374 @param[in] DeviceIndex Specifies the block device to which the function wants
375 to talk. Because the driver that implements Block I/O
376 PPIs will manage multiple block devices, the PPIs that
377 want to talk to a single device must specify the device
378 index that was assigned during the enumeration process.
379 This index is a number from one to NumberBlockDevices.
380 @param[in] StartLBA The starting logical block address (LBA) to read from
381 on the device
382 @param[in] BufferSize The size of the Buffer in bytes. This number must be
383 a multiple of the intrinsic block size of the device.
384 @param[out] Buffer A pointer to the destination buffer for the data.
385 The caller is responsible for the ownership of the
386 buffer.
387
388 @retval EFI_SUCCESS The data was read correctly from the device.
389 @retval EFI_DEVICE_ERROR The device reported an error while attempting
390 to perform the read operation.
391 @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not
392 valid, or the buffer is not properly aligned.
393 @retval EFI_NO_MEDIA There is no media in the device.
394 @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of
395 the intrinsic block size of the device.
396
397 **/
398 EFI_STATUS
399 EFIAPI
400 BotReadBlocks (
401 IN EFI_PEI_SERVICES **PeiServices,
402 IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
403 IN UINTN DeviceIndex,
404 IN EFI_PEI_LBA StartLBA,
405 IN UINTN BufferSize,
406 OUT VOID *Buffer
407 )
408 {
409 PEI_BOT_DEVICE *PeiBotDev;
410 EFI_STATUS Status;
411 UINTN BlockSize;
412 UINTN NumberOfBlocks;
413
414 Status = EFI_SUCCESS;
415 PeiBotDev = PEI_BOT_DEVICE_FROM_THIS (This);
416
417 //
418 // Check parameters
419 //
420 if (Buffer == NULL) {
421 return EFI_INVALID_PARAMETER;
422 }
423
424 if (BufferSize == 0) {
425 return EFI_SUCCESS;
426 }
427
428 if (!PeiBotDev->Media.MediaPresent) {
429 return EFI_NO_MEDIA;
430 }
431
432 BlockSize = PeiBotDev->Media.BlockSize;
433
434 if (BufferSize % BlockSize != 0) {
435 Status = EFI_BAD_BUFFER_SIZE;
436 }
437
438 if (StartLBA > PeiBotDev->Media.LastBlock) {
439 Status = EFI_INVALID_PARAMETER;
440 }
441
442 NumberOfBlocks = BufferSize / (PeiBotDev->Media.BlockSize);
443
444 if (Status == EFI_SUCCESS) {
445
446 Status = PeiUsbTestUnitReady (
447 PeiServices,
448 PeiBotDev
449 );
450 if (Status == EFI_SUCCESS) {
451 Status = PeiUsbRead10 (
452 PeiServices,
453 PeiBotDev,
454 Buffer,
455 StartLBA,
456 1
457 );
458 }
459 } else {
460 //
461 // To generate sense data for DetectMedia use.
462 //
463 PeiUsbTestUnitReady (
464 PeiServices,
465 PeiBotDev
466 );
467 }
468
469 if (EFI_ERROR (Status)) {
470 //
471 // if any error encountered, detect what happened to the media and
472 // update the media info accordingly.
473 //
474 Status = PeiBotDetectMedia (
475 PeiServices,
476 PeiBotDev
477 );
478 if (Status != EFI_SUCCESS) {
479 return EFI_DEVICE_ERROR;
480 }
481
482 NumberOfBlocks = BufferSize / PeiBotDev->Media.BlockSize;
483
484 if (!(PeiBotDev->Media.MediaPresent)) {
485 return EFI_NO_MEDIA;
486 }
487
488 if (BufferSize % (PeiBotDev->Media.BlockSize) != 0) {
489 return EFI_BAD_BUFFER_SIZE;
490 }
491
492 if (StartLBA > PeiBotDev->Media.LastBlock) {
493 return EFI_INVALID_PARAMETER;
494 }
495
496 if ((StartLBA + NumberOfBlocks - 1) > PeiBotDev->Media.LastBlock) {
497 return EFI_INVALID_PARAMETER;
498 }
499
500 Status = PeiUsbRead10 (
501 PeiServices,
502 PeiBotDev,
503 Buffer,
504 StartLBA,
505 NumberOfBlocks
506 );
507
508 switch (Status) {
509
510 case EFI_SUCCESS:
511 return EFI_SUCCESS;
512
513 default:
514 return EFI_DEVICE_ERROR;
515 }
516 } else {
517 StartLBA += 1;
518 NumberOfBlocks -= 1;
519 Buffer = (UINT8 *) Buffer + PeiBotDev->Media.BlockSize;
520
521 if (NumberOfBlocks == 0) {
522 return EFI_SUCCESS;
523 }
524
525 Status = PeiUsbRead10 (
526 PeiServices,
527 PeiBotDev,
528 Buffer,
529 StartLBA,
530 NumberOfBlocks
531 );
532 switch (Status) {
533
534 case EFI_SUCCESS:
535 return EFI_SUCCESS;
536
537 default:
538 return EFI_DEVICE_ERROR;
539
540 }
541 }
542 }
543
544 /**
545 Detect whether the removable media is present and whether it has changed.
546
547 @param[in] PeiServices General-purpose services that are available to every
548 PEIM.
549 @param[in] PeiBotDev Indicates the PEI_BOT_DEVICE instance.
550
551 @retval EFI_SUCCESS The media status is successfully checked.
552 @retval Other Failed to detect media.
553
554 **/
555 EFI_STATUS
556 PeiBotDetectMedia (
557 IN EFI_PEI_SERVICES **PeiServices,
558 IN PEI_BOT_DEVICE *PeiBotDev
559 )
560 {
561 EFI_STATUS Status;
562 EFI_STATUS FloppyStatus;
563 UINTN SenseCounts;
564 BOOLEAN NeedReadCapacity;
565 EFI_PHYSICAL_ADDRESS AllocateAddress;
566 ATAPI_REQUEST_SENSE_DATA *SensePtr;
567 UINTN Retry;
568
569 //
570 // if there is no media present,or media not changed,
571 // the request sense command will detect faster than read capacity command.
572 // read capacity command can be bypassed, thus improve performance.
573 //
574 SenseCounts = 0;
575 NeedReadCapacity = TRUE;
576
577 Status = PeiServicesAllocatePages (
578 EfiBootServicesCode,
579 1,
580 &AllocateAddress
581 );
582 if (EFI_ERROR (Status)) {
583 return Status;
584 }
585
586 SensePtr = PeiBotDev->SensePtr;
587 ZeroMem (SensePtr, EFI_PAGE_SIZE);
588
589 Status = PeiUsbRequestSense (
590 PeiServices,
591 PeiBotDev,
592 &SenseCounts,
593 (UINT8 *) SensePtr
594 );
595
596 if (Status == EFI_SUCCESS) {
597 //
598 // No Media
599 //
600 if (IsNoMedia (SensePtr, SenseCounts)) {
601 NeedReadCapacity = FALSE;
602 PeiBotDev->Media.MediaPresent = FALSE;
603 PeiBotDev->Media.LastBlock = 0;
604 } else {
605 //
606 // Media Changed
607 //
608 if (IsMediaChange (SensePtr, SenseCounts)) {
609 PeiBotDev->Media.MediaPresent = TRUE;
610 }
611 //
612 // Media Error
613 //
614 if (IsMediaError (SensePtr, SenseCounts)) {
615 //
616 // if media error encountered, make it look like no media present.
617 //
618 PeiBotDev->Media.MediaPresent = FALSE;
619 PeiBotDev->Media.LastBlock = 0;
620 }
621
622 }
623
624 }
625
626 if (NeedReadCapacity) {
627 //
628 // Retry at most 4 times to detect media info
629 //
630 for (Retry = 0; Retry < 4; Retry++) {
631 switch (PeiBotDev->DeviceType) {
632 case USBCDROM:
633 Status = PeiUsbReadCapacity (
634 PeiServices,
635 PeiBotDev
636 );
637 break;
638
639 case USBFLOPPY2:
640 Status = PeiUsbReadFormattedCapacity (
641 PeiServices,
642 PeiBotDev
643 );
644 if (EFI_ERROR(Status)||
645 !PeiBotDev->Media.MediaPresent) {
646 //
647 // retry the ReadCapacity command
648 //
649 PeiBotDev->DeviceType = USBFLOPPY;
650 Status = EFI_DEVICE_ERROR;
651 }
652 break;
653
654 case USBFLOPPY:
655 Status = PeiUsbReadCapacity (
656 PeiServices,
657 PeiBotDev
658 );
659 if (EFI_ERROR (Status)) {
660 //
661 // retry the ReadFormatCapacity command
662 //
663 PeiBotDev->DeviceType = USBFLOPPY2;
664 }
665 break;
666
667 default:
668 return EFI_INVALID_PARAMETER;
669 }
670
671 SenseCounts = 0;
672 ZeroMem (SensePtr, EFI_PAGE_SIZE);
673
674 if (Status == EFI_SUCCESS) {
675 break;
676 }
677
678 FloppyStatus = PeiUsbRequestSense (
679 PeiServices,
680 PeiBotDev,
681 &SenseCounts,
682 (UINT8 *) SensePtr
683 );
684
685 //
686 // If Request Sense data failed,retry.
687 //
688 if (EFI_ERROR (FloppyStatus)) {
689 continue;
690 }
691 //
692 // No Media
693 //
694 if (IsNoMedia (SensePtr, SenseCounts)) {
695 PeiBotDev->Media.MediaPresent = FALSE;
696 PeiBotDev->Media.LastBlock = 0;
697 break;
698 }
699
700 if (IsMediaError (SensePtr, SenseCounts)) {
701 //
702 // if media error encountered, make it look like no media present.
703 //
704 PeiBotDev->Media.MediaPresent = FALSE;
705 PeiBotDev->Media.LastBlock = 0;
706 break;
707 }
708 }
709 //
710 // ENDFOR
711 //
712 // tell whether the readcapacity process is successful or not
713 // ("Status" variable record the latest status returned
714 // by ReadCapacity )
715 //
716 if (Status != EFI_SUCCESS) {
717 return EFI_DEVICE_ERROR;
718 }
719 }
720
721 return EFI_SUCCESS;
722 }