Xilinx Secure Configuration Linux Driver
This page gives an overview of the xlnx_secure_config driver which is available as part of the Versal Linux distribution. Paths, files, links and documentation on this page are given relative to the Linux kernel source tree.
Table of Contents
Features supported in driver
Access to BBRAM and volatile user keys
Missing Features, Known Issues and Limitations
None
Kernel configuration
To ensure that the driver is included in the kernel image, make sure to enable Xilinx Secure Configuration nvmem firmware support in the Kernel configuration. This configuration is available under :
Device Drivers---> NVMEM Support---> <*>Xilinx Secure Configuration nvmem firmware support
PLM Configuration
To access BBRAM , PLM should be built by commenting #define PLM_NVM_EXCLUDE in xplmi_config.h. This will enable the NVM code in PLM. (By default it is disabled).
Device tree
Below device tree mentions the offset and size of the different registers which can be accessed using the Xilinx Secure Configuration driver:
versal_sec_cfg: versal-sec-cfg {
compatible = "xlnx,versal-sec-cfg";
#address-cells = <1>;
#size-cells = <1>;
bbram_zeroize: bbram-zeroize@4 {
reg = <0x04 0x4>;
};
bbram_key: bbram-key@10 {
reg = <0x10 0x20>;
};
bbram_usr: bbram-usr@30 {
reg = <0x30 0x4>;
};
bbram_lock: bbram-lock@48 {
reg = <0x48 0x4>;
};
user_key0: user-key0@110 {
reg = <0x110 0x20>;
};
user_key1: user-key1@130 {
reg = <0x130 0x20>;
};
user_key2: user-key2@150 {
reg = <0x150 0x20>;
};
user_key3: user-key3@170 {
reg = <0x170 0x20>;
};
user_key4: user-key4@190 {
reg = <0x190 0x20>;
};
user_key5: user-key5@1B0 {
reg = <0x1B0 0x20>;
};
user_key6: user-key6@1D0 {
reg = <0x1D0 0x20>;
};
user_key7: user-key7@1F0 {
reg = <0x1F0 0x20>;
};
};
The following table gives the overview of possible addresses for read/write and with the sizes.
Register | Read | Write | Size in bytes | Offset is |
BBRAM Zeroize | NO | YES | 0x4 | 0x4 |
BBRAM Key | NO | YES | 0x20 | 0x10 |
BBRAM User data | YES | YES | 0x4 | 0x30 |
BBRAM Lock | NO | YES | 0x4 | 0x48 |
User Key0 | NO | YES | 0x20 | 0x110 |
User Key1 | NO | YES | 0x20 | 0x130 |
User Key2 | NO | YES | 0x20 | 0x150 |
User Key3 | NO | YES | 0x20 | 0x170 |
User Key4 | NO | YES | 0x20 | 0x190 |
User Key5 | NO | YES | 0x20 | 0x1B0 |
User Key6 | NO | YES | 0x20 | 0x1D0 |
User Key7 | NO | YES | 0x20 | 0x1F0 |
Link to device tree binding file : https://github.com/Xilinx/linux-xlnx/blob/master/Documentation/devicetree/bindings/nvmem/xlnx,versal-sec-cfg.yaml
Example
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;
}
Expected Output
#Write BBRAM User data
root@xilinx-vck190-2021_2:~# ./test_example -w 0x30 12121212
#Read BBRAM user data
root@xilinx-vck190-2021_2:~# ./test_example -r 0x30
12121212
#Lock BBRAM user data for write
root@xilinx-vck190-2021_2:~# ./test_example -w 0x48 12345678
#Write BBRAM User data after locking
root@xilinx-vck190-2021_2:~# ./test_example -w 0x30 12121213
[1632957.443]XPlmi_IpiDispatchHandler: Error: Unhandled IPI received
[1632960.624]PLM Error Status: 0x2B038105
#Zeroize BBRAM AES key
root@xilinx-vck190-2021_2:~# ./test_example -w 0x4 0x87654321
#Write BBRAM AES Key
root@xilinx-vck190-2021_2:~# ./test_example -w 0x10 2EC3072B2DA1554374D03004E53D1A7A3F364AA7DB22A0F62FA1192C72B2A0E1
Data written at offset = 10 of size = 64 bytes
#Write User Key0
root@xilinx-vck190-2021_2:~# ./test_example -w 0x110 2EC3072B2DA1554374D03004E53D1A7A3F364AA7DB22A0F62FA1192C72B2A0E1
Data written at offset = 10 of size = 64 bytes
#Write User Key1
root@xilinx-vck190-2021_2:~# ./test_example -w 0x130 2EC3072B2DA1554374D03004E53D1A7A3F364AA7DB22A0F62FA1192C72B2A0E1
Data written at offset = 10 of size = 64 bytes
#Write User Key2
root@xilinx-vck190-2021_2:~# ./test_example -w 0x150 2EC3072B2DA1554374D03004E53D1A7A3F364AA7DB22A0F62FA1192C72B2A0E1
Data written at offset = 10 of size = 64 bytes
#Write User Key3
root@xilinx-vck190-2021_2:~# ./test_example -w 0x170 2EC3072B2DA1554374D03004E53D1A7A3F364AA7DB22A0F62FA1192C72B2A0E1
Data written at offset = 10 of size = 64 bytes
#Write User Key4
root@xilinx-vck190-2021_2:~# ./test_example -w 0x190 2EC3072B2DA1554374D03004E53D1A7A3F364AA7DB22A0F62FA1192C72B2A0E1
Data written at offset = 10 of size = 64 bytes
#Write User Key5
root@xilinx-vck190-2021_2:~# ./test_example -w 0x1B0 2EC3072B2DA1554374D03004E53D1A7A3F364AA7DB22A0F62FA1192C72B2A0E1
Data written at offset = 10 of size = 64 bytes
#Write User Key6
root@xilinx-vck190-2021_2:~# ./test_example -w 0x1D0 2EC3072B2DA1554374D03004E53D1A7A3F364AA7DB22A0F62FA1192C72B2A0E1
Data written at offset = 10 of size = 64 bytes
#Write User Key7
root@xilinx-vck190-2021_2:~# ./test_example -w 0x1F0 2EC3072B2DA1554374D03004E53D1A7A3F364AA7DB22A0F62FA1192C72B2A0E1
Data written at offset = 10 of size = 64 bytes
Mainline status
The driver is not available at Mainline
Changelog
2021.1
Commits:
https://github.com/Xilinx/linux-xlnx/commit/49c96e3e6eff4a66915479a3fda37a01450c9fc3
https://github.com/Xilinx/linux-xlnx/commit/42156394eb2c159b7f3ced7d942a38022d595858
https://github.com/Xilinx/linux-xlnx/commit/7bcdd6e548002a4f161cc7444804f52e0ca7522e
Related Links
https://github.com/Xilinx/linux-xlnx/blob/master/drivers/nvmem/xlnx_secure_config.c
© Copyright 2019 - 2022 Xilinx Inc. Privacy Policy