Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
languagec
/******************************************************************************
* 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;
}

...