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