Example

Linux Drivers

Release Date
2023-07-22

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; }