From patchwork Tue Nov 29 00:38:45 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cyril Bur X-Patchwork-Id: 700237 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3tSPnj17t8z9vDw for ; Tue, 29 Nov 2016 11:39:37 +1100 (AEDT) Received: from lists.ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3tSPnj0THtzDvsy for ; Tue, 29 Nov 2016 11:39:37 +1100 (AEDT) X-Original-To: skiboot@lists.ozlabs.org Delivered-To: skiboot@lists.ozlabs.org Received: from mx0a-001b2d01.pphosted.com (mx0b-001b2d01.pphosted.com [148.163.158.5]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3tSPnR1lRFzDq6B for ; Tue, 29 Nov 2016 11:39:23 +1100 (AEDT) Received: from pps.filterd (m0098420.ppops.net [127.0.0.1]) by mx0b-001b2d01.pphosted.com (8.16.0.17/8.16.0.17) with SMTP id uAT0cV4p007109 for ; Mon, 28 Nov 2016 19:39:20 -0500 Received: from e23smtp05.au.ibm.com (e23smtp05.au.ibm.com [202.81.31.147]) by mx0b-001b2d01.pphosted.com with ESMTP id 270ud3uxwx-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Mon, 28 Nov 2016 19:39:20 -0500 Received: from localhost by e23smtp05.au.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Tue, 29 Nov 2016 10:39:17 +1000 Received: from d23dlp01.au.ibm.com (202.81.31.203) by e23smtp05.au.ibm.com (202.81.31.211) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Tue, 29 Nov 2016 10:39:14 +1000 Received: from d23relay06.au.ibm.com (d23relay06.au.ibm.com [9.185.63.219]) by d23dlp01.au.ibm.com (Postfix) with ESMTP id C892C2CE8054 for ; Tue, 29 Nov 2016 11:39:13 +1100 (EST) Received: from d23av02.au.ibm.com (d23av02.au.ibm.com [9.190.235.138]) by d23relay06.au.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id uAT0dDiW52625438 for ; Tue, 29 Nov 2016 11:39:13 +1100 Received: from d23av02.au.ibm.com (localhost [127.0.0.1]) by d23av02.au.ibm.com (8.14.4/8.14.4/NCO v10.0 AVout) with ESMTP id uAT0dDHJ003546 for ; Tue, 29 Nov 2016 11:39:13 +1100 Received: from ozlabs.au.ibm.com (ozlabs.au.ibm.com [9.192.253.14]) by d23av02.au.ibm.com (8.14.4/8.14.4/NCO v10.0 AVin) with ESMTP id uAT0dDXA003539 for ; Tue, 29 Nov 2016 11:39:13 +1100 Received: from camb691.ozlabs.ibm.com (haven.au.ibm.com [9.192.254.114]) (using TLSv1.2 with cipher DHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ozlabs.au.ibm.com (Postfix) with ESMTPSA id 2D384A01D3 for ; Tue, 29 Nov 2016 11:39:13 +1100 (AEDT) From: Cyril Bur To: skiboot@lists.ozlabs.org Date: Tue, 29 Nov 2016 11:38:45 +1100 X-Mailer: git-send-email 2.10.2 In-Reply-To: <20161129003845.23051-1-cyril.bur@au1.ibm.com> References: <20161129003845.23051-1-cyril.bur@au1.ibm.com> X-TM-AS-MML: disable X-Content-Scanned: Fidelis XPS MAILER x-cbid: 16112900-0016-0000-0000-000001F9E811 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 16112900-0017-0000-0000-000005F4CC55 Message-Id: <20161129003845.23051-4-cyril.bur@au1.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2016-11-28_19:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=4 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1609300000 definitions=main-1611290008 Subject: [Skiboot] [RFC v2 3/3] external/ffspart: Simple C program to be able to make an FFS partition X-BeenThere: skiboot@lists.ozlabs.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Mailing list for skiboot development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: skiboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Skiboot" A typical input file to generate something that current op-build would create: HBI,0x00010000,0x05a0000,EV,./bins/HBI.bin MVPD,0x05b0000,0x0090000,EF,./bins/MVPD.bin CVPD,0x0640000,0x0048000,EF,./bins/CVPD.bin DJVPD,0x688000,0x0048000,EF,./bins/DJVPD.bin HBD,0x006d0000,0x0060000,E,./bins/HBD.bin SBEC,0x0730000,0x0090000,EI,./bins/SBEC.bin SBE,0x007c0000,0x0048000,EI,./bins/SBE.bin ATTR_TMP,0x808000,0x8000,F,./bins/ATTR_TMP.bin ATTR_PERM,0x810000,0x8000,EF,./bins/ATTR_PERM.bin WINK,0x00818000,0x0120000,EV,./bins/WINK.bin GUARD,0x00938000,0x005000,EPF,./bins/GUARD.bin HBEL,0x0093d000,0x0024000,EF,./bins/HBEL.bin PAYLOAD,0x961000,0x100000,,./bins/skiboot.lid BOOTKERNEL,0xa61000,0xf00000,,./bins/petitboot.zImage NVRAM,0x01961000,0x90000,EPF,./bins/NVRAM.bin HBRT,0x019f1000,0x360000,EV,./bins/HBRT.bin OCC,0x001d51000,0x120000,E,./bins/OCC.bin FIRDATA,0x1e71000,0x3000,EF,./bins/FIRDATA.bin CAPP,0x001e74000,0x24000,E,./bins/CAPP.bin HBB,0x0001f67000,0x90000,EV,./bins/HBB.bin VERSION,0x1ff7000,0x1000,,./bins/VERSION2.bin Signed-off-by: Cyril Bur --- external/ffspart/Makefile | 40 +++++ external/ffspart/ffspart.c | 416 +++++++++++++++++++++++++++++++++++++++++++++ external/ffspart/rules.mk | 33 ++++ 3 files changed, 489 insertions(+) create mode 100644 external/ffspart/Makefile create mode 100644 external/ffspart/ffspart.c create mode 100644 external/ffspart/rules.mk diff --git a/external/ffspart/Makefile b/external/ffspart/Makefile new file mode 100644 index 0000000..4b71081 --- /dev/null +++ b/external/ffspart/Makefile @@ -0,0 +1,40 @@ +# Use make V=1 for a verbose build. +GET_ARCH = ../../external/common/get_arch.sh +include ../../external/common/rules.mk +include rules.mk + +all: $(EXE) + +.PHONY: links +links: libflash ccan common + +$(OBJS): | links arch_links + +.PHONY: VERSION-always +.version: VERSION-always + @echo $(FFSPART_VERSION) > $@.tmp + @cmp -s $@ $@.tmp || cp $@.tmp $@ + @rm -f $@.tmp + +install: all + install -D ffspart $(DESTDIR)$(sbindir)/ffspart + +.PHONY: dist +#File is named $(FFSPART_VERSION).tar because the expectation is that ffspart- +#is always at the start of the verion. This remains consistent with skiboot +#version strings +dist: links .version + find -L ../ffspart/ -iname '*.[ch]' -print0 | xargs -0 tar -rhf $(FFSPART_VERSION).tar + tar --transform 's/Makefile.dist/Makefile/' -rhf $(FFSPART_VERSION).tar \ + ../ffspart/Makefile.dist ../ffspart/rules.mk \ + ../ffspart/.version ../ffspart/make_version.sh \ + ../ffspart/common/* + +.PHONY: clean +clean: arch_clean + rm -f $(OBJS) $(EXE) *.o *.d +.PHONY: distclean +distclean: clean + rm -f *.c~ *.h~ *.sh~ Makefile~ config.mk~ libflash/*.c~ libflash/*.h~ + rm -f libflash ccan .version .version.tmp + rm -f common io.h diff --git a/external/ffspart/ffspart.c b/external/ffspart/ffspart.c new file mode 100644 index 0000000..beb338e --- /dev/null +++ b/external/ffspart/ffspart.c @@ -0,0 +1,416 @@ +/* Copyright 2013-2016 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * Flags: + * - E: ECC for this part + */ + +/* + * TODO FIXME + * Max line theoretical max size: + * - name: 15 chars = 15 + * - base: 0xffffffff = 10 + * - size: 0xffffffff = 10 + * - flag: E = 1 + * + * 36 + 3 separators = 39 + * Plus \n 40 + * Lets do 50. + */ +#define MAX_LINE 100 +#define SEPARATOR ',' + +enum order { + ORDER_ADB, + ORDER_ABD +}; + +/* Full version number (possibly includes gitid). */ +extern const char version[]; + +static void print_version(void) +{ + printf("Open-Power FFS format tool %s\n", version); +} + +static void print_help(const char *pname) +{ + print_version(); + printf("Usage: %s [options] -b size -c num -i layout_file -p pnor_file ...\n\n", pname); + printf(" Options:\n"); + printf("\t-b, --block_size=size\n"); + printf("\t\tSize (in hex with leading 0x) of the blocks on the flash in bytes\n\n"); + printf("\t-c, --block_count=num\n"); + printf("\t\tNumber of blocks on the flash\n\n"); + printf("\t-i, --input=file\n"); + printf("\t\tFile containing the required partition data\n\n"); + printf("\t-o, --order=( ADB | ABD )\n"); + printf("\t\tOrdering of the TOC, Data and Backup TOC. Currently only ADB (default)\n"); + printf("\t\tis supported\n"); + printf("\t-p, -pnor=file\n"); + printf("\t\tOutput file to write data\n\n"); + printf("\t-s, -sides=( 1 | 2 )\n"); + printf("\t\tNumber of sides to the flash (Default: 1)\n"); +} + +int main(int argc, char *argv[]) +{ + const char *pname = argv[0]; + struct blocklevel_device *bl = NULL; + unsigned int sides = 1; + uint32_t block_size = 0, block_count = 0; + enum order order = ORDER_ADB; + bool bad_input = false, backup_part = false; + char *pnor = NULL, *input = NULL; + struct ffs_hdr *new_hdr; + FILE *in_file; + char line[MAX_LINE]; + int rc; + + while(1) { + struct option long_opts[] = { + {"backup", no_argument, NULL, 'b'}, + {"block_size", required_argument, NULL, 's'}, + {"block_count", required_argument, NULL, 'c'}, + {"input", required_argument, NULL, 'i'}, + {"order", required_argument, NULL, 'o'}, + {"pnor", required_argument, NULL, 'p'}, + {"tocs", required_argument, NULL, 't'}, + {NULL, 0, 0, 0} + }; + int c, oidx = 0; + + c = getopt_long(argc, argv, "bc:i:o:p:s:t:", long_opts, &oidx); + if (c == EOF) + break; + switch(c) { + case 'b': + backup_part = true; + break; + case 'c': + block_count = strtoul(optarg, NULL, 0); + break; + case 'i': + input = strdup(optarg); + break; + case 'o': + if (strncmp(optarg, "ABD", 3) == 0) + order = ORDER_ABD; + else if (strncmp(optarg, "ADB", 3) == 0) + order = ORDER_ADB; + else + bad_input = true; + break; + case 'p': + pnor = strdup(optarg); + break; + case 's': + block_size = strtoul(optarg, NULL, 0); + break; + case 't': + sides = strtoul(optarg, NULL, 0); + break; + default: + exit(1); + } + } + + if (sides == 0) + sides = 1; + + if (sides > 2) { + fprintf(stderr, "Greater than two sides is not supported\n"); + bad_input = true; + } + + if (!block_size || !block_count || !input || !pnor) + bad_input = true; + + /* TODO Check assumption that sides divide the flash in half. */ + if (block_count % sides) { + fprintf(stderr, "Invalid block_count %u for sides %u\n", block_count, sides); + bad_input = true; + } + + if (bad_input || order == ORDER_ABD) { + print_help(pname); + rc = 1; + goto out; + } + + /* + * TODO: Rethink, is ffspart providing a variable TOC size useful? + * Use 1 block for the size of the partition table... + */ + rc = ffs_hdr_new(block_size, block_size, block_count / sides, &new_hdr); + if (rc) { + if (rc == FFS_ERR_BAD_SIZE) { + /* Well this check is a tad redudant now */ + fprintf(stderr, "Bad size parametres passed to libffs: " + "size must be a multiple of block_size\n" + "size (%u), block_size (%u) \n", block_size, block_size); + } else { + fprintf(stderr, "Error %d initialising new TOC\n", rc); + } + goto out; + } + + if (sides == 2) { + rc = ffs_hdr_add_side(new_hdr); + if (rc) { + fprintf(stderr, "Couldn't add side to header\n"); + goto out_free_hdr; + } + } + + in_file = fopen(input, "r"); + if (!in_file) { + rc = errno; + fprintf(stderr, "Couldn't open your input file %s because %s\n", input, strerror(errno)); + goto out_free_hdr; + } + + rc = arch_flash_init(&bl, pnor, true); + if (rc) { + fprintf(stderr, "Couldn't initialise architecture flash structures\n"); + goto out_close_f; + } + + /* + * 'Erase' the file, make it all 0xFF + * TODO: Add sparse option and don't do this. + */ + rc = blocklevel_erase(bl, 0, block_size * block_count); + if (rc) { + fprintf(stderr, "Couldn't erase file\n"); + goto out_close_bl; + } + + while (fgets(line, MAX_LINE, in_file) != NULL) { + struct ffs_entry *new_entry; + struct ffs_entry_user user = { 0 }; + char *pos, *old_pos; + char *name; + int side = -1; + uint32_t pbase, psize, pactual = 0; + + if (line[strlen(line) - 1] == '\n') + line[strlen(line) - 1] = '\0'; + + pos = strchr(line, SEPARATOR); + if (!pos) { + fprintf(stderr, "Invalid input file format: Couldn't find name\n"); + rc = -1; + goto out_close_bl; + } + *pos = '\0'; + name = line; + + pos++; + old_pos = pos; + pos = strchr(pos, SEPARATOR); + if (!pos) { + fprintf(stderr, "Invalid input file format: Couldn't find base\n"); + rc = -1; + goto out_close_bl; + } + *pos = '\0'; + pbase = strtoul(old_pos, NULL, 0); + + pos++; + old_pos = pos; + pos = strchr(pos, SEPARATOR); + if (!pos) { + fprintf(stderr, "Invalid input file format: Couldn't find size\n"); + rc = -1; + goto out_close_bl; + } + *pos = '\0'; + psize = strtoul(old_pos, NULL, 0); + + pos++; + while (*pos != '\0' && *pos != SEPARATOR) { + switch (*pos) { + case 'E': + user.datainteg |= FFS_ENRY_INTEG_ECC; + break; + case 'V': + user.vercheck |= FFS_VERCHECK_SHA512V; + break; + case 'I': + user.vercheck |= FFS_VERCHECK_SHA512EC; + break; + case 'P': + user.miscflags |= FFS_MISCFLAGS_PRESERVED; + break; + case 'R': + user.miscflags |= FFS_MISCFLAGS_READONLY; + break; + case 'F': + user.miscflags |= FFS_MISCFLAGS_REPROVISION; + break; + /* Not sure these are valid */ + case 'B': + user.miscflags |= FFS_MISCFLAGS_BACKUP; + break; + case '0': + case '1': + case '2': + /* + * There should only be one side specified, fail if + * we've already seen a side + */ + if (side != -1) { + rc = -1; + goto out_close_bl; + } else { + side = *pos - '0'; + } + break; + default: + fprintf(stderr, "Unknown flag '%c'\n", *pos); + } + pos++; + } + + if (side == -1) /* Default to 0 */ + side = 0; + + printf("Adding '%s' 0x%08x, 0x%08x\n", name, pbase, psize); + rc = ffs_entry_new(name, pbase, psize, &new_entry); + if (rc) { + fprintf(stderr, "Invalid entry '%s' 0x%08x for 0x%08x\n", + name, pbase, psize); + goto out_close_bl; + } + + rc = ffs_entry_user_set(new_entry, &user); + if (rc) { + fprintf(stderr, "Invalid flag passed to ffs_entry_user_set\n"); + goto out_while; + } + + rc = ffs_entry_add(new_hdr, new_entry, side); + if (rc) { + fprintf(stderr, "Couldn't add entry '%s' 0x%08x for 0x%08x\n", + name, pbase, psize); + goto out_while; + } + + if (*pos != '\0') { + struct stat data_stat; + int data_fd; + uint8_t *data_ptr; + char *data_fname = pos + 1; + + data_fd = open(data_fname, O_RDONLY); + if (data_fd == -1) { + fprintf(stderr, "Couldn't open data file for partition '%s' (filename: %s)\n", + name, data_fname); + rc = -1; + goto out_while; + } + + if (fstat(data_fd, &data_stat) == -1) { + fprintf(stderr, "Couldn't stat data file for partition '%s': %s\n", + name, strerror(errno)); + rc = -1; + goto out_if; + } + pactual = data_stat.st_size; + + /* + * Sanity check that the file isn't too large for + * partition + */ + if (pactual > psize) { + fprintf(stderr, "Data file for partition '%s' is too large\n", + name); + rc = -1; + goto out_if; + } + + data_ptr = mmap(NULL, pactual, PROT_READ, MAP_SHARED, data_fd, 0); + if (!data_ptr) { + fprintf(stderr, "Couldn't mmap data file for partition '%s': %s\n", + name, strerror(errno)); + rc = -1; + goto out_if; + } + + rc = blocklevel_write(bl, pbase, data_ptr, pactual); + if (rc) + fprintf(stderr, "Couldn't write data file for partition '%s' to pnor file:" + " %s\n", name, strerror(errno)); + + munmap(data_ptr, pactual); +out_if: + close(data_fd); + if (rc) + goto out_while; + /* + * TODO: Update the actual size within the partition table. + */ + } + + continue; +out_while: + free(new_entry); + goto out_close_bl; + } + + if (backup_part) { + rc = ffs_hdr_create_backup(new_hdr); + if (rc) { + fprintf(stderr, "Failed to create backup part\n"); + goto out_close_bl; + } + } + + rc = ffs_hdr_finalise(bl, new_hdr); + if (rc) + fprintf(stderr, "Failed to write out TOC values\n"); + +out_close_bl: + arch_flash_close(bl, pnor); +out_close_f: + fclose(in_file); +out_free_hdr: + ffs_hdr_free(new_hdr); +out: + free(input); + free(pnor); + return rc; +} diff --git a/external/ffspart/rules.mk b/external/ffspart/rules.mk new file mode 100644 index 0000000..7d028ea --- /dev/null +++ b/external/ffspart/rules.mk @@ -0,0 +1,33 @@ +.DEFAULT_GOAL := all + +override CFLAGS += -O2 -Wall -g -I. +EXE = ffspart +OBJS = $(EXE).o version.o +LIBFLASH_FILES := libflash.c libffs.c ecc.c blocklevel.c file.c +LIBFLASH_OBJS := $(addprefix libflash-, $(LIBFLASH_FILES:.c=.o)) +LIBFLASH_SRC := $(addprefix libflash/,$(LIBFLASH_FILES)) +OBJS += $(LIBFLASH_OBJS) +OBJS += common-arch_flash.o + +CC = $(CROSS_COMPILE)gcc + +FFSPART_VERSION ?= $(shell ../../make_version.sh $(EXE)) + +version.c: .version + @(if [ "a$(FFSPART_VERSION)" = "a" ]; then \ + echo "#error You need to set FFSPART_VERSION environment variable" > $@ ;\ + else \ + echo "const char version[] = \"$(FFSPART_VERSION)\";" ;\ + fi) > $@ + +%.o : %.c + $(Q_CC)$(CC) $(CFLAGS) -c $< -o $@ + +$(LIBFLASH_SRC): | links + +$(LIBFLASH_OBJS): libflash-%.o : libflash/%.c + $(Q_CC)$(CC) $(CFLAGS) -c $< -o $@ + +$(EXE): $(OBJS) + $(Q_CC)$(CC) $(CFLAGS) $^ -lrt -o $@ +