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
- 1 Features supported in driver
- 2 Missing Features, Known Issues and Limitations
- 3 Kernel configuration
- 4 PLM Configuration
- 5 Device tree
- 6 Example
- 7 Expected Output
- 8 Linux application to program PUFHD, generate and program black key and PPK hash
- 9 Expected Output
- 10 Mainline status
- 11 Changelog
- 12 Related Links
Features supported in driver
Access to BBRAM, volatile user keys and eFuse.
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 and EFUSE , 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).
To write the specific eFuse, the below varibles should be enabled in PLM according to the requirement.
xnvm_en_write_sec_crit_efuse - For Security Critical eFuses
xnvm_en_write_user_efuse - For User eFuses
xnvm_en_write_key_management_efuse - For key management eFuses
For both eFuse read and write, cache offset have to be provided by the user, where the offset is defined as follows:
1.offset is of 32 bit, where lower 16 bits indicate offset value of the corresponding memory device
2.Bit 17th of the offset is used to identify, which memory device should be programmed; To program eFuse, 17th bit of the offset should be set as 1, for BBRAM should be set to 0.
3.For eFuse write, user has the choice to enable or disable the environment disable variable, by setting the 18th bit of the offset to 1 or 0 respectively.
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>;
};
metaheader_iv: metaheader_iv@10180 {
reg = <0x10180 0x0c>;
};
plm_iv: plm_iv@101dc {
reg = <0x101dc 0x0c>;
};
black_iv: black_iv@101d0 {
reg = <0x101d0 0x0c>;
};
datapartition_iv: datapartition_iv@101e8 {
reg = <0x101e8 0x0c>;
};
security_misc: security_misc@100e8 {
reg = <0x100e8 0x4>;
};
user_1: user_1@10208 {
reg = <0x10208 0x4>;
};
user_2: user_2@1020c {
reg = <0x1020c 0x4>;
};
user_3: user_3@10210 {
reg = <0x10210 0x4>;
};
user_4: user_4@10214 {
reg = <0x10214 0x4>;
};
user_5: user_5@10218 {
reg = <0x10218 0x4>;
};
user_6: user_6@1021c {
reg = <0x1021c 0x4>;
};
user_7: user_7@10220 {
reg = <0x10220 0x4>;
};
user_8: user_8@10224 {
reg = <0x10224 0x4>;
};
user_9: user_9@10228 {
reg = <0x10228 0x4>;
};
user_10: user_10@1022c {
reg = <0x1022c 0x4>;
};
user_11: user_11@10230 {
reg = <0x10230 0x4>;
};
user_12: user_12@10234 {
reg = <0x10234 0x4>;
};
user_13: user_13@10238 {
reg = <0x10238 0x4>;
};
user_14: user_14@1023c {
reg = <0x1023c 0x4>;
};
user_15: user_15@10240 {
reg = <0x10240 0x4>;
};
user_16: user_16@10244 {
reg = <0x10244 0x4>;
};
user_17: user_17@10248 {
reg = <0x10248 0x4>;
};
user_18: user_18@1024c {
reg = <0x1024c 0x4>;
};
user_19: user_19@10250 {
reg = <0x10250 0x4>;
};
user_20: user_20@10254 {
reg = <0x10254 0x4>;
};
user_21: user_21@10258 {
reg = <0x10258 0x4>;
};
user_22: user_22@1025c {
reg = <0x1025c 0x4>;
};
user_23: user_23@10260 {
reg = <0x10260 0x4>;
};
user_24: user_24@10264 {
reg = <0x10264 0x4>;
};
user_25: user_25@10268 {
reg = <0x10268 0x4>;
};
user_26: user_26@1026c {
reg = <0x1026c 0x4>;
};
user_27: user_27@10270 {
reg = <0x10270 0x4>;
};
user_28: user_28@10274 {
reg = <0x10274 0x4>;
};
user_29: user_29@10278 {
reg = <0x10278 0x4>;
};
user_30: user_30@1027c {
reg = <0x1027c 0x4>;
};
user_31: user_31@10280 {
reg = <0x10280 0x4>;
};
user_32: user_32@10284 {
reg = <0x10284 0x4>;
};
user_33: user_33@10288 {
reg = <0x10288 0x4>;
};
user_34: user_34@1028c {
reg = <0x1028c 0x4>;
};
user_35: user_35@10290 {
reg = <0x10290 0x4>;
};
user_36: user_36@10294 {
reg = <0x10294 0x4>;
};
user_37: user_37@10298 {
reg = <0x10298 0x4>;
};
user_38: user_38@1029c {
reg = <0x1029c 0x4>;
};
user_39: user_39@102a0 {
reg = <0x102a0 0x4>;
};
user_40: user_40@102a4 {
reg = <0x102a4 0x4>;
};
user_41: user_41@102a8 {
reg = <0x102a8 0x4>;
};
user_42: user_42@102ac {
reg = <0x102ac 0x4>;
};
user_43: user_43@102b0 {
reg = <0x102b0 0x4>;
};
user_44: user_44@102b4 {
reg = <0x102b4 0x4>;
};
user_45: user_45@102b8 {
reg = <0x102b8 0x4>;
};
user_46: user_46@102bc {
reg = <0x102bc 0x4>;
};
user_47: user_47@102c0 {
reg = <0x102c0 0x4>;
};
user_48: user_48@102c4 {
reg = <0x102c4 0x4>;
};
user_49: user_49@102c8 {
reg = <0x102c8 0x4>;
};
user_50: user_50@102cc {
reg = <0x102cc 0x4>;
};
user_51: user_51@102d0 {
reg = <0x102d0 0x4>;
};
user_52: user_52@102d4 {
reg = <0x102d4 0x4>;
};
user_53: user_53@102d8 {
reg = <0x102d8 0x4>;
};
user_54: user_54@102dc {
reg = <0x102dc 0x4>;
};
user_55: user_55@102e0 {
reg = <0x102e0 0x4>;
};
user_56: user_56@102e4 {
reg = <0x102e4 0x4>;
};
user_57: user_57@102e8 {
reg = <0x102e8 0x4>;
};
user_58: user_58@102ec {
reg = <0x102ec 0x4>;
};
user_59: user_59@102f0 {
reg = <0x102f0 0x4>;
};
user_60: user_60@102f4 {
reg = <0x102f4 0x4>;
};
user_61: user_61@102f8 {
reg = <0x102f8 0x4>;
};
user_62: user_62@102fc {
reg = <0x102fc 0x4>;
};
user_63: user_63@10300 {
reg = <0x10300 0x4>;
};
aux: aux@100a4 {
reg = <0x100a4 0x4>;
};
chash: chash@100a8 {
reg = <0x100a8 0x4>;
};
revocationid_0: revocationid_0@100b0 {
reg = <0x100b0 0x4>;
};
revocationid_1: revocationid_1@100b4 {
reg = <0x100b4 0x4>;
};
revocationid_2: revocationid_2@100b8 {
reg = <0x100b8 0x4>;
};
revocationid_3: revocationid_3@100bc {
reg = <0x100bc 0x4>;
};
revocationid_4: revocationid_4@100c0 {
reg = <0x100c0 0x4>;
};
revocationid_5: revocationid_5@100c4 {
reg = <0x100c4 0x4>;
};
revocationid_6: revocationid_6@100c8 {
reg = <0x100c8 0x4>;
};
revocationid_7: revocationid_7@100cc {
reg = <0x100cc 0x4>;
};
offchip_revoke_0: offchip_revoke_0@10160 {
reg = <0x10160 0x4>;
};
offchip_revoke_1: offchip_revoke_1@10164 {
reg = <0x10164 0x4>;
};
offchip_revoke_2: offchip_revoke_2@10168 {
reg = <0x10168 0x4>;
};
offchip_revoke_3: offchip_revoke_3@1016c {
reg = <0x1016c 0x4>;
};
offchip_revoke_4: offchip_revoke_4@10170 {
reg = <0x10170 0x4>;
};
offchip_revoke_5: offchip_revoke_5@10174 {
reg = <0x10174 0x4>;
};
offchip_revoke_6: offchip_revoke_6@10178 {
reg = <0x10178 0x4>;
};
offchip_revoke_7: offchip_revoke_7@1017c {
reg = <0x1017c 0x4>;
};
security_control: security_control@100ac {
reg = <0x100ac 0x4>;
};
pufdata: pufdata@1ffff {
reg = <0x1ffff 0x408>;
};
puf_syndata: puf_syndata@10a04 {
reg = <0x10a04 0x1fc>;
};
ppk0_hash: ppk0_hash@10100 {
reg = <0x10100 0x20>;
};
ppk1_hash: ppk1_hash@10104 {
reg = <0x10104 0x20>;
};
ppk2_hash: ppk2_hash@10108 {
reg = <0x10108 0x20>;
};
anlg_trim3: anlg_trim3@10010 {
reg = <0x10010 0x4>;
};
boot_env_ctrl: boot_env_ctrl@10094 {
reg = <0x10094 0x4>;
};
misc_ctrl: misc_ctrl@100a0 {
reg = <0x100a0 0x4>;
};
security_misc0: security_misc0@100e4 {
reg = <0x100e4 0x4>;
};
dna_0: dna_0@10020 {
reg = <0x10020 0x4>;
};
dna_1: dna_1@10024 {
reg = <0x10024 0x4>;
};
dna_2: dna_2@10028 {
reg = <0x10028 0x4>;
};
dna_3: dna_3@1002c {
reg = <0x1002c 0x4>;
};
};
The following table gives the overview of possible addresses for read/write and with the sizes for BBRAM. For eFuses all the above mentioned eFuses has read and write permissions except dna which has only read permissions.
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 : linux-xlnx/Documentation/devicetree/bindings/nvmem/xlnx,versal-sec-cfg.yaml at master · Xilinx/linux-xlnx
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>
#include <sys/types.h>
#include <unistd.h>
typedef struct {
u_int64_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 120
#define BUFFER_SIZE 1137
static void print_help();
static u_int32_t get_length(u_int64_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_int64_t offset);
static int32_t write_sec_cfg(int fd, u_int64_t offset, char* value, u_int32_t val_len);
int main(int argc, char* argv[])
{
int fd;
u_int64_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;
FILE *file;
char buffer[BUFFER_SIZE];
char filename[100]; // Adjust the size according to your needs
size_t bytesRead;
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);
if(offset == 0x3FFFF) {
strcpy(filename,argv[3]);
file = fopen(filename, "r");
if (file == NULL) {
perror("Error opening file");
return 1;
}
bytesRead = fread(buffer, 1, BUFFER_SIZE - 1, file);
buffer[bytesRead] = '\0';
value = buffer;
fclose(file);
}
else {
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_int64_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 */
{0x101DC,0x0C}, /*PLM IV*/
{0x101D0,0x0C}, /*BLACK IV*/
{0x101E8,0x0C}, /*DATA PARTITION IV*/
{0x10180,0x0C}, /*METAHEADER IV*/
{0x100E8,0x4}, /*Security Misc_1*/
{0x10204,0x4}, /*USER_1*/
{0x10208,0x4}, /*USER_2*/
{0x1020C,0x4}, /*USER_3*/
{0x10210,0x4}, /*USER_4*/
{0x10214,0x4}, /*USER_5*/
{0x10218,0x4}, /*USER_6*/
{0x1021C,0x4}, /*USER_7*/
{0x10220,0x4}, /*USER_8*/
{0x10224,0x4}, /*USER_9*/
{0x10228,0x4}, /*USER_10*/
{0x1022C,0x4}, /*USER_11*/
{0x10230,0x4}, /*USER_12*/
{0x10234,0x4}, /*USER_13*/
{0x10238,0x4}, /*USER_14*/
{0x1023C,0x4}, /*USER_15*/
{0x10240,0x4}, /*USER_16*/
{0x10244,0x4}, /*USER_17*/
{0x10248,0x4}, /*USER_18*/
{0x1024C,0x4}, /*USER_19*/
{0x10250,0x4}, /*USER_20*/
{0x10254,0x4}, /*USER_21*/
{0x10258,0x4}, /*USER_22*/
{0x1025C,0x4}, /*USER_23*/
{0x10260,0x4}, /*USER_24*/
{0x10264,0x4}, /*USER_25*/
{0x10268,0x4}, /*USER_26*/
{0x1026C,0x4}, /*USER_27*/
{0x10270,0x4}, /*USER_28*/
{0x10274,0x4}, /*USER_29*/
{0x10278,0x4}, /*USER_30*/
{0x1027C,0x4}, /*USER_31*/
{0x10280,0x4}, /*USER_32*/
{0x10284,0x4}, /*USER_33*/
{0x10288,0x4}, /*USER_34*/
{0x1028C,0x4}, /*USER_35*/
{0x10290,0x4}, /*USER_36*/
{0x10294,0x4}, /*USER_37*/
{0x10298,0x4}, /*USER_38*/
{0x1029C,0x4}, /*USER_39*/
{0x102A0,0x4}, /*USER_40*/
{0x102A4,0x4}, /*USER_41*/
{0x102A8,0x4}, /*USER_42*/
{0x102AC,0x4}, /*USER_43*/
{0x102B0,0x4}, /*USER_44*/
{0x102B4,0x4}, /*USER_45*/
{0x102B8,0x4}, /*USER_46*/
{0x102BC,0x4}, /*USER_47*/
{0x102C0,0x4}, /*USER_48*/
{0x102C4,0x4}, /*USER_49*/
{0x102C8,0x4}, /*USER_50*/
{0x102CC,0x4}, /*USER_51*/
{0x102D0,0x4}, /*USER_52*/
{0x102D4,0x4}, /*USER_53*/
{0x102D8,0x4}, /*USER_54*/
{0x102DC,0x4}, /*USER_55*/
{0x102E0,0x4}, /*USER_56*/
{0x102E4,0x4}, /*USER_57*/
{0x102E8,0x4}, /*USER_58*/
{0x102EC,0x4}, /*USER_59*/
{0x102F0,0x4}, /*USER_60*/
{0x102F4,0x4}, /*USER_61*/
{0x102F8,0x4}, /*USER_62*/
{0x102FC,0x4}, /*USER_63*/
{0x100A4,0x4}, /*AUX*/
{0x100A8,0x4}, /*CHASH*/
{0x100B0,0x4}, /*REVOCATIONID_0*/
{0x100B4,0x4}, /*REVOCATIONID_1*/
{0x100B8,0x4}, /*REVOCATIONID_2*/
{0x100BC,0x4}, /*REVOCATIONID_3*/
{0x100C0,0x4}, /*REVOCATIONID_4*/
{0x100C4,0x4}, /*REVOCATIONID_5*/
{0x100C8,0x4}, /*REVOCATIONID_6*/
{0x100CC,0x4}, /*REVOCATIONID_7*/
{0x10160,0x4}, /*OFFCHIP_REVOKE_0*/
{0x10164,0x4}, /*OFFCHIP_REVOKE_1*/
{0x10168,0x4}, /*OFFCHIP_REVOKE_2*/
{0x1016C,0x4}, /*OFFCHIP_REVOKE_3*/
{0x10170,0x4}, /*OFFCHIP_REVOKE_4*/
{0x10174,0x4}, /*OFFCHIP_REVOKE_5*/
{0x10178,0x4}, /*OFFCHIP_REVOKE_6*/
{0x1017C,0x4}, /*OFFCHIP_REVOKE_7*/
{0x100AC,0x4}, /*SECURITY_CONTROL*/
{0x1FFFF,0x408}, /*PUF_DATA*/
{0x10A04,0x1FC}, /*PUF_SYNDATA*/
{0x10100,0x20}, /*PPK0_HASH*/
{0x10120,0x20}, /*PPK1_HASH*/
{0x10140,0x20}, /*PPK2_HASH*/
{0x10010,0x4}, /*ANLG_TRIM3*/
{0x10094,0x4}, /*BOOT_ENV_CTRL_OFFSET*/
{0x100A0,0x4}, /*MISC_CTRL_OFFSET*/
{0x100E4,0x4}, /*SECURITY_MISC0_OFFSET*/
{0x10020,0x4}, /*DNA_0*/
{0x10024,0x4}, /*DNA_1*/
{0x10028,0x4}, /*DNA_2*/
{0x1002C,0x4}, /*DNA_3*/
};
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, 1136);
if ((*str == '0') && (*(str + 1) == 'x' || *(str + 1) == 'X')) {
strcpy(str, &str[2]);
}
return strnlen(str, 1136);
}
/*
* 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 > 8) {
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_int64_t offset)
{
u_int32_t length = get_length(offset & ~(0x20000));
ssize_t size;
u_int32_t read_data[1121] = {0};
int32_t index;
printf("length calculated is %d \n\r",length);
if (length == 0xFF) {
printf("Invalid offset\n\r");
return EINVAL;
}
if(offset& 0x10000 == 0){
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 {
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_int64_t offset, char* value, u_int32_t val_len)
{
u_int32_t length = get_length(offset & ~(0x20000));
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
#PLM_IV
/ # ./test_example -w 0x101dc 123456789123123456789123
[48545.512]XPlmi_IpiDispatchHandler: Error: IPI command failed for C/ # ommand ID: 0x1000B18
[48549.174]PLM Error Status: 0x2B18F200
/ # ./test_example -w 0x301dc 123456789123123456789123
Data written at offset = 301dc of size = 24 bytes
/ # ./test_example -r 0x301dc
length calculated is 12
23917856 34122391 78563412
#BLACK_IV
/ # ./test_example -w 0x101d0 123456789123123456789123
[87780.979]XPlmi_IpiDispatchHandler: Error: IPI c/ # ommand failed for Command ID: 0x1000B18
[87784.225]PLM Error Status: 0x2B18F200
/ # ./test_example -w 0x301d0 123456789123123456789123
Data written at offset = 301d0 of size = 24 bytes
/ # ./test_example -r 0x301d0
length calculated is 12
23917856 34122391 78563412
#DATA_PARTITION_IV
/ # ./test_example -w 0x101e8 123456789123123456789123
[94745.490]XPlmi_IpiDispatchHandler: Error: IPI comma/ # nd failed for Command ID: 0x1000B18
[94748.533]PLM Error Status: 0x2B18F200
/ # ./test_example -w 0x301e8 123456789123123456789123
Data written at offset = 301e8 of size = 24 bytes
/ # ./test_example -r 0x301e8
length calculated is 12
23917856 34122391 78563412
#METAHEADER_IV
/ # ./test_example -w 0x10180 123456789123123456789123
[94745.490]XPlmi_IpiDispatchHandler: Error: IPI comma/ # nd failed for Command ID: 0x1000B18
[94748.533]PLM Error Status: 0x2B18F200
/ # ./test_example -w 0x30180 123456789123123456789123
Data written at offset = 30180 of size = 24 bytes
/ # ./test_example -r 0x30180
length calculated is 12
23917856 34122391 78563412
/ #
#SECURITY_MISC_1
/ # ./test_example -w 0x100e8 ffff1fff
[36976.096]XPlmi_IpiDispatchHandler: Error: IPI command failed for Command ID: 0x10/ # 00B19
[36979.815]PLM Error Status: 0x2B19F200
/ # ./test_example -w 0x300e8 ffff1fff
Data written at offset = 300e8 of size = 8 bytes
/ # ./test_example -r 0x300e8
length calculated is 4
1fff
#ANLG_TRIM3
/ # ./test_example -w 0x10010 7fffffff
[119665.926]XPlmi_IpiDispatchHandler: Error: IPI command failed/ # for Command ID: 0x1000B1F
[119669.010]PLM Error Status: 0x2B1FF200
/ # ./test_example -w 0x30010 7fffffff
Data written at offset = 30010 of size = 8 bytes
/ # ./test_example -r 0x30010
length calculated is 4
7fffffff
/ # ./test_example -w 0x30010 ffffff00
Data written at offset = 30010 of size = 8 bytes
/ # ./test_example -r 0x30010
length calculated is 4
ffffffff
#PUF_DATA
Note : To write puf data,give your input data in data.txt file and save it in the tftpd directory. Data should be in chash,aux and pufdata format. and the offset value is PUF_DATA
For read operation, we can individually read the corresponding register offsets
/ # ./test_example -w 0x1ffff data.txt
.891]XPlmi_IpiDispatchHandler: Error: IPI c/ # ommand failed for Command ID: 0x1000B1A
[96081.451]PLM Error Status: 0x2B1AF200
/ # ./test_example -w 0x3ffff data.txt
Data written at offset = 3ffff of size = 1136 bytes
/ # ./test_example -r 0x30a04
length calculated is 508
5de49269 51d24991 551c8251 8d098ad4 6501ca3b c54a2b60 5e0d103a dd3b83f9 9ee14159 f49584e a8dedb63 b53caa68 a0e4466 180d22ff 1136034d 92fcf1cc a698f232 65b1b1a7 9587845c ba5ade54 9d3d539b 4e55121c 5666c4dd 78700ebc 74d2b70a f711453c 42c80854 33082f6e 822502c2 1fdcf1be 7c6a9999 a293e895 8c26ac0b 36fad142 f044cfeb 6faa8378 2204797b d48129a3 fae894e1 d49665a6 b533b564 574d48ff 70e977f7 ce66594d 1ef9a8d4 35821209 16035eac 95911dd8 bc15318b a1b598ca fa44d1c9 a3eb0df9 dbadb06c 730a84b4 62876dd5 dfd72763 a19c4253 c45e1213 ff1cda82 3c4958d 62d97dcd d2de449f bfa96633 f95cfd2a 3ee6867d 740fbb58 adedeb33 5e48a828 d7b15741 c5964c0 9e50a5a6 703d0dcd 679d28c 1419e1b5 ebc3644c cb437f44 5860373 9b11565b 9f91c313 ea8a1f9a 9628aaa8 ac40cd4d 9afe68dc f22fb975 d3d6cdf8 4920498e 44836ec5 77d1792f 40b206ec 82fa9d4e 7159ef54 114e462f 8f1030d0 a0e7a4df 60b153cd 5260217c 95daf9cf ff66287 6de4dca1 73c6f23 c322ec80 422c24b1 e7cdc803 416e8683 43d86929 35bf05cb ecd7ffe4 769fa698 e636d9f dc23164e c3d868a2 38460805 a586d414 748a67bc 8967b177 c336a315 8e4c81d4 cbd0a1b9 8d9ce1d 3c51d479 9b4c068 f2a2c422 caccdf0 e628dd63 a2cb4ad1 75e4bd05 bd6976d1
/ # ./test_example -r 0x100a4
length calculated is 4
3020c
/ # ./test_example -r 0x100a8
length calculated is 4
403020b
#OFFCHIP_REVOKE_0 TO OFFCHIP_REVOKE_7
/ # ./test_example -w 0x10160 12345678
[38851.130]XPlmi_IpiDispatchHandler: Error: IPI command failed for Command ID: 0x1000B1B
/ # [38853.716]PLM Error Status: 0x2B1BF200
/ # ./test_example -w 0x30160 12345678
Data written at offset = 30160 of size = 8 bytes
/ # ./test_example -r 0x30160
length calculated is 4
78563412
/ # ./test_example -w 0x10164 12345678
[48360.252]XPlmi_IpiDispatchHandler: Error: IPI command failed/ # for Command ID: 0x1000B1B
[48363.684]PLM Error Status: 0x2B1BF200
/ # ./test_example -w 0x30164 12345678
Data written at offset = 30164 of size = 8 bytes
/ # ./test_example -r 0x30164
length calculated is 4
78563412
/ # ./test_example -w 0x10168 12345678
[53241.811]XPlmi_IpiDispatchHandler: Error: IPI command failed for Com/ # mand ID: 0x1000B1B
[53244.563]PLM Error Status: 0x2B1BF200
/ # ./test_example -w 0x30168 12345678
Data written at offset = 30168 of size = 8 bytes
/ # ./test_example -r 0x30168
length calculated is 4
78563412
/ # ./test_example -w 0x1016c 12345678
[58541.411]XPlmi_IpiDispatchHandler: Error: IPI command failed for Co/ # mmand ID: 0x1000B1B
[58544.240]PLM Error Status: 0x2B1BF200
/ # ./test_example -w 0x3016c 12345678
Data written at offset = 3016c of size = 8 bytes
/ # ./test_example -r 0x3016c
length calculated is 4
78563412
/ # ./test_example -w 0x10170 12345678
[65165.887]XPlmi_IpiDispatchHandler: Error: IPI command failed for Command / # ID: 0x1000B1B
[65169.044]PLM Error Status: 0x2B1BF200
/ # ./test_example -w 0x30170 12345678
Data written at offset = 30170 of size = 8 bytes
/ # ./test_example -r 0x30170
length calculated is 4
78563412
/ # ./test_example -w 0x10174 12345678
[70260.302]XPlmi_IpiDispatchHandler: Error: IPI command failed for Command ID: 0x1000B1B/ #
[70263.334]PLM Error Status: 0x2B1BF200
/ # ./test_example -w 0x30174 12345678
Data written at offset = 30174 of size = 8 bytes
/ # ./test_example -r 0x30174
length calculated is 4
78563412
/ # ./test_example -w 0x10178 12345678
[74751.987]XPlmi_IpiDispatchHandler: Error: IPI / # command failed for Command ID: 0x1000B1B
[74755.567]PLM Error Status: 0x2B1BF200
/ # ./test_example -w 0x30178 12345678
Data written at offset = 30178 of size = 8 bytes
/ # ./test_example -r 0x30178
length calculated is 4
78563412
/ # ./test_example -w 0x1017C 12345678
[101045.247]XPlmi_IpiDispatchHandler: Error: IPI command failed for Command ID: 0x1000B1B
/ # [101048.599]PLM Error Status: 0x2B1BF200
/ # ./test_example -w 0x3017C 12345678
Data written at offset = 3017c of size = 8 bytes
/ # ./test_example -r 0x3017C
length calculated is 4
78563412
#REVOCATIONID_0 to REVOCATIONID_7
/ # ./test_example -w 0x100b0 12345678
[124927.668]XPlmi_IpiDispatchHandler: Error: IPI c/ # ommand failed for Command ID: 0x1000B1D
[124930.634]PLM Error Status: 0x2B1DF200
/ # ./test_example -w 0x300b0 12345678
Data written at offset = 300b0 of size = 8 bytes
/ # ./test_example -r 0x300b0
length calculated is 4
78563412
/ # ./test_example -w 0x100b4 12345678
[131446.691]XPlmi_IpiDispatchHandler: Error: IPI/ # command failed for Command ID: 0x1000B1D
[131450.088]PLM Error Status: 0x2B1DF200
/ # ./test_example -w 0x300b4 12345678
Data written at offset = 300b4 of size = 8 bytes
/ # ./test_example -r 0x300b4
length calculated is 4
78563412
/ # ./test_example -w 0x100b8 12345678
[136081.883]XPlmi_IpiDispatchHandler: Error: IPI command/ # failed for Command ID: 0x1000B1D
[136085.118]PLM Error Status: 0x2B1DF200
/ # ./test_example -w 0x300b8 12345678
Data written at offset = 300b8 of size = 8 bytes
/ # ./test_example -r 0x300b8
length calculated is
78563412
/ # ./test_example -w 0x100bc 12345678
[141206.833]XPlmi_IpiDispatchHandler: Error: IPI command/ # failed for Command ID: 0x1000B1D
[141209.895]PLM Error Status: 0x2B1DF200
/ # ./test_example -w 0x300bc 12345678
Data written at offset = 300bc of size = 8 bytes
/ # ./test_example -r 0x300bc
length calculated is 4
78563412
/ # ./test_example -w 0x100c0 12345678
[151918.119]XPlmi_IpiDispatchHandler: Error:/ # IPI command failed for Command ID: 0x1000B1D
[151921.862]PLM Error Status: 0x2B1DF200
/ # ./test_example -w 0x300c0 12345678
Data written at offset = 300c0 of size = 8 bytes
/ # ./test_example -r 0x300c0
length calculated is 4
78563412
/ # ./test_example -w 0x100c4 12345678
[162347.161]XPlmi_IpiDispatchHandler: Error: IPI/ # command failed for Command ID: 0x1000B1D
[162350.356]PLM Error Status: 0x2B1DF200
/ # ./test_example -w 0x300c4 12345678
Data written at offset = 300c4 of size = 8 bytes
/ # ./test_example -r 0x300c4
length calculated is 4
78563412
/ # ./test_example -w 0x100c8 12345678
[168412.445]XPlmi_IpiDispatchHandler: Error: IPI comm/ # and failed for Command ID: 0x1000B1D
[168415.448]PLM Error Status: 0x2B1DF200
/ # ./test_example -w 0x300c8 12345678
Data written at offset = 300c8 of size = 8 bytes
/ # ./test_example -r 0x300c8
length calculated is 4
78563412
/ # ./test_example -w 0x100cc 12345678
[175143.329]XPlmi_IpiDispatchHandler: Error: IPI command failed f/ # or Command ID: 0x1000B1D
[175146.274]PLM Error Status: 0x2B1DF200
/ # ./test_example -w 0x300cc 12345678
Data written at offset = 300cc of size = 8 bytes
/ # ./test_example -r 0x300cc
length calculated is 4
78563412
#PPK0_HASH,PPK1_HASH,PPK2_HASH
# ./test_example -w 0x10100 0102030405060708090001020304050607080900010203040506070809000102
[230379.920]XPlmi_IpiDispatchHandler: Error: IPI command failed for Command ID: 0x1000B1/E #
[230382.361]PLM Error Status: 0x2B1EF200
/ # ./test_example -w 0x30100 0102030405060708090001020304050607080900010203040506070809000102
Data written at offset = 30100 of size = 64 bytes
/ # ./test_example -r 0x10100
length calculated is 32
2010009 8070605 4030201 90807 6050403 2010009 8070605 4030201
/ # ./test_example -w 0x10120 0102030405060708090001020304050607080900010203040506070809000102
[241690.451]XPlmi_IpiDispatchHandler: Error: IPI comman/ # d failed for Command ID: 0x1000B1E
[241693.665]PLM Error Status: 0x2B1EF200
/ # ./test_example -w 0x30120 0102030405060708090001020304050607080900010203
040506070809000102
Data written at offset = 30120 of size = 64 bytes
/ # ./test_example -r 0x30120
length calculated is 32
2010009 8070605 4030201 90807 6050403 2010009 8070605 4030201
/ # ./test_example -w 0x10140 0102030405060708090001020304050607080900010203
040506070809000102
[250700.685]XPlmi_IpiDispatchHandler: Error: IPI command fai/ # led for Command ID: 0x1000B1E
[250703.440]PLM Error Status: 0x2B1EF200
/ # ./test_example -w 0x30140 0102030405060708090001020304050607080900010203
040506070809000102
Data written at offset = 30140 of size = 64 bytes
/ # ./test_example -r 0x30140
length calculated is 32
2010009 8070605 4030201 90807 6050403 2010009 8070605 4030201
/ #
#USER_EFUSES
/ # ./test_example -w 0x10204 12345678
[34861.453]XPlmi_IpiDispatchHandler: Error: IPI command failed for Command ID: 0x1000B1C
[3/ # 4863.691]PLM Error Status: 0x2B1CF200
/ # ./test_example -w 0x30204 12345678
Data written at offset = 30204 of size = 8 bytes
/ # ./test_example -r 0x30204
length calculated is 4
78563412
/ # ./test_example -w 0x10220 12345678
[43214.176]XPlmi_IpiDispatchHandler: Error: I/ # PI command failed for Command ID: 0x1000B1C
[43217.867]PLM Error Status: 0x2B1CF200
/ # ./test_example -w 0x30220 12345678
Data written at offset = 30220 of size = 8 bytes
/ # ./test_example -r 0x30220
length calculated is 4
78563412
/ # ./test_example -w 0x10240 12345678
[51223.143]XPlmi_IpiDispatchHandler: Error: IPI c/ # ommand failed for Command ID: 0x1000B1C
[51226.577]PLM Error Status: 0x2B1CF200
/ # ./test_example -w 0x30240 12345678
Data written at offset = 30240 of size = 8 bytes
/ # ./test_example -r 0x30240
length calculated is 4
78563412
/ # ./test_example -w 0x10260 12345678
[56438.824]XPlmi_IpiDispatchHandler: Error: IPI / # command failed for Command ID: 0x1000B1C
[56442.341]PLM Error Status: 0x2B1CF200
/ # ./test_example -w 0x30260 12345678
Data written at offset = 30260 of size = 8 bytes
/ # ./test_example -r 0x30260
length calculated is 4
78563412
/ # ./test_example -w 0x10280 12345678
[63023.439]XPlmi_IpiDispatchHandler: Error: I/ # PI command failed for Command ID: 0x1000B1C
[63026.978]PLM Error Status: 0x2B1CF200
/ # ./test_example -w 0x30280 12345678
Data written at offset = 30280 of size = 8 bytes
/ # ./test_example -r 0x30280
length calculated is 4
78563412
/ # ./test_example -w 0x102a0 12345678
[69474.075]XPlmi_IpiDispatchHandler: Error: IPI command fai/ # led for Command ID: 0x1000B1C
[69477.840]PLM Error Status: 0x2B1CF200
/ # ./test_example -w 0x302a0 12345678
Data written at offset = 302a0 of size = 8 bytes
/ # ./test_example -r 0x302a0
length calculated is 4
78563412
/ # ./test_example -w 0x102c0 12345678
[75164.754]XPlmi_IpiDispatchHandler: Error: IP/ # I command failed for Command ID: 0x1000B1C
[75168.494]PLM Error Status: 0x2B1CF200
/ # ./test_example -w 0x302c0 12345678
Data written at offset = 302c0 of size = 8 bytes
/ # ./test_example -r 0x302c0
length calculated is 4
78563412
/ # ./test_example -w 0x102e0 12345678
[84919.016]XPlmi_IpiDispatchHandler: Error: IPI comm/ # and failed for Command ID: 0x1000B1C
[84922.308]PLM Error Status: 0x2B1CF200
/ # ./test_example -w 0x302e0 12345678
Data written at offset = 302e0 of size = 8 bytes
/ # ./test_example -r 0x302e0
length calculated is 4
78563412
/ # ./test_example -w 0x102fc 12345678
[90704.410]XPlmi_IpiDispatchHandler: Error: I/ # PI command failed for Command ID: 0x1000B1C
[90707.870]PLM Error Status: 0x2B1CF200
/ # ./test_example -w 0x302fc 12345678
Data written at offset = 302fc of size = 8 bytes
/ # ./test_example -r 0x302fc
length calculated is 4
78563412
#SECURITY_CONTROL_EFUSE
/ # ./test_example -w 0x100ac d3f8000f
[416730.116]XPlmi_IpiDispatchHandler: Error: IPI comm/ # and failed for Command ID: 0x1000B22
[416733.273]PLM Error Status: 0x2B22F200
/ # ./test_example -w 0x300ac d3f8000f
Data written at offset = 300ac of size = 8 bytes
/ # ./test_example -r 0x100ac
length calculated is 4
d3f8000f
Trying to write key-management bits
/ # ./test_example -w 0x300ac 00000040
[438969.860]XPlmi_IpiDispatchHandler: Error: IPI command fa/ # iled for Command ID: 0x1000B22
[438972.862]PLM Error Status: 0x2B22FA00
#MISC_CTRL_EFUSE
/ # ./test_example -w 0x300a0 e078c100
Data written at offset = 300a0 of size = 8 bytes
/ # ./test_example -r 0x300a0
length calculated is 4
78c100
/ # ./test_example -w 0x100a0 e078c100
[36022.249]XPlmi_IpiDispatchHandler: Error: IPI command failed for Command / # ID: 0x1000B21
[36025.555]PLM Error Status: 0x2B21F200
#BOOT ENV CONTROL
/ # ./test_example -r 0x30094
length calculated is 4
0
/ # ./test_example -w 0x30094 ffffffff
Data written at offset = 30094 of size = 8 bytes
/ # ./test_example -r 0x30094
length calculated is 4
363e03
/ # ./test_example -w 0x10094 ffffffff
[36022.249]XPlmi_IpiDispatchHandler: Error: IPI command failed for Command / # ID: 0x1000B21
[36025.555]PLM Error Status: 0x2B21F200
Linux application to program PUFHD, generate and program black key and PPK hash
Invoke PUF registration.
Read the red key from the location used in step 3, request the AES engine to encrypt the red key using the PUF KEK to generate a black key (the IV is embedded in the boot header)
Program the generated PUF helper data and the black key to eFUSEs
Program PPK hashes to eFUSEs
Note: User should provide PPK HASH, RED KEY, BLACK IV
typedef unsigned char u8;
typedef unsigned int u32;
typedef unsigned long long u64;
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/if_alg.h>
#include <linux/socket.h>
#include <linux/types.h>
#include "xlnx_puf.h"
#include <string.h>
#include <errno.h>
#include <stdbool.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#define MAX_SYNDROME_DATA_LEN (PUF_MAX_SYNDROME_DATA_LEN_IN_WORDS * sizeof(u32))
#define MAX_EFUSE_TRIM_SYN_DATA_LEN (PUF_EFUSE_TRIM_SYN_DATA_IN_WORDS * sizeof(u32))
#define MAX_PUF_ID_LEN (PUF_ID_LEN_IN_WORDS * sizeof(u32))
#define PUF_SHUTTER_VALUE 0x81000100U
/* Xilinx error codes */
#define RD_FAILED 1026
#define WR_FAILED 1027
#define SYS_PATH "/sys/bus/nvmem/devices/xilinx-secure-config0/nvmem"
#ifndef SOL_ALG
#define SOL_ALG 279
#endif
#define KEY_SIZE 32
#define IV_SIZE 12
#define AAD_SIZE 16
#define GCM_TAG_SIZE 16
#define DATA_SIZE 32
#define PUF_WORD_LENGTH 0x4
#define PUF_RED_KEY_LEN_IN_BYTES 32
#define PUF_RED_KEY_LEN_IN_BITS PUF_RED_KEY_LEN_IN_BYTES*8
#define HASH_STRING_LEN_IN_BYTES 65
#define BLACK_IV_STRING_LEN_IN_BYTES 25
#define PUF_DATA_LENGTH 129
#define PUF_BLACK_KEY_STRING_LEN_IN_BYTES 64
#define PUF_DATA_LENGTH_IN_BYTES 512
#define PUF_DATA_STR_LEN_IN_BYTES 1016
#define VERSAL_AES_PUF_kEY 11 /* Refer enum in driver for more details */
struct pufdata puf_data;
char str[1016] = {0};
int PufRegis(void) {
int fd;
int err;
struct puf_usrparams params;
ssize_t size;
// Open the PUF device file
fd = open("/dev/xpuf", O_RDWR);
if (fd < 0) {
perror("Failed to open PUF device /dev/xlnx-puf");
err = 1;
goto END;
}
// Set the parameters for PUF registration
params.pufoperation = 0U; // PUF registration
params.globalvarfilter = 1U;
params.readoption = PUF_READ_FROM_RAM;
params.shuttervalue = PUF_SHUTTER_VALUE;
params.pufdataaddr = (u64)&puf_data.pufhd;
params.trimsyndataaddr = (u64)puf_data.efusesyndata;
// Perform PUF registration
if (ioctl(fd, PUF_REGISTRATION, ¶ms) < 0) {
perror("PUF registration failed \n");
err = 1;
goto END;
}
// Print the PUF ID
printf("1 : PUF ID registartion:\n ");
for (int i = 0; i < PUF_ID_LEN_IN_WORDS * 4U; i++) {
printf("%02x ", *((u8*)puf_data.pufid + i));
}
printf("\n");
// Print the syndrome data
printf("Syndrome Data:\n");
for (int i = 0; i < PUF_MAX_SYNDROME_DATA_LEN_IN_WORDS; i++) {
printf("%08x ", puf_data.pufhd.syndata[i]);
}
printf("\n");
printf("trimmed Syndrome Data:\n");
for (int i = 0; i < PUF_EFUSE_TRIM_SYN_DATA_IN_WORDS; i++) {
printf("%08x ", puf_data.efusesyndata[i]);
}
printf("\n");
printf("PUF id:\n");
for (int i = 0; i < PUF_ID_LEN_IN_WORDS; i++) {
printf("%08x ", puf_data.pufid[i]);
}
printf("\n");
// Print the auxiliary data
printf("Auxiliary Data: %08x\n", puf_data.pufhd.aux);
printf("Chash Data: %08x\n", puf_data.pufhd.chash);
close(fd);
err = 0;
printf("PUF registration completed successfully.\n");
END:
return err;
}
void FormatAesKey(const u8* Key, u8* FormattedKey, u32 KeyLen)
{
int Index;
int Words = KeyLen / PUF_WORD_LENGTH;
u32 WordIndex = Words / 2U;
u32* InputKey = (u32*)Key;
u32* OutputKey = (u32*)FormattedKey;
for(Index = 0U; Index < Words; Index++)
{
OutputKey[Index] = InputKey[WordIndex];
WordIndex++;
/*
* AES word size = 128 bits
* So to change the endianness, code should swap lower 64bits
* with upper 64 bits
* 64 bits = 8 bytes
*/
WordIndex = WordIndex % 8U;
}
}
void ReverseData (const u8 *OrgDataPtr, u8* SwapPtr, u32 Len){
u32 Index = 0U;
u32 ReverseIndex = Len - 1U;
for(Index = 0U; Index < Len; Index++)
{
SwapPtr[Index] = OrgDataPtr[ReverseIndex];
ReverseIndex--;
}
}
int main(void)
{
int len = DATA_SIZE + GCM_TAG_SIZE;
int opfd;
int nvmfd;
ssize_t size;
int tfmfd;
unsigned int keytype;
unsigned int envdis;
u_int64_t offset;
struct sockaddr_alg sa = {
.salg_family = AF_ALG,
.salg_type = "aead",
.salg_name = "gcm(aes)"
};
struct msghdr msg = {};
struct cmsghdr *cmsg;
char cbuf[CMSG_SPACE(4) + CMSG_SPACE(1024) + CMSG_SPACE(1024)] = {0};
char buf[len];
int length = strlen(str);
struct af_alg_iv *iv;
struct iovec iov;
int i;
u32 PufData[PUF_DATA_LENGTH];
unsigned char blackkey[PUF_BLACK_KEY_STRING_LEN_IN_BYTES] = {0};
unsigned char blkkey[PUF_BLACK_KEY_STRING_LEN_IN_BYTES]={0};
char PufDataresult[PUF_DATA_LENGTH_IN_BYTES]={0};
char PufDataInString[PUF_DATA_STR_LEN_IN_BYTES] = {0};
char hash[HASH_STRING_LEN_IN_BYTES]="5CA45B3D17F4F809549796A9ECE8CC23365AFAC4BCA636B11F08ABE2EF54C36F";
char blackiv[BLACK_IV_STRING_LEN_IN_BYTES] = "D2450E07EA5DE0426C0FA133";
u8 FormattedBlackKey[PUF_RED_KEY_LEN_IN_BITS] = {0};
u8 FlashBlackKey[PUF_RED_KEY_LEN_IN_BITS] = {0};
unsigned char temp[len];
u8 temp1[PUF_RED_KEY_LEN_IN_BITS];
__u8 dummykey[32U] = {0U};
__u8 key[] = {
0xF8, 0x78, 0xB8, 0x38, 0xD8, 0x58, 0x98, 0x18, 0xE8, 0x68, 0xA8, 0x28, 0xC8, 0x48,
0x88, 0x08, 0xF0, 0x70, 0xB0, 0x30, 0xD0, 0x50, 0x90, 0x10, 0xE0, 0x60, 0xA0, 0x20,
0xC0, 0x40, 0x80, 0x00
};
__u8 key_type[] = {VERSAL_AES_PUF_kEY};
__u8 usr_iv[] = {
0xD2, 0x45, 0x0E, 0x07, 0xEA, 0x5D, 0xE0, 0x42, 0x6C, 0x0F, 0xA1, 0x33
};
__u8 input[DATA_SIZE] = {
/* Plain text */
0x12, 0x34, 0x56, 0x78, 0x08, 0xF0, 0x70, 0xB0, 0x30, 0xD0, 0x50, 0x90, 0x10, 0xE0,
0x60, 0xA0, 0x20, 0xC0, 0x40, 0x80, 0x00, 0xA5, 0xDE, 0x08, 0xD8, 0x58, 0x98, 0xA5,
0xA5, 0xFE, 0xDC, 0xA1
};
/* Do registration */
if (PufRegis() > 0) {
perror("PUF registartion failed\n");
return 1;
}
/* Do enc/dec using PUF kek */
tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(tfmfd, (struct sockaddr *)&sa, sizeof(sa));
setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY, key_type, 1);
setsockopt(tfmfd, SOL_ALG, ALG_SET_KEY, dummykey, 32);
setsockopt(tfmfd, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, NULL, 16);
//setsockopt(tfmfd, SOL_ALG, ALG_SET_AEAD_ASSOCLEN, aad, AAD_SIZE);
opfd = accept(tfmfd, NULL, 0);
msg.msg_control = cbuf;
msg.msg_controllen = sizeof(cbuf);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_ALG;
cmsg->cmsg_type = ALG_SET_OP;
cmsg->cmsg_len = CMSG_LEN(4);
*(__u32 *)CMSG_DATA(cmsg) = ALG_OP_ENCRYPT;
cmsg = CMSG_NXTHDR(&msg, cmsg);
cmsg->cmsg_level = SOL_ALG;
cmsg->cmsg_type = ALG_SET_IV;
cmsg->cmsg_len = CMSG_LEN(1024);
iv = (void *)CMSG_DATA(cmsg);
iv->ivlen = IV_SIZE;
memcpy(iv->iv, usr_iv, IV_SIZE);
/* AAD may not be required in case of KEK enc/dec use case */
cmsg = CMSG_NXTHDR(&msg, cmsg);
cmsg->cmsg_level = SOL_ALG;
cmsg->cmsg_type = ALG_SET_AEAD_ASSOCLEN;
cmsg->cmsg_len = CMSG_LEN(1024);
iv = (void *)CMSG_DATA(cmsg);
//iv->ivlen = AAD_SIZE;
iov.iov_base = input;
iov.iov_len = len;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
sendmsg(opfd, &msg, 0);
read(opfd, buf, len);
printf("Data Out:\r\n");
for (i = 0; i < len - GCM_TAG_SIZE; i++) {
printf("%02x", (unsigned char)buf[i]);
}
printf("\n");
printf("GCM TAG:\r\n");
for (i = len - GCM_TAG_SIZE; i < len; i++) {
printf("%02x", (unsigned char)buf[i]);
}
printf("\n");
printf("Input:\r\n");
for (i = 0; i < len - GCM_TAG_SIZE; i++) {
printf("%02x", (unsigned char)input[i]);
}
close(opfd);
close(tfmfd);
nvmfd = open(SYS_PATH, O_RDWR);
printf("nvmfd = %x \n\r",nvmfd);
if(nvmfd <= 0) {
printf("Opening SYS FS NVMEM file is failed %d \n", errno);
perror(open);
return errno;
}
/* programming black iv */
size = pwrite(nvmfd,blackiv,2*IV_SIZE,0x301D0);
if (size == 2*IV_SIZE) {
printf("Black IV PROGRAMMED \n\r");
}
else {
printf("Black IV is not PROGRAMMED \n\r");
return WR_FAILED;
}
size = pwrite(nvmfd,hash,2*DATA_SIZE,0x30100);
if (size == 2*DATA_SIZE) {
printf("PPK HASH IS PROGRAMMED\n\r");
}
else {
printf("PPK HASH is not PROGRAMMED \n\r");
return WR_FAILED;
}
PufData[0] = puf_data.pufhd.chash;
PufData[1] = puf_data.pufhd.aux;
for(int i=2;i<129;i++) {
PufData[i]= puf_data.efusesyndata[i-2];
}
for(int i=0;i<129;i++){
sprintf(PufDataresult,"%08x",PufData[i]);
strcat(PufDataInString,PufDataresult);
}
length = strlen(PufDataInString);
size = pwrite(nvmfd,PufDataInString,length,0x3FFFF);
if (size == length) {
printf("PUF data is PROGRAMMED \n\r");
}
else {
printf("PUF data is not PROGRAMMED \n\r");
return WR_FAILED;
}
/*buf is an array where encrypted data(encrypted redkey) is stored*/
FormatAesKey(buf,FormattedBlackKey,PUF_RED_KEY_LEN_IN_BYTES);
ReverseData(FormattedBlackKey, FlashBlackKey, PUF_RED_KEY_LEN_IN_BYTES);
for(int i=0;i<DATA_SIZE;i++){
sprintf(blackkey,"%02x",(unsigned char)FlashBlackKey[i]);
strcat(blkkey,blackkey);
}
/*Key type can be choosen accordingly AES KEY = 1, USER_KEY_0 = 2, USER_KEY_1 = 4*/
keytype = 1;
envdis = 1;
offset = (1 << 16)| (envdis<<17) | keytype;
size = pwrite(nvmfd, blkkey, 2*DATA_SIZE, offset);
if (size == 2*DATA_SIZE) {
printf("Black key is programmed \n\r");
}
else {
printf("blackkey programming failed \n\r");
return WR_FAILED;
}
close(nvmfd);
return 0;
}
Expected Output
For PUF Registration:
PUF ID registartion:
51 4c ff 22 ff 25 99 48 68 8d 1f e9 66 69 5a 63 61 57 a0 50 a9 fe c0 8f 46 51 07 b3 34 9d 35 e2
Syndrome Data:
41463c3c 5076454b 3e3e4655 0000000a 00000000 00000000 00000000 00000000 89789548 bbea176b cbafd8b7 00000000 00000000 00000000 00000000 00000000 9834989f 230904de e304da78 00000000 00000000 00000000 00000000 00000000 14092ae3 fed00acd 44332211 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
trimmed Syndrome Data:
41463c3c 5076454b 3e3e4655 00000000 00000000 00000000 00000000 00897895 48bbea17 6bcbafd8 b7000000 00000000 00000000 00000000 00009834 989f2309 04dee304 da780000 00000000 00000000 00000000 00000014 092ae3fe d00acd44 33221100 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
PUF id:
22ff4c51 489925ff e91f8d68 635a6966 50a05761 8fc0fea9 b3075146 e2359d34
Auxiliary Data: 00aa22aa
Chash Data: 44332211
PUF registration completed successfully.
Data Out:
f8f4e39774ae1e3fba730fcd42b3beb8c9ff81828a8a00b04773bd314ab0fb2f
GCM TAG:
a2c90da35a336010afca44eab458c8bf
Input:
1234567808f070b030d0509010e060a020c0408000a5de08d85898a5a5fedca1
PUF data is PROGRAMMED
Black key is programmed
/ #
Mainline status
The driver is not available at Mainline
Changelog
2021.1
Commits:
firmware: xilinx: Add support to access BBRAM and volatile user keys · Xilinx/linux-xlnx@49c96e3
dt-bindings: nvmem: Added nodes for BBRAM and volatile user keys · Xilinx/linux-xlnx@4215639
nvmem: Add driver for BBRAM and volatile user keys access · Xilinx/linux-xlnx@7bcdd6e
Related Links
linux-xlnx/drivers/nvmem/xlnx_secure_config.c at master · Xilinx/linux-xlnx
Related content
© Copyright 2019 - 2022 Xilinx Inc. Privacy Policy