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_LEN                16
32 #define SERIAL_NUMBER_SIZE               17
33 
34 #define RANDOM_MAX                       0x7FFFFFFFFFFFFFFF
35 #define RANDOM_MAGIC                     0x9A4DBEAF
36 
37 STATIC
38 EFI_STATUS
GenerateRandomData(IN UINT32 Seed,OUT UINT64 * RandomData)39 GenerateRandomData (
40   IN  UINT32              Seed,
41   OUT UINT64             *RandomData
42   )
43 {
44   INT64                   Quotient, Remainder, Tmp;
45 
46   if (RandomData == NULL) {
47     return EFI_INVALID_PARAMETER;
48   }
49   Quotient = (INT64) Seed / 127773;
50   Remainder = (INT64) Seed % 127773;
51   Tmp = (16807 * Remainder) - (2836 * Quotient);
52   if (Tmp < 0) {
53     Tmp += RANDOM_MAX;
54   }
55   Tmp = Tmp % ((UINT64)RANDOM_MAX + 1);
56   *RandomData = (UINT64)Tmp;
57   return EFI_SUCCESS;
58 }
59 
60 EFI_STATUS
GenerateUsbSNBySeed(IN UINT32 Seed,OUT RANDOM_SERIAL_NUMBER * RandomSN)61 GenerateUsbSNBySeed (
62   IN  UINT32                  Seed,
63   OUT RANDOM_SERIAL_NUMBER   *RandomSN
64   )
65 {
66   EFI_STATUS               Status;
67   UINT64                   Tmp;
68 
69   if (RandomSN == NULL) {
70     return EFI_INVALID_PARAMETER;
71   }
72   ZeroMem (RandomSN, sizeof (RANDOM_SERIAL_NUMBER));
73   Status = GenerateRandomData (Seed, &Tmp);
74   if (EFI_ERROR (Status)) {
75     return Status;
76   }
77   RandomSN->Data = (Tmp << 32) | Seed;
78   UnicodeSPrint (RandomSN->UnicodeSN, SERIAL_NUMBER_SIZE * sizeof (CHAR16), L"%lx", RandomSN->Data);
79   RandomSN->Magic = RANDOM_MAGIC;
80   return EFI_SUCCESS;
81 }
82 
83 EFI_STATUS
GenerateUsbSN(OUT CHAR16 * UnicodeSN)84 GenerateUsbSN (
85   OUT CHAR16                  *UnicodeSN
86   )
87 {
88   EFI_STATUS               Status;
89   UINT64                   Tmp;
90   UINT32                   Seed;
91   RANDOM_SERIAL_NUMBER     RandomSN;
92 
93   if (UnicodeSN == NULL) {
94     return EFI_INVALID_PARAMETER;
95   }
96   ZeroMem (&RandomSN, sizeof (RANDOM_SERIAL_NUMBER));
97   Seed = ArmGenericTimerGetSystemCount ();
98   Status = GenerateRandomData (Seed, &Tmp);
99   if (EFI_ERROR (Status)) {
100     return Status;
101   }
102   RandomSN.Data = (Tmp << 32) | Seed;
103   UnicodeSPrint (RandomSN.UnicodeSN, SERIAL_NUMBER_SIZE * sizeof (CHAR16), L"%lx", RandomSN.Data);
104   StrCpyS (UnicodeSN, SERIAL_NUMBER_SIZE * sizeof (CHAR16), RandomSN.UnicodeSN);
105   return EFI_SUCCESS;
106 }
107 
108 EFI_STATUS
AssignUsbSN(IN CHAR8 * AsciiCmd,OUT CHAR16 * UnicodeSN)109 AssignUsbSN (
110   IN  CHAR8                   *AsciiCmd,
111   OUT CHAR16                  *UnicodeSN
112   )
113 {
114   CHAR8                       Data;
115   UINTN                       Index;
116   RANDOM_SERIAL_NUMBER        RandomSN;
117 
118   if ((AsciiCmd == NULL) || (UnicodeSN == NULL)) {
119     return EFI_INVALID_PARAMETER;
120   }
121   for (Index = 0; Index < SERIAL_NUMBER_LEN; Index++) {
122     Data = *(AsciiCmd + Index);
123     if (((Data >= '0') && (Data <= '9')) ||
124         ((Data >= 'A') && (Data <= 'F'))) {
125       continue;
126     }
127     // Always use with upper case
128     if ((Data >= 'a') && (Data <= 'f')) {
129       *(AsciiCmd + Index) = Data - 'a' + 'A';
130       continue;
131     }
132     if (Data == '\0') {
133       break;
134     }
135     return EFI_INVALID_PARAMETER;
136   }
137   ZeroMem (&RandomSN, sizeof (RANDOM_SERIAL_NUMBER));
138   AsciiStrToUnicodeStr (AsciiCmd, RandomSN.UnicodeSN);
139   StrCpyS (UnicodeSN, SERIAL_NUMBER_SIZE * sizeof (CHAR16), RandomSN.UnicodeSN);
140   return EFI_SUCCESS;
141 }
142 
143 EFI_STATUS
LoadSNFromBlock(IN EFI_HANDLE FlashHandle,IN EFI_LBA Lba,OUT CHAR16 * UnicodeSN)144 LoadSNFromBlock (
145   IN  EFI_HANDLE              FlashHandle,
146   IN  EFI_LBA                 Lba,
147   OUT CHAR16                 *UnicodeSN
148   )
149 {
150   EFI_STATUS                  Status;
151   EFI_BLOCK_IO_PROTOCOL      *BlockIoProtocol;
152   VOID                       *DataPtr;
153   BOOLEAN                     Found = FALSE;
154   UINT32                      Seed;
155   RANDOM_SERIAL_NUMBER       *RandomSN;
156   UINTN                       NumPages;
157   CHAR16                      UnicodeStr[SERIAL_NUMBER_SIZE];
158 
159   if (UnicodeSN == NULL) {
160     return EFI_INVALID_PARAMETER;
161   }
162   Status = gBS->OpenProtocol (
163                   FlashHandle,
164                   &gEfiBlockIoProtocolGuid,
165                   (VOID **) &BlockIoProtocol,
166                   gImageHandle,
167                   NULL,
168                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
169                   );
170   if (EFI_ERROR (Status)) {
171     DEBUG ((DEBUG_WARN, "Warning: Couldn't open block device (status: %r)\n", Status));
172     return EFI_DEVICE_ERROR;
173   }
174 
175   NumPages = EFI_SIZE_TO_PAGES (BlockIoProtocol->Media->BlockSize);
176   DataPtr = AllocatePages (NumPages);
177   if (DataPtr == NULL) {
178     return EFI_BUFFER_TOO_SMALL;
179   }
180   Status = BlockIoProtocol->ReadBlocks (
181                               BlockIoProtocol,
182                               BlockIoProtocol->Media->MediaId,
183                               Lba,
184                               BlockIoProtocol->Media->BlockSize,
185                               DataPtr
186                               );
187   if (EFI_ERROR (Status)) {
188     DEBUG ((DEBUG_WARN, "Warning: Failed on reading blocks\n"));
189     goto Exit;
190   }
191 
192   Seed = ArmGenericTimerGetSystemCount ();
193   RandomSN = (RANDOM_SERIAL_NUMBER *)DataPtr;
194   if (RandomSN->Magic == RANDOM_MAGIC) {
195     Found = TRUE;
196     // Verify the unicode string.
197     ZeroMem (UnicodeStr, SERIAL_NUMBER_SIZE * sizeof (CHAR16));
198     UnicodeSPrint (UnicodeStr, SERIAL_NUMBER_SIZE * sizeof (CHAR16), L"%lx", RandomSN->Data);
199     if (StrLen (RandomSN->UnicodeSN) != StrLen (UnicodeStr)) {
200       Found = FALSE;
201     }
202     if (StrnCmp (RandomSN->UnicodeSN, UnicodeStr, StrLen (UnicodeStr)) != 0) {
203       Found = FALSE;
204     }
205   }
206   if (Found == FALSE) {
207     Status = GenerateUsbSNBySeed (Seed, RandomSN);
208     if (EFI_ERROR (Status)) {
209       DEBUG ((DEBUG_WARN, "Warning: Failed to generate serial number\n"));
210       goto Exit;
211     }
212     // Update SN to block device
213     Status = BlockIoProtocol->WriteBlocks (
214                                 BlockIoProtocol,
215                                 BlockIoProtocol->Media->MediaId,
216                                 Lba,
217                                 BlockIoProtocol->Media->BlockSize,
218                                 DataPtr
219                                 );
220     if (EFI_ERROR (Status)) {
221       DEBUG ((DEBUG_WARN, "Warning: Failed on writing blocks\n"));
222       goto Exit;
223     }
224   }
225   CopyMem (UnicodeSN, RandomSN->UnicodeSN, SERIAL_NUMBER_SIZE * sizeof (CHAR16));
226 Exit:
227   FreePages (DataPtr, NumPages);
228   return Status;
229 }
230 
231 EFI_STATUS
StoreSNToBlock(IN EFI_HANDLE FlashHandle,IN EFI_LBA Lba,IN CHAR16 * UnicodeSN)232 StoreSNToBlock (
233   IN EFI_HANDLE               FlashHandle,
234   IN EFI_LBA                  Lba,
235   IN CHAR16                  *UnicodeSN
236   )
237 {
238   EFI_STATUS                  Status;
239   EFI_BLOCK_IO_PROTOCOL      *BlockIoProtocol;
240   VOID                       *DataPtr;
241   UINTN                       NumPages;
242   RANDOM_SERIAL_NUMBER       *RandomSN;
243   CHAR16                      UnicodeStr[SERIAL_NUMBER_SIZE];
244 
245   if (UnicodeSN == NULL) {
246 DEBUG ((DEBUG_ERROR, "#%a, %d\n", __func__, __LINE__));
247     return EFI_INVALID_PARAMETER;
248   }
249   Status = gBS->OpenProtocol (
250                   FlashHandle,
251                   &gEfiBlockIoProtocolGuid,
252                   (VOID **) &BlockIoProtocol,
253                   gImageHandle,
254                   NULL,
255                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
256                   );
257   if (EFI_ERROR (Status)) {
258     DEBUG ((DEBUG_WARN, "Warning: Couldn't open block device (status: %r)\n", Status));
259     return EFI_DEVICE_ERROR;
260   }
261   NumPages = EFI_SIZE_TO_PAGES (BlockIoProtocol->Media->BlockSize);
262   DataPtr = AllocatePages (NumPages);
263   if (DataPtr == NULL) {
264     return EFI_BUFFER_TOO_SMALL;
265   }
266   ZeroMem (DataPtr, BlockIoProtocol->Media->BlockSize);
267   RandomSN = (RANDOM_SERIAL_NUMBER *)DataPtr;
268   RandomSN->Magic = RANDOM_MAGIC;
269   StrnCpyS (RandomSN->UnicodeSN, SERIAL_NUMBER_SIZE * sizeof (CHAR16), UnicodeSN, StrSize (UnicodeSN));
270   RandomSN->Data = StrHexToUint64 (RandomSN->UnicodeSN);
271 
272   // Verify the unicode string.
273   ZeroMem (UnicodeStr, SERIAL_NUMBER_SIZE * sizeof (CHAR16));
274   UnicodeSPrint (UnicodeStr, SERIAL_NUMBER_SIZE * sizeof (CHAR16), L"%lx", RandomSN->Data);
275   if (StrLen (RandomSN->UnicodeSN) != StrLen (UnicodeStr)) {
276 DEBUG ((DEBUG_ERROR, "#%a, %d, strlen:%d, %d\n", __func__, __LINE__, StrLen (RandomSN->UnicodeSN), StrLen (UnicodeStr)));
277     Status = EFI_INVALID_PARAMETER;
278     goto Exit;
279   }
280   if (StrnCmp (RandomSN->UnicodeSN, UnicodeStr, StrLen (UnicodeStr)) != 0) {
281 DEBUG ((DEBUG_ERROR, "#%a, %d, %s, %s\n", __func__, __LINE__, RandomSN->UnicodeSN, UnicodeStr));
282     Status = EFI_INVALID_PARAMETER;
283     goto Exit;
284   }
285 
286   Status = BlockIoProtocol->WriteBlocks (
287                               BlockIoProtocol,
288                               BlockIoProtocol->Media->MediaId,
289                               Lba,
290                               BlockIoProtocol->Media->BlockSize,
291                               DataPtr
292                               );
293   if (EFI_ERROR (Status)) {
294     DEBUG ((DEBUG_WARN, "Warning: Failed on writing blocks\n"));
295     goto Exit;
296   }
297 Exit:
298   FreePages (DataPtr, NumPages);
299   return Status;
300 }
301