Home | History | Annotate | Download | only in UsbSerialNumberLib
      1 /** @file
      2 
      3   Copyright (c) 2017, Linaro. All rights reserved.
      4 
      5   This program and the accompanying materials
      6   are licensed and made available under the terms and conditions of the BSD License
      7   which accompanies this distribution.  The full text of the license may be found at
      8   http://opensource.org/licenses/bsd-license.php
      9 
     10   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 **/
     14 
     15 #include <Uefi.h>
     16 
     17 #include <Library/ArmGenericTimerCounterLib.h>
     18 #include <Library/BaseLib.h>
     19 #include <Library/BaseMemoryLib.h>
     20 #include <Library/DebugLib.h>
     21 #include <Library/DevicePathLib.h>
     22 #include <Library/MemoryAllocationLib.h>
     23 #include <Library/PrintLib.h>
     24 #include <Library/UefiBootServicesTableLib.h>
     25 #include <Library/UsbSerialNumberLib.h>
     26 
     27 #include <Protocol/BlockIo.h>
     28 #include <Protocol/DevicePath.h>
     29 
     30 
     31 #define SERIAL_NUMBER_SIZE               17
     32 
     33 #define RANDOM_MAX                       0x7FFFFFFFFFFFFFFF
     34 #define RANDOM_MAGIC                     0x9A4DBEAF
     35 
     36 STATIC
     37 EFI_STATUS
     38 GenerateRandomData (
     39   IN  UINT32              Seed,
     40   OUT UINT64             *RandomData
     41   )
     42 {
     43   INT64                   Quotient, Remainder, Tmp;
     44 
     45   if (RandomData == NULL) {
     46     return EFI_INVALID_PARAMETER;
     47   }
     48   Quotient = (INT64) Seed / 127773;
     49   Remainder = (INT64) Seed % 127773;
     50   Tmp = (16807 * Remainder) - (2836 * Quotient);
     51   if (Tmp < 0) {
     52     Tmp += RANDOM_MAX;
     53   }
     54   Tmp = Tmp % ((UINT64)RANDOM_MAX + 1);
     55   *RandomData = (UINT64)Tmp;
     56   return EFI_SUCCESS;
     57 }
     58 
     59 EFI_STATUS
     60 GenerateUsbSNBySeed (
     61   IN  UINT32                  Seed,
     62   OUT RANDOM_SERIAL_NUMBER   *RandomSN
     63   )
     64 {
     65   EFI_STATUS               Status;
     66   UINT64                   Tmp;
     67 
     68   if (RandomSN == NULL) {
     69     return EFI_INVALID_PARAMETER;
     70   }
     71   ZeroMem (RandomSN, sizeof (RANDOM_SERIAL_NUMBER));
     72   Status = GenerateRandomData (Seed, &Tmp);
     73   if (EFI_ERROR (Status)) {
     74     return Status;
     75   }
     76   RandomSN->Data = (Tmp << 32) | Seed;
     77   UnicodeSPrint (RandomSN->UnicodeSN, SERIAL_NUMBER_SIZE * sizeof (CHAR16), L"%lx", RandomSN->Data);
     78   RandomSN->Magic = RANDOM_MAGIC;
     79   return EFI_SUCCESS;
     80 }
     81 
     82 EFI_STATUS
     83 GenerateUsbSN (
     84   OUT CHAR16                  *UnicodeSN
     85   )
     86 {
     87   EFI_STATUS               Status;
     88   UINT64                   Tmp;
     89   UINT32                   Seed;
     90   RANDOM_SERIAL_NUMBER     RandomSN;
     91 
     92   if (UnicodeSN == NULL) {
     93     return EFI_INVALID_PARAMETER;
     94   }
     95   ZeroMem (&RandomSN, sizeof (RANDOM_SERIAL_NUMBER));
     96   Seed = ArmGenericTimerGetSystemCount ();
     97   Status = GenerateRandomData (Seed, &Tmp);
     98   if (EFI_ERROR (Status)) {
     99     return Status;
    100   }
    101   RandomSN.Data = (Tmp << 32) | Seed;
    102   UnicodeSPrint (RandomSN.UnicodeSN, SERIAL_NUMBER_SIZE * sizeof (CHAR16), L"%lx", RandomSN.Data);
    103   StrCpyS (UnicodeSN, SERIAL_NUMBER_SIZE * sizeof (CHAR16), RandomSN.UnicodeSN);
    104   return EFI_SUCCESS;
    105 }
    106 
    107 EFI_STATUS
    108 LoadSNFromBlock (
    109   IN  EFI_HANDLE              FlashHandle,
    110   IN  EFI_LBA                 Lba,
    111   OUT CHAR16                 *UnicodeSN
    112   )
    113 {
    114   EFI_STATUS                  Status;
    115   EFI_BLOCK_IO_PROTOCOL      *BlockIoProtocol;
    116   VOID                       *DataPtr;
    117   BOOLEAN                     Found = FALSE;
    118   UINT32                      Seed;
    119   RANDOM_SERIAL_NUMBER       *RandomSN;
    120   UINTN                       NumPages;
    121   CHAR16                      UnicodeStr[SERIAL_NUMBER_SIZE];
    122 
    123   if (UnicodeSN == NULL) {
    124     return EFI_INVALID_PARAMETER;
    125   }
    126   Status = gBS->OpenProtocol (
    127                   FlashHandle,
    128                   &gEfiBlockIoProtocolGuid,
    129                   (VOID **) &BlockIoProtocol,
    130                   gImageHandle,
    131                   NULL,
    132                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
    133                   );
    134   if (EFI_ERROR (Status)) {
    135     DEBUG ((DEBUG_WARN, "Warning: Couldn't open block device (status: %r)\n", Status));
    136     return EFI_DEVICE_ERROR;
    137   }
    138 
    139   NumPages = EFI_SIZE_TO_PAGES (BlockIoProtocol->Media->BlockSize);
    140   DataPtr = AllocatePages (NumPages);
    141   if (DataPtr == NULL) {
    142     return EFI_BUFFER_TOO_SMALL;
    143   }
    144   Status = BlockIoProtocol->ReadBlocks (
    145                               BlockIoProtocol,
    146                               BlockIoProtocol->Media->MediaId,
    147                               Lba,
    148                               BlockIoProtocol->Media->BlockSize,
    149                               DataPtr
    150                               );
    151   if (EFI_ERROR (Status)) {
    152     DEBUG ((DEBUG_WARN, "Warning: Failed on reading blocks\n"));
    153     goto Exit;
    154   }
    155 
    156   Seed = ArmGenericTimerGetSystemCount ();
    157   RandomSN = (RANDOM_SERIAL_NUMBER *)DataPtr;
    158   if (RandomSN->Magic == RANDOM_MAGIC) {
    159     Found = TRUE;
    160     // Verify the unicode string.
    161     ZeroMem (UnicodeStr, SERIAL_NUMBER_SIZE * sizeof (CHAR16));
    162     UnicodeSPrint (UnicodeStr, SERIAL_NUMBER_SIZE * sizeof (CHAR16), L"%lx", RandomSN->Data);
    163     if (StrLen (RandomSN->UnicodeSN) != StrLen (UnicodeStr)) {
    164       Found = FALSE;
    165     }
    166     if (StrnCmp (RandomSN->UnicodeSN, UnicodeStr, StrLen (UnicodeStr)) != 0) {
    167       Found = FALSE;
    168     }
    169   }
    170   if (Found == FALSE) {
    171     Status = GenerateUsbSNBySeed (Seed, RandomSN);
    172     if (EFI_ERROR (Status)) {
    173       DEBUG ((DEBUG_WARN, "Warning: Failed to generate serial number\n"));
    174       goto Exit;
    175     }
    176     // Update SN to block device
    177     Status = BlockIoProtocol->WriteBlocks (
    178                                 BlockIoProtocol,
    179                                 BlockIoProtocol->Media->MediaId,
    180                                 Lba,
    181                                 BlockIoProtocol->Media->BlockSize,
    182                                 DataPtr
    183                                 );
    184     if (EFI_ERROR (Status)) {
    185       DEBUG ((DEBUG_WARN, "Warning: Failed on writing blocks\n"));
    186       goto Exit;
    187     }
    188   }
    189   CopyMem (UnicodeSN, RandomSN->UnicodeSN, SERIAL_NUMBER_SIZE * sizeof (CHAR16));
    190 Exit:
    191   FreePages (DataPtr, NumPages);
    192   return Status;
    193 }
    194 
    195 EFI_STATUS
    196 StoreSNToBlock (
    197   IN EFI_HANDLE               FlashHandle,
    198   IN EFI_LBA                  Lba,
    199   IN CHAR16                  *UnicodeSN
    200   )
    201 {
    202   EFI_STATUS                  Status;
    203   EFI_BLOCK_IO_PROTOCOL      *BlockIoProtocol;
    204   VOID                       *DataPtr;
    205   UINTN                       NumPages;
    206   RANDOM_SERIAL_NUMBER       *RandomSN;
    207   CHAR16                      UnicodeStr[SERIAL_NUMBER_SIZE];
    208 
    209   if (UnicodeSN == NULL) {
    210     return EFI_INVALID_PARAMETER;
    211   }
    212   Status = gBS->OpenProtocol (
    213                   FlashHandle,
    214                   &gEfiBlockIoProtocolGuid,
    215                   (VOID **) &BlockIoProtocol,
    216                   gImageHandle,
    217                   NULL,
    218                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
    219                   );
    220   if (EFI_ERROR (Status)) {
    221     DEBUG ((DEBUG_WARN, "Warning: Couldn't open block device (status: %r)\n", Status));
    222     return EFI_DEVICE_ERROR;
    223   }
    224   NumPages = EFI_SIZE_TO_PAGES (BlockIoProtocol->Media->BlockSize);
    225   DataPtr = AllocatePages (NumPages);
    226   if (DataPtr == NULL) {
    227     return EFI_BUFFER_TOO_SMALL;
    228   }
    229   ZeroMem (DataPtr, BlockIoProtocol->Media->BlockSize);
    230   RandomSN = (RANDOM_SERIAL_NUMBER *)DataPtr;
    231   RandomSN->Magic = RANDOM_MAGIC;
    232   StrnCpyS (RandomSN->UnicodeSN, SERIAL_NUMBER_SIZE * sizeof (CHAR16), UnicodeSN, StrSize (UnicodeSN));
    233   RandomSN->Data = StrHexToUint64 (RandomSN->UnicodeSN);
    234 
    235   // Verify the unicode string.
    236   ZeroMem (UnicodeStr, SERIAL_NUMBER_SIZE * sizeof (CHAR16));
    237   UnicodeSPrint (UnicodeStr, SERIAL_NUMBER_SIZE * sizeof (CHAR16), L"%lx", RandomSN->Data);
    238   if (StrLen (RandomSN->UnicodeSN) != StrLen (UnicodeStr)) {
    239     Status = EFI_INVALID_PARAMETER;
    240     goto Exit;
    241   }
    242   if (StrnCmp (RandomSN->UnicodeSN, UnicodeStr, StrLen (UnicodeStr)) != 0) {
    243     Status = EFI_INVALID_PARAMETER;
    244     goto Exit;
    245   }
    246 
    247   Status = BlockIoProtocol->WriteBlocks (
    248                               BlockIoProtocol,
    249                               BlockIoProtocol->Media->MediaId,
    250                               Lba,
    251                               BlockIoProtocol->Media->BlockSize,
    252                               DataPtr
    253                               );
    254   if (EFI_ERROR (Status)) {
    255     DEBUG ((DEBUG_WARN, "Warning: Failed on writing blocks\n"));
    256     goto Exit;
    257   }
    258 Exit:
    259   FreePages (DataPtr, NumPages);
    260   return Status;
    261 }
    262