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