SPM: modify sptool to generate individual SP blobs

Currently sptool generates a single blob containing all the Secure
Partitions, with latest SPM implementation, it is desirable to have
individual blobs for each Secure Partition. It allows to leverage
packaging and parsing of SP on existing FIP framework. It also allows
SP packages coming from different sources.

This patch modifies sptool so that it takes number of SP payload pairs
as input and generates number of SP blobs instead of a single blob.

Each SP blob can optionally have its own header containing offsets and
sizes of different payloads along with a SP magic number and version.
It is also associated in FIP with a UUID, provided by SP owner.

Usage example:
sptool -i sp1.bin:sp1.dtb -o sp1.pkg -i sp2.bin:sp2.dtb -o sp2.pkg ...

Signed-off-by: Manish Pandey <manish.pandey2@arm.com>
Change-Id: Ie2db8e601fa1d4182d0a1d22e78e9533dce231bc
This commit is contained in:
Manish Pandey 2020-01-07 17:05:28 +00:00
parent 235c8174ff
commit 3977a82564
2 changed files with 171 additions and 149 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2018, Arm Limited. All rights reserved. * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
* *
* SPDX-License-Identifier: BSD-3-Clause * SPDX-License-Identifier: BSD-3-Clause
*/ */
@ -9,21 +9,17 @@
#include <stdint.h> #include <stdint.h>
/* Header for a secure partition package. There is one per package. */ /* 4 Byte magic name "SPKG" */
struct sp_pkg_header { #define SECURE_PARTITION_MAGIC 0x474B5053
uint64_t version;
uint64_t number_of_sp;
};
/* /* Header for a secure partition package. */
* Entry descriptor in a secure partition package. Each entry comprises a struct sp_pkg_header {
* secure partition and its resource description. uint32_t magic;
*/ uint32_t version;
struct sp_pkg_entry { uint32_t pm_offset;
uint64_t sp_offset; uint32_t pm_size;
uint64_t sp_size; uint32_t img_offset;
uint64_t rd_offset; uint32_t img_size;
uint64_t rd_size;
}; };
#endif /* SPTOOL_H */ #endif /* SPTOOL_H */

View File

@ -1,10 +1,11 @@
/* /*
* Copyright (c) 2018, Arm Limited. All rights reserved. * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
* *
* SPDX-License-Identifier: BSD-3-Clause * SPDX-License-Identifier: BSD-3-Clause
*/ */
#include <stdarg.h> #include <stdarg.h>
#include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -16,25 +17,26 @@
#define PAGE_SIZE 4096 #define PAGE_SIZE 4096
/* /*
* Linked list of entries describing entries in the secure * Entry describing Secure Partition package.
* partition package.
*/ */
struct sp_entry_info { struct sp_pkg_info {
/* Location of the files in the host's RAM. */ /* Location of the files in the host's RAM. */
void *sp_data, *rd_data; void *img_data, *pm_data;
/* Size of the files. */ /* Size of the files. */
uint64_t sp_size, rd_size; uint32_t img_size, pm_size;
/* Location of the binary files inside the package output file */ /* Location of the binary files inside the package output file */
uint64_t sp_offset, rd_offset; uint32_t img_offset, pm_offset;
struct sp_entry_info *next;
}; };
static struct sp_entry_info *sp_info_head; /*
* List of input provided by user
static uint64_t sp_count; */
struct arg_list {
char *usr_input;
struct arg_list *next;
};
/* Align an address to a power-of-two boundary. */ /* Align an address to a power-of-two boundary. */
static unsigned int align_to(unsigned int address, unsigned int boundary) static unsigned int align_to(unsigned int address, unsigned int boundary)
@ -89,26 +91,61 @@ static void xfseek(FILE *fp, long offset, int whence)
} }
} }
static void cleanup(void) /*
* Free SP package structure
*/
static void cleanup(struct sp_pkg_info *sp)
{ {
struct sp_entry_info *sp = sp_info_head;
while (sp != NULL) { if (sp != NULL) {
struct sp_entry_info *next = sp->next; if (sp->img_data != NULL) {
free(sp->img_data);
}
if (sp->sp_data != NULL) if (sp->pm_data != NULL) {
free(sp->sp_data); free(sp->pm_data);
}
if (sp->rd_data != NULL)
free(sp->rd_data);
free(sp); free(sp);
sp = next;
} }
}
sp_count = 0; /*
sp_info_head = NULL; * Free argument list structure
*/
static void freelist(struct arg_list *head)
{
struct arg_list *tmp;
while (head != NULL) {
tmp = head;
head = head->next;
free(tmp);
}
}
/*
* Append user inputs in argument list structure
*/
static void append_user_input(struct arg_list **head, char *args)
{
struct arg_list *tmp = *head;
if (tmp == NULL) {
tmp = xzalloc(sizeof(struct arg_list),
"Failed to allocate arg_list struct");
tmp->usr_input = args;
*head = tmp;
} else {
while (tmp->next != NULL) {
tmp = tmp->next;
}
tmp->next = xzalloc(sizeof(struct arg_list),
"Failed to allocate arg_list struct");
tmp = tmp->next;
tmp->usr_input = args;
}
} }
/* /*
@ -116,7 +153,7 @@ static void cleanup(void)
* load the file into it. Fill 'size' with the file size. Exit the program on * load the file into it. Fill 'size' with the file size. Exit the program on
* error. * error.
*/ */
static void load_file(const char *path, void **ptr, uint64_t *size) static void load_file(const char *path, void **ptr, uint32_t *size)
{ {
FILE *f = fopen(path, "rb"); FILE *f = fopen(path, "rb");
if (f == NULL) { if (f == NULL) {
@ -147,59 +184,40 @@ static void load_file(const char *path, void **ptr, uint64_t *size)
fclose(f); fclose(f);
} }
static void load_sp_rd(char *path) /*
* Parse the string containing input payloads and fill in the
* SP Package data structure.
*/
static void load_sp_pm(char *path, struct sp_pkg_info **sp_out)
{ {
struct sp_pkg_info *sp_pkg;
char *split_mark = strstr(path, ":"); char *split_mark = strstr(path, ":");
*split_mark = '\0'; *split_mark = '\0';
char *sp_path = path; char *sp_path = path;
char *rd_path = split_mark + 1; char *pm_path = split_mark + 1;
struct sp_entry_info *sp; sp_pkg = xzalloc(sizeof(struct sp_pkg_info),
"Failed to allocate sp_pkg_info struct");
if (sp_info_head == NULL) { load_file(pm_path, &sp_pkg->pm_data, &sp_pkg->pm_size);
sp_info_head = xzalloc(sizeof(struct sp_entry_info), printf("\nLoaded SP Manifest file %s (%u bytes)\n", pm_path, sp_pkg->pm_size);
"Failed to allocate sp_entry_info struct");
sp = sp_info_head; load_file(sp_path, &sp_pkg->img_data, &sp_pkg->img_size);
} else { printf("Loaded SP Image file %s (%u bytes)\n", sp_path, sp_pkg->img_size);
sp = sp_info_head;
while (sp->next != NULL) { *sp_out = sp_pkg;
sp = sp->next;
}
sp->next = xzalloc(sizeof(struct sp_entry_info),
"Failed to allocate sp_entry_info struct");
sp = sp->next;
}
load_file(sp_path, &sp->sp_data, &sp->sp_size);
printf("Loaded image file %s (%lu bytes)\n", sp_path, sp->sp_size);
load_file(rd_path, &sp->rd_data, &sp->rd_size);
printf("Loaded RD file %s (%lu bytes)\n", rd_path, sp->rd_size);
sp_count++;
} }
static void output_write(const char *path) /*
* Write SP package data structure into output file.
*/
static void output_write(const char *path, struct sp_pkg_info *sp, bool header)
{ {
struct sp_entry_info *sp; struct sp_pkg_header sp_header_info;
unsigned int file_ptr = 0;
if (sp_count == 0) {
fprintf(stderr, "error: At least one SP must be provided.\n");
exit(1);
}
/* The layout of the structs is specified in the header file sptool.h */
printf("Writing %lu partitions to output file.\n", sp_count);
unsigned int header_size = (sizeof(struct sp_pkg_header) * 8)
+ (sizeof(struct sp_pkg_entry) * 8 * sp_count);
FILE *f = fopen(path, "wb"); FILE *f = fopen(path, "wb");
if (f == NULL) { if (f == NULL) {
@ -207,70 +225,46 @@ static void output_write(const char *path)
exit(1); exit(1);
} }
unsigned int file_ptr = align_to(header_size, PAGE_SIZE); /* Reserve Header size */
if (header) {
/* First, save all partition images aligned to page boundaries */ file_ptr = sizeof(struct sp_pkg_header);
sp = sp_info_head;
for (uint64_t i = 0; i < sp_count; i++) {
xfseek(f, file_ptr, SEEK_SET);
printf("Writing image %lu to offset 0x%x (0x%lx bytes)\n",
i, file_ptr, sp->sp_size);
sp->sp_offset = file_ptr;
xfwrite(sp->sp_data, sp->sp_size, f);
file_ptr = align_to(file_ptr + sp->sp_size, PAGE_SIZE);
sp = sp->next;
} }
/* Now, save resource description blobs aligned to 8 bytes */ /* Save partition manifest */
xfseek(f, file_ptr, SEEK_SET);
printf("Writing SP Manifest at offset 0x%x (%u bytes)\n",
file_ptr, sp->pm_size);
sp = sp_info_head; sp->pm_offset = file_ptr;
xfwrite(sp->pm_data, sp->pm_size, f);
for (uint64_t i = 0; i < sp_count; i++) { /* Save partition image aligned to Page size */
xfseek(f, file_ptr, SEEK_SET); file_ptr = align_to((sp->pm_offset + sp->pm_size), PAGE_SIZE);
xfseek(f, file_ptr, SEEK_SET);
printf("Writing SP Image at offset 0x%x (%u bytes)\n",
file_ptr, sp->img_size);
printf("Writing RD blob %lu to offset 0x%x (0x%lx bytes)\n", sp->img_offset = file_ptr;
i, file_ptr, sp->rd_size); xfwrite(sp->img_data, sp->img_size, f);
sp->rd_offset = file_ptr; /* Finally, write header, if needed */
xfwrite(sp->rd_data, sp->rd_size, f); if (header) {
file_ptr = align_to(file_ptr + sp->rd_size, 8); sp_header_info.magic = SECURE_PARTITION_MAGIC;
sp = sp->next; sp_header_info.version = 0x1;
} sp_header_info.img_offset = sp->img_offset;
sp_header_info.img_size = sp->img_size;
sp_header_info.pm_offset = sp->pm_offset;
sp_header_info.pm_size = sp->pm_size;
/* Finally, write header */ xfseek(f, 0, SEEK_SET);
uint64_t version = 0x1; printf("Writing package header\n");
uint64_t sp_num = sp_count;
xfseek(f, 0, SEEK_SET); xfwrite(&sp_header_info, sizeof(struct sp_pkg_header), f);
xfwrite(&version, sizeof(uint64_t), f);
xfwrite(&sp_num, sizeof(uint64_t), f);
sp = sp_info_head;
for (unsigned int i = 0; i < sp_count; i++) {
uint64_t sp_offset, sp_size, rd_offset, rd_size;
sp_offset = sp->sp_offset;
sp_size = align_to(sp->sp_size, PAGE_SIZE);
rd_offset = sp->rd_offset;
rd_size = sp->rd_size;
xfwrite(&sp_offset, sizeof(uint64_t), f);
xfwrite(&sp_size, sizeof(uint64_t), f);
xfwrite(&rd_offset, sizeof(uint64_t), f);
xfwrite(&rd_size, sizeof(uint64_t), f);
sp = sp->next;
} }
/* All information has been written now */ /* All information has been written now */
printf("\nsptool: Built Secure Partition blob %s\n", path);
fclose(f); fclose(f);
} }
@ -286,30 +280,51 @@ static void usage(void)
#endif #endif
printf(" [<args>]\n\n"); printf(" [<args>]\n\n");
printf("This tool takes as inputs several image binary files and the\n" printf("This tool takes as input set of image binary files and the\n"
"resource description blobs as input and generates a package\n" "partition manifest blobs as input and generates set of\n"
"file that contains them.\n\n"); "output package files\n"
"Usage example: sptool -i sp1.bin:sp1.dtb -o sp1.pkg\n"
" -i sp2.bin:sp2.dtb -o sp2.pkg ...\n\n");
printf("Commands supported:\n"); printf("Commands supported:\n");
printf(" -o <path> Set output file path.\n"); printf(" -o <path> Set output file path.\n");
printf(" -i <sp_path:rd_path> Add Secure Partition image and Resource\n" printf(" -i <sp_path:pm_path> Add Secure Partition image and\n"
" Description blob (specified in two paths\n" " Manifest blob (specified in two paths\n"
" separated by a colon).\n"); " separated by a colon).\n");
printf(" -n Generate package without header\n");
printf(" -h Show this message.\n"); printf(" -h Show this message.\n");
exit(1); exit(1);
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int ch; struct sp_pkg_info *sp_pkg = NULL;
const char *outname = NULL; struct arg_list *in_head = NULL;
struct arg_list *out_head = NULL;
struct arg_list *in_list = NULL;
struct arg_list *out_list = NULL;
unsigned int match_counter = 0;
bool need_header = true;
while ((ch = getopt(argc, argv, "hi:o:")) != -1) { int ch;
if (argc <= 1) {
fprintf(stderr, "error: File paths must be provided.\n\n");
usage();
return 1;
}
while ((ch = getopt(argc, argv, "hni:o:")) != -1) {
switch (ch) { switch (ch) {
case 'i': case 'i':
load_sp_rd(optarg); append_user_input(&in_head, optarg);
match_counter++;
break; break;
case 'o': case 'o':
outname = optarg; append_user_input(&out_head, optarg);
match_counter--;
break;
case 'n':
need_header = false;
break; break;
case 'h': case 'h':
default: default:
@ -317,18 +332,29 @@ int main(int argc, char *argv[])
} }
} }
argc -= optind; if (match_counter) {
argv += optind; fprintf(stderr, "error: Input/Output count mismatch.\n\n");
freelist(in_head);
if (outname == NULL) { freelist(out_head);
fprintf(stderr, "error: An output file path must be provided.\n\n");
usage(); usage();
return 1; return 1;
} }
output_write(outname); in_list = in_head;
out_list = out_head;
while (in_list != NULL) {
load_sp_pm(in_list->usr_input, &sp_pkg);
output_write(out_list->usr_input, sp_pkg, need_header);
in_list = in_list->next;
out_list = out_list->next;
}
cleanup(); argc -= optind;
argv += optind;
cleanup(sp_pkg);
freelist(in_head);
freelist(out_head);
return 0; return 0;
} }