Once booted into Linux, to read/write particular field please do read/write from/to "/sys/bus/nvmem/devices/xilinx-secure-config0/nvmem" to the particular offset with the corresponding size.
A Linux application can be used to read/write into BBRAM/volatile user keys from Linux.
Copy below code in a .c file say test_example.c and compile it with aarch64-linux-gnu-gcc compiler.
After booting till Linux, below commands can be used to read/write into BBRAM/volatile user keys.
For help : ./test_example --help
For reading from offset : ./test_example --read <offset in hex>
For writing into offset : ./test_example --write <offset in hex> <value in hex>
/******************************************************************************
* Copyright (c) 2021 Xilinx, Inc. All rights reserved.
* SPDX-License-Identifier: MIT
******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <getopt.h>
#include <errno.h>
#include <stdbool.h>
typedef struct {
u_int32_t offset;
u_int32_t size;
}SecCfgLookupTable;
/* Xilinx error codes */
#define RD_FAILED 1026
#define WR_FAILED 1027
#define SYS_PATH "/sys/bus/nvmem/devices/xilinx-secure-config0/nvmem"
#define MAX_ROWS 12
static void print_help();
static u_int32_t get_length(u_int32_t offset);
static u_int32_t remove_initial_0x(char *str);
static u_int32_t validate_offset(char *str);
static int32_t read_sec_cfg(int fd, u_int32_t offset);
static int32_t write_sec_cfg(int fd, u_int32_t offset, char* value, u_int32_t val_len);
int main(int argc, char* argv[])
{
int fd;
u_int32_t offset = 0;
u_int32_t bytes = 0;
int32_t readflag = 0;
int32_t writeflag = 0;
int32_t helpflag = 0;
char* value = NULL;
int32_t c;
int32_t long_index = 0;
int32_t status;
static struct option long_options[] = {
{"help", no_argument, 0, 'h' },
{"read", no_argument, 0, 'r' },
{"write", no_argument, 0, 'w' },
{0, 0, 0, 0 }
};
while ((c = getopt_long(argc, argv, "hrw", long_options, &long_index)) != -1) {
switch (c) {
case 'h':
helpflag++;
break;
case 'r':
readflag++;
break;
case 'w':
writeflag++;
break;
default:
print_help();
abort ();
break;
}
}
if (((readflag + writeflag + helpflag) > 1) ||
(readflag == true && argc != 3) ||
(writeflag == true && argc != 4)) {
fprintf (stderr, "Invalid syntax\n");
print_help();
return EINVAL;
}
if (helpflag == true) {
print_help();
return 0;
}
fd = open(SYS_PATH, O_RDWR);
if(fd <= 0) {
printf("Opening SYS FS NVMEM file is failed\n");
return errno;
}
if (readflag == true) {
status = validate_offset(argv[2]);
if (status != 0) {
return status;
}
offset = strtoul(argv[2], NULL, 16);
status = read_sec_cfg(fd, offset);
return status;
}
if (writeflag == true) {
status = validate_offset(argv[2]);
if (status != 0) {
return status;
}
offset = strtoul(argv[2], NULL, 16);
value = argv[3];
u_int32_t length = remove_initial_0x(value);
status = write_sec_cfg(fd, offset, value, length);
return status;
}
close(fd);
return 0;
}
/*
* Prints help on the syntax and supported arguments.
* Called if --help is provided as argument or in case of invalid syntax
*/
static void print_help()
{
printf("Usage: \r\n");
printf("Syntax : \r\n");
printf("Read from Secure configuration: \r\n ./sec_cfg_access --read "
"<Offset in hex>\r\n");
printf("Write into Secure configuration: \r\n ./sec_cfg_access --write "
"<Offset in hex> <Value in hex>\r\n");
printf("\r\n");
printf("Arguments : \r\n");
printf("-h --help \t Prints help\r\n");
printf("-r --read \t Read from Secure configuration\r\n");
printf("-w --write \t Write into Secure configuration\r\n");
printf("For more details please refer -"
"https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/"
"2176221192/Xilinx+Secure+Configuration+Linux+Driver");
}
/*
* Returns the supported length of the efuse in bytes as per the provided offset
* In case of invalid offset, returns 0xFF.
*/
static u_int32_t get_length(u_int32_t offset)
{
const SecCfgLookupTable SecCfg[MAX_ROWS] = {
/*+-----+-----+
*|Offset| Size|
*+------+-----+
*/
{0x4, 0x4}, /* BBRAM Zeroize */
{0x10, 0x20}, /* BBRAM_Key */
{0x30, 0x4}, /* BBRAM user data */
{0x48, 0x4}, /* BBRAM Lock */
{0x110, 0x20}, /* User Key0 */
{0x130, 0x20}, /* User Key1 */
{0x150, 0x20}, /* User Key2 */
{0x170, 0x20}, /* User Key3 */
{0x190, 0x20}, /* User Key4 */
{0x1B0, 0x20}, /* User Key5 */
{0x1D0, 0x20}, /* User Key6 */
{0x1F0, 0x20}, /* User Key7 */
};
u_int32_t size = 0xFF;
int32_t index;
for(index = 0; index < MAX_ROWS; index++) {
if (SecCfg[index].offset == offset) {
size = SecCfg[index].size;
break;
}
}
return size;
}
/*
* Removes 0x or 0X from starting of the string
* eg : 0x1234 -> 1234
* Returns length of the updated string
*/
static u_int32_t remove_initial_0x(char *str)
{
int32_t index;
int32_t n = strnlen(str, 64);
if ((*str == '0') && (*(str + 1) == 'x' || *(str + 1) == 'X')) {
strcpy(str, &str[2]);
}
return strnlen(str, 64);
}
/*
* Validates offset
*/
static u_int32_t validate_offset(char *str)
{
u_int32_t index = 0;
u_int32_t modified_len = remove_initial_0x(str);
if (modified_len > 3) {
return EINVAL;
}
for (index = 0; str[index] != '\0'; index++) {
if ((str[index] < '0' || str[index] > '9') &&
(str[index] < 'A' || str[index] > 'F') &&
(str[index] < 'a' || str[index] > 'f')) {
return EINVAL;
}
}
return 0;
}
/*
* Reads eFUSE values from the offset
*/
static int32_t read_sec_cfg(int fd, u_int32_t offset)
{
u_int32_t length = get_length(offset);
ssize_t size;
u_int32_t read_data[50] = {0};
int32_t index;
if (length == 0xFF) {
printf("Invalid offset\n\r");
return EINVAL;
}
if (offset != 0x30) {
printf("Read is not allowed for this offset\n\r");
return EINVAL;
}
size = pread(fd, (void *)&read_data, length, offset);
if (size == length) {
for (index = (size/4)-1; index >= 0; index--) {
printf("%x ", read_data[index]);
}
printf("\n\r");
}
else {
printf("size != length\n\r");
return RD_FAILED;
}
return 0;
}
/*
* Writes user provided value in the eFUSE at the given offset
*/
static int32_t write_sec_cfg(int fd, u_int32_t offset, char* value, u_int32_t val_len)
{
u_int32_t length = get_length(offset);
ssize_t size;
unsigned char write_data[32] = {0};
int32_t status;
int32_t index;
u_int32_t converted_value;
if (length == 0xFF) {
printf("Invalid offset\n\r");
return EINVAL;
}
if (val_len > (length*2)) {
printf("Length of provided value is longer than expected\n\r");
return EINVAL;
}
if(offset == 0x4 || offset == 0x30 || offset == 0x48) {
converted_value = strtoul(value, NULL, 16);
size = pwrite(fd, &converted_value, length, offset);
}
else {
size = pwrite(fd, value, val_len, offset);
}
if (size == val_len) {
printf("Data written at offset = %x of size = %d bytes\n\r",
offset, size);
}
else {
return WR_FAILED;
}
return 0;
}