blob: 550a9735094478fc9109da2a2a9ce99eb13594e3 [file] [log] [blame]
/** @file
Copyright (c) 2017, Linaro. All rights reserved.
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include <Uefi.h>
#include <Library/ArmGenericTimerCounterLib.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/DevicePathLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/PrintLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UsbSerialNumberLib.h>
#include <Protocol/BlockIo.h>
#include <Protocol/DevicePath.h>
#define SERIAL_NUMBER_LEN 16
#define SERIAL_NUMBER_SIZE 17
#define RANDOM_MAX 0x7FFFFFFFFFFFFFFF
#define RANDOM_MAGIC 0x9A4DBEAF
STATIC
EFI_STATUS
GenerateRandomData (
IN UINT32 Seed,
OUT UINT64 *RandomData
)
{
INT64 Quotient, Remainder, Tmp;
if (RandomData == NULL) {
return EFI_INVALID_PARAMETER;
}
Quotient = (INT64) Seed / 127773;
Remainder = (INT64) Seed % 127773;
Tmp = (16807 * Remainder) - (2836 * Quotient);
if (Tmp < 0) {
Tmp += RANDOM_MAX;
}
Tmp = Tmp % ((UINT64)RANDOM_MAX + 1);
*RandomData = (UINT64)Tmp;
return EFI_SUCCESS;
}
EFI_STATUS
GenerateUsbSNBySeed (
IN UINT32 Seed,
OUT RANDOM_SERIAL_NUMBER *RandomSN
)
{
EFI_STATUS Status;
UINT64 Tmp;
if (RandomSN == NULL) {
return EFI_INVALID_PARAMETER;
}
ZeroMem (RandomSN, sizeof (RANDOM_SERIAL_NUMBER));
Status = GenerateRandomData (Seed, &Tmp);
if (EFI_ERROR (Status)) {
return Status;
}
RandomSN->Data = (Tmp << 32) | Seed;
UnicodeSPrint (RandomSN->UnicodeSN, SERIAL_NUMBER_SIZE * sizeof (CHAR16), L"%lx", RandomSN->Data);
RandomSN->Magic = RANDOM_MAGIC;
return EFI_SUCCESS;
}
EFI_STATUS
GenerateUsbSN (
OUT CHAR16 *UnicodeSN
)
{
EFI_STATUS Status;
UINT64 Tmp;
UINT32 Seed;
RANDOM_SERIAL_NUMBER RandomSN;
if (UnicodeSN == NULL) {
return EFI_INVALID_PARAMETER;
}
ZeroMem (&RandomSN, sizeof (RANDOM_SERIAL_NUMBER));
Seed = ArmGenericTimerGetSystemCount ();
Status = GenerateRandomData (Seed, &Tmp);
if (EFI_ERROR (Status)) {
return Status;
}
RandomSN.Data = (Tmp << 32) | Seed;
UnicodeSPrint (RandomSN.UnicodeSN, SERIAL_NUMBER_SIZE * sizeof (CHAR16), L"%lx", RandomSN.Data);
StrCpyS (UnicodeSN, SERIAL_NUMBER_SIZE * sizeof (CHAR16), RandomSN.UnicodeSN);
return EFI_SUCCESS;
}
EFI_STATUS
AssignUsbSN (
IN CHAR8 *AsciiCmd,
OUT CHAR16 *UnicodeSN
)
{
CHAR8 Data;
UINTN Index;
RANDOM_SERIAL_NUMBER RandomSN;
if ((AsciiCmd == NULL) || (UnicodeSN == NULL)) {
return EFI_INVALID_PARAMETER;
}
for (Index = 0; Index < SERIAL_NUMBER_LEN; Index++) {
Data = *(AsciiCmd + Index);
if (((Data >= '0') && (Data <= '9')) ||
((Data >= 'A') && (Data <= 'F'))) {
continue;
}
// Always use with upper case
if ((Data >= 'a') && (Data <= 'f')) {
*(AsciiCmd + Index) = Data - 'a' + 'A';
continue;
}
if (Data == '\0') {
break;
}
return EFI_INVALID_PARAMETER;
}
ZeroMem (&RandomSN, sizeof (RANDOM_SERIAL_NUMBER));
AsciiStrToUnicodeStr (AsciiCmd, RandomSN.UnicodeSN);
StrCpyS (UnicodeSN, SERIAL_NUMBER_SIZE * sizeof (CHAR16), RandomSN.UnicodeSN);
return EFI_SUCCESS;
}
EFI_STATUS
LoadSNFromBlock (
IN EFI_HANDLE FlashHandle,
IN EFI_LBA Lba,
OUT CHAR16 *UnicodeSN
)
{
EFI_STATUS Status;
EFI_BLOCK_IO_PROTOCOL *BlockIoProtocol;
VOID *DataPtr;
BOOLEAN Found = FALSE;
UINT32 Seed;
RANDOM_SERIAL_NUMBER *RandomSN;
UINTN NumPages;
CHAR16 UnicodeStr[SERIAL_NUMBER_SIZE];
if (UnicodeSN == NULL) {
return EFI_INVALID_PARAMETER;
}
Status = gBS->OpenProtocol (
FlashHandle,
&gEfiBlockIoProtocolGuid,
(VOID **) &BlockIoProtocol,
gImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "Warning: Couldn't open block device (status: %r)\n", Status));
return EFI_DEVICE_ERROR;
}
NumPages = EFI_SIZE_TO_PAGES (BlockIoProtocol->Media->BlockSize);
DataPtr = AllocatePages (NumPages);
if (DataPtr == NULL) {
return EFI_BUFFER_TOO_SMALL;
}
Status = BlockIoProtocol->ReadBlocks (
BlockIoProtocol,
BlockIoProtocol->Media->MediaId,
Lba,
BlockIoProtocol->Media->BlockSize,
DataPtr
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "Warning: Failed on reading blocks\n"));
goto Exit;
}
Seed = ArmGenericTimerGetSystemCount ();
RandomSN = (RANDOM_SERIAL_NUMBER *)DataPtr;
if (RandomSN->Magic == RANDOM_MAGIC) {
Found = TRUE;
// Verify the unicode string.
ZeroMem (UnicodeStr, SERIAL_NUMBER_SIZE * sizeof (CHAR16));
UnicodeSPrint (UnicodeStr, SERIAL_NUMBER_SIZE * sizeof (CHAR16), L"%lx", RandomSN->Data);
if (StrLen (RandomSN->UnicodeSN) != StrLen (UnicodeStr)) {
Found = FALSE;
}
if (StrnCmp (RandomSN->UnicodeSN, UnicodeStr, StrLen (UnicodeStr)) != 0) {
Found = FALSE;
}
}
if (Found == FALSE) {
Status = GenerateUsbSNBySeed (Seed, RandomSN);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "Warning: Failed to generate serial number\n"));
goto Exit;
}
// Update SN to block device
Status = BlockIoProtocol->WriteBlocks (
BlockIoProtocol,
BlockIoProtocol->Media->MediaId,
Lba,
BlockIoProtocol->Media->BlockSize,
DataPtr
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "Warning: Failed on writing blocks\n"));
goto Exit;
}
}
CopyMem (UnicodeSN, RandomSN->UnicodeSN, SERIAL_NUMBER_SIZE * sizeof (CHAR16));
Exit:
FreePages (DataPtr, NumPages);
return Status;
}
EFI_STATUS
StoreSNToBlock (
IN EFI_HANDLE FlashHandle,
IN EFI_LBA Lba,
IN CHAR16 *UnicodeSN
)
{
EFI_STATUS Status;
EFI_BLOCK_IO_PROTOCOL *BlockIoProtocol;
VOID *DataPtr;
UINTN NumPages;
RANDOM_SERIAL_NUMBER *RandomSN;
CHAR16 UnicodeStr[SERIAL_NUMBER_SIZE];
if (UnicodeSN == NULL) {
DEBUG ((DEBUG_ERROR, "#%a, %d\n", __func__, __LINE__));
return EFI_INVALID_PARAMETER;
}
Status = gBS->OpenProtocol (
FlashHandle,
&gEfiBlockIoProtocolGuid,
(VOID **) &BlockIoProtocol,
gImageHandle,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "Warning: Couldn't open block device (status: %r)\n", Status));
return EFI_DEVICE_ERROR;
}
NumPages = EFI_SIZE_TO_PAGES (BlockIoProtocol->Media->BlockSize);
DataPtr = AllocatePages (NumPages);
if (DataPtr == NULL) {
return EFI_BUFFER_TOO_SMALL;
}
ZeroMem (DataPtr, BlockIoProtocol->Media->BlockSize);
RandomSN = (RANDOM_SERIAL_NUMBER *)DataPtr;
RandomSN->Magic = RANDOM_MAGIC;
StrnCpyS (RandomSN->UnicodeSN, SERIAL_NUMBER_SIZE * sizeof (CHAR16), UnicodeSN, StrSize (UnicodeSN));
RandomSN->Data = StrHexToUint64 (RandomSN->UnicodeSN);
// Verify the unicode string.
ZeroMem (UnicodeStr, SERIAL_NUMBER_SIZE * sizeof (CHAR16));
UnicodeSPrint (UnicodeStr, SERIAL_NUMBER_SIZE * sizeof (CHAR16), L"%lx", RandomSN->Data);
if (StrLen (RandomSN->UnicodeSN) != StrLen (UnicodeStr)) {
DEBUG ((DEBUG_ERROR, "#%a, %d, strlen:%d, %d\n", __func__, __LINE__, StrLen (RandomSN->UnicodeSN), StrLen (UnicodeStr)));
Status = EFI_INVALID_PARAMETER;
goto Exit;
}
if (StrnCmp (RandomSN->UnicodeSN, UnicodeStr, StrLen (UnicodeStr)) != 0) {
DEBUG ((DEBUG_ERROR, "#%a, %d, %s, %s\n", __func__, __LINE__, RandomSN->UnicodeSN, UnicodeStr));
Status = EFI_INVALID_PARAMETER;
goto Exit;
}
Status = BlockIoProtocol->WriteBlocks (
BlockIoProtocol,
BlockIoProtocol->Media->MediaId,
Lba,
BlockIoProtocol->Media->BlockSize,
DataPtr
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "Warning: Failed on writing blocks\n"));
goto Exit;
}
Exit:
FreePages (DataPtr, NumPages);
return Status;
}