由于ATF的安全策略,从LInux烧写Efuse,需要经过SMC,调用PMU的API来实现。
对PMU代码进行修改(Petalinux下增加EFUSE内容后,会导致PMU RAM(128K) 溢出,暂时用Vitis进行编译):
在src目中中找到xpfw_config.h中
在extra_compiler_flags 里面增加: -DXSK_ACCESS_USER_EFUSE -DXSK_ACCESS_KEY_MANAGE_EFUSE, 最终结果类似这样的:
-g -ffunction-sections -fdata-sections -Wall -Wextra -fno-tree-loop-distribute-patterns -Os -flto -ffat-lto-objects -DXSK_ACCESS_USER_EFUSE -DXSK_ACCESS_KEY_MANAGE_EFUSE
备注:efuse里面很多功能都是依靠宏定义隔离的,如果后面有功能无法使用,请检查xilskey_eps_zynqmp.c这个文件中的XilSKey_ZynqMpEfuseWrite函数
同样的方法,打开对应的宏定义即可。
petalinux-config -c kernel
Device Drivers ---> NVMEM Support
nvmem_firmware {
compatible = "xlnx,zynqmp-nvmem-fw";
#address-cells = <1>;
#size-cells = <1>;
soc_revision: soc_revision@0 {
reg = <0x0 0x4>;
};
/ efuse access /
efuse_dna: efuse_dna@c {
reg = <0xc 0xc>;
};
efuse_usr0: efuse_usr0@20 {
reg = <0x20 0x4>;
};
efuse_usr1: efuse_usr1@24 {
reg = <0x24 0x4>;
};
efuse_usr2: efuse_usr2@28 {
reg = <0x28 0x4>;
};
efuse_usr3: efuse_usr3@2c {
reg = <0x2c 0x4>;
};
efuse_usr4: efuse_usr4@30 {
reg = <0x30 0x4>;
};
efuse_usr5: efuse_usr5@34 {
reg = <0x34 0x4>;
};
efuse_usr6: efuse_usr6@38 {
reg = <0x38 0x4>;
};
efuse_usr7: efuse_usr7@3c {
reg = <0x3c 0x4>;
};
efuse_miscusr: efuse_miscusr@40 {
reg = <0x40 0x4>;
};
efuse_chash: efuse_chash@50 {
reg = <0x50 0x4>;
};
efuse_pufmisc: efuse_pufmisc@54 {
reg = <0x54 0x4>;
};
efuse_sec: efuse_sec@58 {
reg = <0x58 0x4>;
};
efuse_spkid: efuse_spkid@5c {
reg = <0x5c 0x4>;
};
efuse_ppk0hash: efuse_ppk0hash@a0 {
reg = <0xa0 0x30>;
};
efuse_ppk1hash: efuse_ppk1hash@d0 {
reg = <0xd0 0x30>;
};
};
./efuse-access --read 0xA0
79f08c4e b1aaf60c b5a65544 5657c03c f7602244 4364f490 822e8747 4764fe89 2ad8fbb3 8cb48653 6cb3151c 3d45b040
./efuse-access --write 0x5C 0x1f
./efuse-access --read 0x5c
1f000000
#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;
}EfuseLookupTable;
/* Xilinx error codes */
#define EFUSE_RD_FAILED 1026
#define EFUSE_WR_FAILED 1027
#define SYS_PATH "/sys/bus/nvmem/devices/zynqmp-nvmem0/nvmem"
#define EFUSE_MAX_ROWS 16
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_efuse(int fd, u_int32_t offset);
static int32_t write_efuse(int fd, u_int32_t offset, char* value, u_int32_t val_len);
static u_int32_t convert_char_to_nibble(char in_char, unsigned char *num);
static u_int32_t convert_string_to_hex_be(const char *str, unsigned char *buf,
u_int32_t len);
static u_int32_t convert_string_to_hex_le(const char *str, unsigned char *buf,
u_int32_t 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_efuse(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_efuse(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 eFuse: \r\n ./efuse_access --read "
"<Offset in hex>\r\n");
printf("Write into eFuse: \r\n ./efuse_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 eFuse\r\n");
printf("-w --write \t Write into eFuse\r\n");
printf("For more details please refer -"
"https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/"
"18841682/Solution+ZynqMP+SoC+revision+read+mechanism\r\n");
}
/*
* 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 EfuseLookupTable EfuseSize[EFUSE_MAX_ROWS] = {
/*+-----+-----+
*|Offset| Size|
*+------+-----+
*/
{0x4, 0x0}, /* Version */
{0xC, 0xC}, /* DNA */
{0x20, 0x4}, /* User0 */
{0x24, 0x4}, /* User1 */
{0x28, 0x4}, /* User2 */
{0x2C, 0x4}, /* User3 */
{0x30, 0x4}, /* User4 */
{0x34, 0x4}, /* User5 */
{0x38, 0x4}, /* User6 */
{0x3C, 0x4}, /* User7 */
{0x40, 0x4}, /* Misc User */
{0x58, 0x4}, /* Secure Control */
{0x5C, 0x4}, /* SPK ID */
{0x60, 0x20}, /* AES Key */
{0xA0, 0x30}, /* PPK0 Hash */
{0xD0, 0x30}, /* PPK1 Hash */
};
u_int32_t size = 0xFF;
int32_t index;
for(index = 0; index < EFUSE_MAX_ROWS; index++) {
if (EfuseSize[index].offset == offset) {
size = EfuseSize[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, 48);
if ((*str == '0') && (*(str + 1) == 'x' || *(str + 1) == 'X')) {
strcpy(str, &str[2]);
}
return strnlen(str, 48);
}
/*
* 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 > 2) {
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_efuse(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 == 0x60) {
printf("Read is not allowed for AES key\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 EFUSE_RD_FAILED;
}
return 0;
}
/*
* Writes user provided value in the eFUSE at the given offset
*/
static int32_t write_efuse(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[48] = {0};
int32_t status;
int32_t index;
if (length == 0xFF) {
printf("Invalid offset\n\r");
return EINVAL;
}
if (offset == 0xC) {
printf("Write is not allowed for DNA\n\r");
return EINVAL;
}
if (offset == 0x4) {
printf("Write is not allowed for Version\n\r");
return EINVAL;
}
if (val_len > (length*2)) {
printf("Length of provided value is longer than expected\n\r");
return EINVAL;
}
/* Convert to big endian format in case of PPK0/PPK1 */
if ((offset == 0xA0) || (offset == 0xD0)) {
status = convert_string_to_hex_be(value, write_data,
length*8);
if (status != 0) {
return status;
}
}
/* Convert to little endian format in case of other fuses */
else {
status = convert_string_to_hex_le(value, write_data,
length*8);
if (status != 0) {
return status;
}
}
size = pwrite(fd, (void *)&write_data, length, offset);
if (size == length) {
printf("Data written at offset = %x of size = %d bytes\n\r",
offset, size);
}
else {
return EFUSE_WR_FAILED;
}
return 0;
}
/*
* Converts character to nibble
*/
static u_int32_t convert_char_to_nibble(char in_char, unsigned char *num)
{
if ((in_char >= '0') && (in_char <= '9')) {
*num = in_char - '0';
}
else if ((in_char >= 'a') && (in_char <= 'f')) {
*num = in_char - 'a' + 10;
}
else if ((in_char >= 'A') && (in_char <= 'F')) {
*num = in_char - 'A' + 10;
}
else {
return EINVAL;
}
return 0;
}
/*
* Converts string to hex in big endian format
*/
static u_int32_t convert_string_to_hex_be(const char *str, unsigned char *buf,
u_int32_t len)
{
u_int32_t converted_len;
unsigned char lower_nibble = 0U;
unsigned char upper_nibble = 0U;
if ((str == NULL) || (buf == NULL)) {
return EINVAL;
}
if ((len == 0U) || ((len % 8) != 0U)) {
return EINVAL;
}
if((strlen(str) * 4) > len) {
return EINVAL;
}
converted_len = 0U;
while (converted_len < strlen(str)) {
if (convert_char_to_nibble(str[converted_len],&upper_nibble) ==
0) {
if (convert_char_to_nibble(str[converted_len+1],
&lower_nibble) == 0) {
buf[converted_len/2] = (upper_nibble << 4) |
lower_nibble;
}
else {
return EINVAL;
}
}
else {
return EINVAL;
}
converted_len += 2U;
}
return 0;
}
/*
* Converts string to hex in little endian format
*/
static u_int32_t convert_string_to_hex_le(const char *str, unsigned char *buf,
u_int32_t len)
{
u_int32_t converted_len;
unsigned char lower_nibble = 0U;
unsigned char upper_nibble = 0U;
u_int32_t str_index;
if ((NULL == str) || (NULL == buf)) {
return EINVAL;
}
if ((len == 0U) || ((len % 8) != 0U)) {
return EINVAL;
}
if((strlen(str) * 4) > len) {
return EINVAL;
}
str_index = (len / 8) - 1U;
converted_len = 0U;
while (converted_len < strlen(str)) {
if (convert_char_to_nibble(str[converted_len],
&upper_nibble) == 0) {
if (convert_char_to_nibble(str[converted_len + 1],
&lower_nibble) == 0) {
buf[str_index] = (upper_nibble << 4) | lower_nibble;
str_index = str_index - 1U;
}
else {
return EINVAL;
}
}
else {
return EINVAL;
}
converted_len += 2U;
}
return 0;
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。