1 /** @file
2 Pei USB ATATPI command implementations.
3 
4 Copyright (c) 1999 - 2015, Intel Corporation. All rights reserved.<BR>
5 
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions
8 of the BSD License which accompanies this distribution.  The
9 full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11 
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14 
15 **/
16 
17 #include "UsbBotPeim.h"
18 #include "BotPeim.h"
19 
20 #define MAXSENSEKEY 5
21 
22 /**
23   Sends out ATAPI Inquiry Packet Command to the specified device. This command will
24   return INQUIRY data of the device.
25 
26   @param PeiServices    The pointer of EFI_PEI_SERVICES.
27   @param PeiBotDevice   The pointer to PEI_BOT_DEVICE instance.
28 
29   @retval EFI_SUCCESS       Inquiry command completes successfully.
30   @retval EFI_DEVICE_ERROR  Inquiry command failed.
31 
32 **/
33 EFI_STATUS
PeiUsbInquiry(IN EFI_PEI_SERVICES ** PeiServices,IN PEI_BOT_DEVICE * PeiBotDevice)34 PeiUsbInquiry (
35   IN  EFI_PEI_SERVICES  **PeiServices,
36   IN  PEI_BOT_DEVICE    *PeiBotDevice
37   )
38 {
39   ATAPI_PACKET_COMMAND  Packet;
40   EFI_STATUS            Status;
41   ATAPI_INQUIRY_DATA          Idata;
42 
43   //
44   // fill command packet
45   //
46   ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
47   ZeroMem (&Idata, sizeof (ATAPI_INQUIRY_DATA));
48 
49   Packet.Inquiry.opcode             = ATA_CMD_INQUIRY;
50   Packet.Inquiry.page_code          = 0;
51   Packet.Inquiry.allocation_length  = 36;
52 
53   //
54   // Send scsi INQUIRY command packet.
55   // According to SCSI Primary Commands-2 spec, host only needs to
56   // retrieve the first 36 bytes for standard INQUIRY data.
57   //
58   Status = PeiAtapiCommand (
59             PeiServices,
60             PeiBotDevice,
61             &Packet,
62             (UINT8) sizeof (ATAPI_PACKET_COMMAND),
63             &Idata,
64             36,
65             EfiUsbDataIn,
66             2000
67             );
68 
69   if (EFI_ERROR (Status)) {
70     return EFI_DEVICE_ERROR;
71   }
72 
73   if ((Idata.peripheral_type & 0x1f) == 0x05) {
74     PeiBotDevice->DeviceType      = USBCDROM;
75     PeiBotDevice->Media.BlockSize = 0x800;
76     PeiBotDevice->Media2.ReadOnly       = TRUE;
77     PeiBotDevice->Media2.RemovableMedia = TRUE;
78     PeiBotDevice->Media2.BlockSize      = 0x800;
79   } else {
80     PeiBotDevice->DeviceType      = USBFLOPPY;
81     PeiBotDevice->Media.BlockSize = 0x200;
82     PeiBotDevice->Media2.ReadOnly       = FALSE;
83     PeiBotDevice->Media2.RemovableMedia = TRUE;
84     PeiBotDevice->Media2.BlockSize      = 0x200;
85   }
86 
87   return EFI_SUCCESS;
88 }
89 
90 /**
91   Sends out ATAPI Test Unit Ready Packet Command to the specified device
92   to find out whether device is accessible.
93 
94   @param PeiServices    The pointer of EFI_PEI_SERVICES.
95   @param PeiBotDevice   The pointer to PEI_BOT_DEVICE instance.
96 
97   @retval EFI_SUCCESS        TestUnit command executed successfully.
98   @retval EFI_DEVICE_ERROR   Device cannot be executed TestUnit command successfully.
99 
100 **/
101 EFI_STATUS
PeiUsbTestUnitReady(IN EFI_PEI_SERVICES ** PeiServices,IN PEI_BOT_DEVICE * PeiBotDevice)102 PeiUsbTestUnitReady (
103   IN  EFI_PEI_SERVICES  **PeiServices,
104   IN  PEI_BOT_DEVICE    *PeiBotDevice
105   )
106 {
107   ATAPI_PACKET_COMMAND  Packet;
108   EFI_STATUS            Status;
109 
110   //
111   // fill command packet
112   //
113   ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
114   Packet.TestUnitReady.opcode = ATA_CMD_TEST_UNIT_READY;
115 
116   //
117   // send command packet
118   //
119   Status = PeiAtapiCommand (
120             PeiServices,
121             PeiBotDevice,
122             &Packet,
123             (UINT8) sizeof (ATAPI_PACKET_COMMAND),
124             NULL,
125             0,
126             EfiUsbNoData,
127             2000
128             );
129 
130   if (EFI_ERROR (Status)) {
131     return EFI_DEVICE_ERROR;
132   }
133 
134   return EFI_SUCCESS;
135 }
136 
137 /**
138   Sends out ATAPI Request Sense Packet Command to the specified device.
139 
140   @param PeiServices    The pointer of EFI_PEI_SERVICES.
141   @param PeiBotDevice   The pointer to PEI_BOT_DEVICE instance.
142   @param SenseCounts    Length of sense buffer.
143   @param SenseKeyBuffer Pointer to sense buffer.
144 
145   @retval EFI_SUCCESS           Command executed successfully.
146   @retval EFI_DEVICE_ERROR      Some device errors happen.
147 
148 **/
149 EFI_STATUS
PeiUsbRequestSense(IN EFI_PEI_SERVICES ** PeiServices,IN PEI_BOT_DEVICE * PeiBotDevice,OUT UINTN * SenseCounts,IN UINT8 * SenseKeyBuffer)150 PeiUsbRequestSense (
151   IN  EFI_PEI_SERVICES  **PeiServices,
152   IN  PEI_BOT_DEVICE    *PeiBotDevice,
153   OUT UINTN             *SenseCounts,
154   IN  UINT8             *SenseKeyBuffer
155   )
156 {
157   EFI_STATUS                  Status;
158   ATAPI_PACKET_COMMAND        Packet;
159   UINT8                       *Ptr;
160   BOOLEAN                     SenseReq;
161   ATAPI_REQUEST_SENSE_DATA    *Sense;
162 
163   *SenseCounts = 0;
164 
165   //
166   // fill command packet for Request Sense Packet Command
167   //
168   ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
169   Packet.RequestSence.opcode            = ATA_CMD_REQUEST_SENSE;
170   Packet.RequestSence.allocation_length = (UINT8) sizeof (ATAPI_REQUEST_SENSE_DATA);
171 
172   Ptr = SenseKeyBuffer;
173 
174   SenseReq = TRUE;
175 
176   //
177   //  request sense data from device continuously
178   //  until no sense data exists in the device.
179   //
180   while (SenseReq) {
181     Sense = (ATAPI_REQUEST_SENSE_DATA *) Ptr;
182 
183     //
184     // send out Request Sense Packet Command and get one Sense
185     // data form device.
186     //
187     Status = PeiAtapiCommand (
188               PeiServices,
189               PeiBotDevice,
190               &Packet,
191               (UINT8) sizeof (ATAPI_PACKET_COMMAND),
192               (VOID *) Ptr,
193               sizeof (ATAPI_REQUEST_SENSE_DATA),
194               EfiUsbDataIn,
195               2000
196               );
197 
198     //
199     // failed to get Sense data
200     //
201     if (EFI_ERROR (Status)) {
202       if (*SenseCounts == 0) {
203         return EFI_DEVICE_ERROR;
204       } else {
205         return EFI_SUCCESS;
206       }
207     }
208 
209     if (Sense->sense_key != ATA_SK_NO_SENSE) {
210 
211       Ptr += sizeof (ATAPI_REQUEST_SENSE_DATA);
212       //
213       // Ptr is byte based pointer
214       //
215       (*SenseCounts)++;
216 
217       if (*SenseCounts == MAXSENSEKEY) {
218         break;
219       }
220 
221     } else {
222       //
223       // when no sense key, skip out the loop
224       //
225       SenseReq = FALSE;
226     }
227   }
228 
229   return EFI_SUCCESS;
230 }
231 
232 /**
233   Sends out ATAPI Read Capacity Packet Command to the specified device.
234   This command will return the information regarding the capacity of the
235   media in the device.
236 
237   @param PeiServices    The pointer of EFI_PEI_SERVICES.
238   @param PeiBotDevice   The pointer to PEI_BOT_DEVICE instance.
239 
240   @retval EFI_SUCCESS           Command executed successfully.
241   @retval EFI_DEVICE_ERROR      Some device errors happen.
242 
243 **/
244 EFI_STATUS
PeiUsbReadCapacity(IN EFI_PEI_SERVICES ** PeiServices,IN PEI_BOT_DEVICE * PeiBotDevice)245 PeiUsbReadCapacity (
246   IN  EFI_PEI_SERVICES  **PeiServices,
247   IN  PEI_BOT_DEVICE    *PeiBotDevice
248   )
249 {
250   EFI_STATUS                  Status;
251   ATAPI_PACKET_COMMAND        Packet;
252   ATAPI_READ_CAPACITY_DATA    Data;
253   UINT32                      LastBlock;
254 
255   ZeroMem (&Data, sizeof (ATAPI_READ_CAPACITY_DATA));
256   ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
257 
258   Packet.Inquiry.opcode = ATA_CMD_READ_CAPACITY;
259 
260   //
261   // send command packet
262   //
263   Status = PeiAtapiCommand (
264             PeiServices,
265             PeiBotDevice,
266             &Packet,
267             (UINT8) sizeof (ATAPI_PACKET_COMMAND),
268             (VOID *) &Data,
269             sizeof (ATAPI_READ_CAPACITY_DATA),
270             EfiUsbDataIn,
271             2000
272             );
273 
274   if (EFI_ERROR (Status)) {
275     return EFI_DEVICE_ERROR;
276   }
277   LastBlock = (Data.LastLba3 << 24) | (Data.LastLba2 << 16) | (Data.LastLba1 << 8) | Data.LastLba0;
278 
279   if (LastBlock == 0xFFFFFFFF) {
280     DEBUG ((EFI_D_INFO, "The usb device LBA count is larger than 0xFFFFFFFF!\n"));
281   }
282 
283   PeiBotDevice->Media.LastBlock    = LastBlock;
284   PeiBotDevice->Media.MediaPresent = TRUE;
285 
286   PeiBotDevice->Media2.LastBlock    = LastBlock;
287   PeiBotDevice->Media2.MediaPresent = TRUE;
288 
289   return EFI_SUCCESS;
290 }
291 
292 /**
293   Sends out ATAPI Read Format Capacity Data Command to the specified device.
294   This command will return the information regarding the capacity of the
295   media in the device.
296 
297   @param PeiServices    The pointer of EFI_PEI_SERVICES.
298   @param PeiBotDevice   The pointer to PEI_BOT_DEVICE instance.
299 
300   @retval EFI_SUCCESS           Command executed successfully.
301   @retval EFI_DEVICE_ERROR      Some device errors happen.
302 
303 **/
304 EFI_STATUS
PeiUsbReadFormattedCapacity(IN EFI_PEI_SERVICES ** PeiServices,IN PEI_BOT_DEVICE * PeiBotDevice)305 PeiUsbReadFormattedCapacity (
306   IN  EFI_PEI_SERVICES  **PeiServices,
307   IN  PEI_BOT_DEVICE    *PeiBotDevice
308   )
309 {
310   EFI_STATUS                      Status;
311   ATAPI_PACKET_COMMAND            Packet;
312   ATAPI_READ_FORMAT_CAPACITY_DATA FormatData;
313   UINT32                          LastBlock;
314 
315   ZeroMem (&FormatData, sizeof (ATAPI_READ_FORMAT_CAPACITY_DATA));
316   ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
317 
318   Packet.ReadFormatCapacity.opcode                = ATA_CMD_READ_FORMAT_CAPACITY;
319   Packet.ReadFormatCapacity.allocation_length_lo  = 12;
320 
321   //
322   // send command packet
323   //
324   Status = PeiAtapiCommand (
325             PeiServices,
326             PeiBotDevice,
327             &Packet,
328             (UINT8) sizeof (ATAPI_PACKET_COMMAND),
329             (VOID *) &FormatData,
330             sizeof (ATAPI_READ_FORMAT_CAPACITY_DATA),
331             EfiUsbDataIn,
332             2000
333             );
334 
335   if (EFI_ERROR (Status)) {
336     return EFI_DEVICE_ERROR;
337   }
338 
339   if (FormatData.DesCode == 3) {
340     //
341     // Media is not present
342     //
343     PeiBotDevice->Media.MediaPresent  = FALSE;
344     PeiBotDevice->Media.LastBlock     = 0;
345     PeiBotDevice->Media2.MediaPresent  = FALSE;
346     PeiBotDevice->Media2.LastBlock     = 0;
347 
348   } else {
349     LastBlock = (FormatData.LastLba3 << 24) | (FormatData.LastLba2 << 16) | (FormatData.LastLba1 << 8) | FormatData.LastLba0;
350     if (LastBlock == 0xFFFFFFFF) {
351       DEBUG ((EFI_D_INFO, "The usb device LBA count is larger than 0xFFFFFFFF!\n"));
352     }
353 
354     PeiBotDevice->Media.LastBlock = LastBlock;
355 
356     PeiBotDevice->Media.LastBlock--;
357 
358     PeiBotDevice->Media.MediaPresent = TRUE;
359 
360     PeiBotDevice->Media2.MediaPresent = TRUE;
361     PeiBotDevice->Media2.LastBlock    = PeiBotDevice->Media.LastBlock;
362   }
363 
364   return EFI_SUCCESS;
365 }
366 
367 /**
368   Execute Read(10) ATAPI command on a specific SCSI target.
369 
370   Executes the ATAPI Read(10) command on the ATAPI target specified by PeiBotDevice.
371 
372   @param PeiServices       The pointer of EFI_PEI_SERVICES.
373   @param PeiBotDevice      The pointer to PEI_BOT_DEVICE instance.
374   @param Buffer            The pointer to data buffer.
375   @param Lba               The start logic block address of reading.
376   @param NumberOfBlocks    The block number of reading.
377 
378   @retval EFI_SUCCESS           Command executed successfully.
379   @retval EFI_DEVICE_ERROR      Some device errors happen.
380 
381 **/
382 EFI_STATUS
PeiUsbRead10(IN EFI_PEI_SERVICES ** PeiServices,IN PEI_BOT_DEVICE * PeiBotDevice,IN VOID * Buffer,IN EFI_PEI_LBA Lba,IN UINTN NumberOfBlocks)383 PeiUsbRead10 (
384   IN  EFI_PEI_SERVICES  **PeiServices,
385   IN  PEI_BOT_DEVICE    *PeiBotDevice,
386   IN  VOID              *Buffer,
387   IN  EFI_PEI_LBA       Lba,
388   IN  UINTN             NumberOfBlocks
389   )
390 {
391   ATAPI_PACKET_COMMAND  Packet;
392   ATAPI_READ10_CMD      *Read10Packet;
393   UINT16                MaxBlock;
394   UINT16                BlocksRemaining;
395   UINT16                SectorCount;
396   UINT32                Lba32;
397   UINT32                BlockSize;
398   UINT32                ByteCount;
399   VOID                  *PtrBuffer;
400   EFI_STATUS            Status;
401   UINT16                TimeOut;
402 
403   //
404   // prepare command packet for the Inquiry Packet Command.
405   //
406   ZeroMem (&Packet, sizeof (ATAPI_PACKET_COMMAND));
407   Read10Packet    = &Packet.Read10;
408   Lba32           = (UINT32) Lba;
409   PtrBuffer       = Buffer;
410 
411   BlockSize       = (UINT32) PeiBotDevice->Media.BlockSize;
412 
413   MaxBlock        = (UINT16) (65535 / BlockSize);
414   BlocksRemaining = (UINT16) NumberOfBlocks;
415 
416   Status          = EFI_SUCCESS;
417   while (BlocksRemaining > 0) {
418 
419     if (BlocksRemaining <= MaxBlock) {
420 
421       SectorCount = BlocksRemaining;
422 
423     } else {
424 
425       SectorCount = MaxBlock;
426     }
427     //
428     // fill the Packet data structure
429     //
430     Read10Packet->opcode = ATA_CMD_READ_10;
431 
432     //
433     // Lba0 ~ Lba3 specify the start logical block address of the data transfer.
434     // Lba0 is MSB, Lba3 is LSB
435     //
436     Read10Packet->Lba3  = (UINT8) (Lba32 & 0xff);
437     Read10Packet->Lba2  = (UINT8) (Lba32 >> 8);
438     Read10Packet->Lba1  = (UINT8) (Lba32 >> 16);
439     Read10Packet->Lba0  = (UINT8) (Lba32 >> 24);
440 
441     //
442     // TranLen0 ~ TranLen1 specify the transfer length in block unit.
443     // TranLen0 is MSB, TranLen is LSB
444     //
445     Read10Packet->TranLen1  = (UINT8) (SectorCount & 0xff);
446     Read10Packet->TranLen0  = (UINT8) (SectorCount >> 8);
447 
448     ByteCount               = SectorCount * BlockSize;
449 
450     TimeOut                 = (UINT16) (SectorCount * 2000);
451 
452     //
453     // send command packet
454     //
455     Status = PeiAtapiCommand (
456               PeiServices,
457               PeiBotDevice,
458               &Packet,
459               (UINT8) sizeof (ATAPI_PACKET_COMMAND),
460               (VOID *) PtrBuffer,
461               ByteCount,
462               EfiUsbDataIn,
463               TimeOut
464               );
465 
466     if (Status != EFI_SUCCESS) {
467       return Status;
468     }
469 
470     Lba32 += SectorCount;
471     PtrBuffer       = (UINT8 *) PtrBuffer + SectorCount * BlockSize;
472     BlocksRemaining = (UINT16) (BlocksRemaining - SectorCount);
473   }
474 
475   return Status;
476 }
477 
478 /**
479   Check if there is media according to sense data.
480 
481   @param  SenseData   Pointer to sense data.
482   @param  SenseCounts Count of sense data.
483 
484   @retval TRUE    No media
485   @retval FALSE   Media exists
486 
487 **/
488 BOOLEAN
IsNoMedia(IN ATAPI_REQUEST_SENSE_DATA * SenseData,IN UINTN SenseCounts)489 IsNoMedia (
490   IN  ATAPI_REQUEST_SENSE_DATA *SenseData,
491   IN  UINTN                    SenseCounts
492   )
493 {
494   ATAPI_REQUEST_SENSE_DATA  *SensePtr;
495   UINTN                     Index;
496   BOOLEAN                   NoMedia;
497 
498   NoMedia   = FALSE;
499   SensePtr  = SenseData;
500 
501   for (Index = 0; Index < SenseCounts; Index++) {
502 
503     switch (SensePtr->sense_key) {
504 
505     case ATA_SK_NOT_READY:
506       switch (SensePtr->addnl_sense_code) {
507       //
508       // if no media, fill IdeDev parameter with specific info.
509       //
510       case ATA_ASC_NO_MEDIA:
511         NoMedia = TRUE;
512         break;
513 
514       default:
515         break;
516       }
517       break;
518 
519     default:
520       break;
521     }
522 
523     SensePtr++;
524   }
525 
526   return NoMedia;
527 }
528 
529 /**
530   Check if there is media error according to sense data.
531 
532   @param  SenseData   Pointer to sense data.
533   @param  SenseCounts Count of sense data.
534 
535   @retval TRUE    Media error
536   @retval FALSE   No media error
537 
538 **/
539 BOOLEAN
IsMediaError(IN ATAPI_REQUEST_SENSE_DATA * SenseData,IN UINTN SenseCounts)540 IsMediaError (
541   IN  ATAPI_REQUEST_SENSE_DATA    *SenseData,
542   IN  UINTN                       SenseCounts
543   )
544 {
545   ATAPI_REQUEST_SENSE_DATA  *SensePtr;
546   UINTN                     Index;
547   BOOLEAN                   Error;
548 
549   SensePtr  = SenseData;
550   Error     = FALSE;
551 
552   for (Index = 0; Index < SenseCounts; Index++) {
553 
554     switch (SensePtr->sense_key) {
555     //
556     // Medium error case
557     //
558     case ATA_SK_MEDIUM_ERROR:
559       switch (SensePtr->addnl_sense_code) {
560       case ATA_ASC_MEDIA_ERR1:
561         //
562         // fall through
563         //
564       case ATA_ASC_MEDIA_ERR2:
565         //
566         // fall through
567         //
568       case ATA_ASC_MEDIA_ERR3:
569         //
570         // fall through
571         //
572       case ATA_ASC_MEDIA_ERR4:
573         Error = TRUE;
574         break;
575 
576       default:
577         break;
578       }
579 
580       break;
581 
582     //
583     // Medium upside-down case
584     //
585     case ATA_SK_NOT_READY:
586       switch (SensePtr->addnl_sense_code) {
587       case ATA_ASC_MEDIA_UPSIDE_DOWN:
588         Error = TRUE;
589         break;
590 
591       default:
592         break;
593       }
594       break;
595 
596     default:
597       break;
598     }
599 
600     SensePtr++;
601   }
602 
603   return Error;
604 }
605 
606 /**
607   Check if media is changed according to sense data.
608 
609   @param  SenseData   Pointer to sense data.
610   @param  SenseCounts Count of sense data.
611 
612   @retval TRUE    There is media change event.
613   @retval FALSE   media is NOT changed.
614 
615 **/
616 BOOLEAN
IsMediaChange(IN ATAPI_REQUEST_SENSE_DATA * SenseData,IN UINTN SenseCounts)617 IsMediaChange (
618   IN  ATAPI_REQUEST_SENSE_DATA *SenseData,
619   IN  UINTN                    SenseCounts
620   )
621 {
622   ATAPI_REQUEST_SENSE_DATA  *SensePtr;
623   UINTN                     Index;
624   BOOLEAN                   MediaChange;
625 
626   MediaChange = FALSE;
627 
628   SensePtr    = SenseData;
629 
630   for (Index = 0; Index < SenseCounts; Index++) {
631     //
632     // catch media change sense key and addition sense data
633     //
634     switch (SensePtr->sense_key) {
635     case ATA_SK_UNIT_ATTENTION:
636       switch (SensePtr->addnl_sense_code) {
637       case ATA_ASC_MEDIA_CHANGE:
638         MediaChange = TRUE;
639         break;
640 
641       default:
642         break;
643       }
644       break;
645 
646     default:
647       break;
648     }
649 
650     SensePtr++;
651   }
652 
653   return MediaChange;
654 }
655