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