From: Cédric Le Goater <c...@fr.ibm.com> This patch adds the logic to read the partition table of a PNOR file and a routine to load the PAYLOAD (skiboot) partition in the machine memory.
Code based on the libflash of skiboot: https://github.com/open-power/skiboot/blob/master/libflash/ Signed-off-by: Cédric Le Goater <c...@kaod.org> --- include/hw/ppc/ffs.h | 150 ++++++++++++++++++++++++++++++++++++ include/hw/ppc/pnv_pnor.h | 6 ++ hw/ppc/pnv.c | 33 +++++--- hw/ppc/pnv_pnor.c | 156 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 334 insertions(+), 11 deletions(-) create mode 100644 include/hw/ppc/ffs.h diff --git a/include/hw/ppc/ffs.h b/include/hw/ppc/ffs.h new file mode 100644 index 000000000000..15216cf2389b --- /dev/null +++ b/include/hw/ppc/ffs.h @@ -0,0 +1,150 @@ +/* + * Copyright 2013-2014 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. + */ +/* + * Copyright (c) International Business Machines Corp., 2012 + * + * FSP Flash Structure + * + * This header defines the layout for the FSP Flash Structure. + */ + +#ifndef FFS_H +#define FFS_H + +#include <stdint.h> + +/* The version of this partition implementation */ +#define FFS_VERSION_1 1 + +/* Magic number for the partition header (ASCII 'PART') */ +#define FFS_MAGIC 0x50415254 + +/* The maximum length of the partition name */ +#define PART_NAME_MAX 15 + +/* + * Sizes of the data structures + */ +#define FFS_HDR_SIZE sizeof(struct ffs_hdr) +#define FFS_ENTRY_SIZE sizeof(struct ffs_entry) + +/* + * Sizes of the data structures w/o checksum + */ +#define FFS_HDR_SIZE_CSUM (FFS_HDR_SIZE - sizeof(uint32_t)) +#define FFS_ENTRY_SIZE_CSUM (FFS_ENTRY_SIZE - sizeof(uint32_t)) + +/* pid of logical partitions/containers */ +#define FFS_PID_TOPLEVEL 0xFFFFFFFF + +/* + * Type of image contained w/in partition + */ +enum type { + FFS_TYPE_DATA = 1, + FFS_TYPE_LOGICAL = 2, + FFS_TYPE_PARTITION = 3, +}; + +/* + * Flag bit definitions + */ +#define FFS_FLAGS_PROTECTED 0x0001 +#define FFS_FLAGS_U_BOOT_ENV 0x0002 + +/* Data integrity flags */ +#define FFS_ENRY_INTEG_ECC 0x8000 + +/** + * struct ffs_entry_user - User data enties + * + * @chip: Chip Select (0,1) + * @compressType: Compression Indication/alg (0=not compressed) + * @dataInteg: Indicates Data Integrity mechanism + * @verCheck: Indicates Version check type + * @miscFlags: Misc Partition related Flags + * @freeMisc[2]: Unused Miscellaneious Info + * @freeUser[14]: Unused User Data + */ +struct ffs_entry_user { + uint8_t chip; + uint8_t compresstype; + uint16_t datainteg; + uint8_t vercheck; + uint8_t miscflags; + uint8_t freemisc[2]; + uint32_t reserved[14]; +}; + +/** + * struct ffs_entry - Partition entry + * + * @name: Opaque null terminated string + * @base: Starting offset of partition in flash (in hdr.block_size) + * @size: Partition size (in hdr.block_size) + * @pid: Parent partition entry (FFS_PID_TOPLEVEL for toplevel) + * @id: Partition entry ID [1..65536] + * @type: Describe type of partition + * @flags: Partition attributes (optional) + * @actual: Actual partition size (in bytes) + * @resvd: Reserved words for future use + * @user: User data (optional) + * @checksum: Partition entry checksum (includes all above) + */ +struct ffs_entry { + char name[PART_NAME_MAX + 1]; + uint32_t base; + uint32_t size; + uint32_t pid; + uint32_t id; + uint32_t type; + uint32_t flags; + uint32_t actual; + uint32_t resvd[4]; + struct ffs_entry_user user; + uint32_t checksum; +} __attribute__ ((packed)); + +/** + * struct ffs_hdr - FSP Flash Structure header + * + * @magic: Eye catcher/corruption detector + * @version: Version of the structure + * @size: Size of partition table (in block_size) + * @entry_size: Size of struct ffs_entry element (in bytes) + * @entry_count: Number of struct ffs_entry elements in @entries array + * @block_size: Size of block on device (in bytes) + * @block_count: Number of blocks on device + * @resvd: Reserved words for future use + * @checksum: Header checksum + * @entries: Pointer to array of partition entries + */ +struct ffs_hdr { + uint32_t magic; + uint32_t version; + uint32_t size; + uint32_t entry_size; + uint32_t entry_count; + uint32_t block_size; + uint32_t block_count; + uint32_t resvd[4]; + uint32_t checksum; + struct ffs_entry entries[]; +} __attribute__ ((packed)); + + +#endif /* FFS_H */ diff --git a/include/hw/ppc/pnv_pnor.h b/include/hw/ppc/pnv_pnor.h index c3dd28643cae..14ec06038f3e 100644 --- a/include/hw/ppc/pnv_pnor.h +++ b/include/hw/ppc/pnv_pnor.h @@ -25,6 +25,12 @@ typedef struct PnvPnor { uint8_t *storage; uint32_t size; MemoryRegion mmio; + + uint32_t skiboot_addr; + uint32_t skiboot_size; } PnvPnor; +extern int pnv_pnor_load_skiboot(PnvPnor *pnor, hwaddr addr, size_t max_size, + Error **errp); + #endif /* _PPC_PNV_PNOR_H */ diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 7019273f1cdd..8cc792dee25d 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -637,6 +637,7 @@ static void pnv_init(MachineState *machine) char *chip_typename; DriveInfo *pnor = drive_get(IF_MTD, 0, 0); DeviceState *dev; + bool load_skiboot_from_pnor = false; /* allocate RAM */ if (machine->ram_size < (1 * GiB)) { @@ -659,23 +660,33 @@ static void pnv_init(MachineState *machine) qdev_init_nofail(dev); pnv->pnor = PNV_PNOR(dev); - /* load skiboot firmware */ + /* + * Try to load skiboot from pnor if no 'bios' was provided on the + * command line. skiboot will load the kernel and initramfs from + * the PNOR. + */ if (bios_name == NULL) { bios_name = FW_FILE_NAME; + load_skiboot_from_pnor = !!pnor; } - fw_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); - if (!fw_filename) { - error_report("Could not find OPAL firmware '%s'", bios_name); - exit(1); - } + if (load_skiboot_from_pnor) { + pnv_pnor_load_skiboot(pnv->pnor, FW_LOAD_ADDR, FW_MAX_SIZE, + &error_fatal); + } else { + fw_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (!fw_filename) { + error_report("Could not find OPAL firmware '%s'", bios_name); + exit(1); + } - fw_size = load_image_targphys(fw_filename, FW_LOAD_ADDR, FW_MAX_SIZE); - if (fw_size < 0) { - error_report("Could not load OPAL firmware '%s'", fw_filename); - exit(1); + fw_size = load_image_targphys(fw_filename, FW_LOAD_ADDR, FW_MAX_SIZE); + if (fw_size < 0) { + error_report("Could not load OPAL firmware '%s'", fw_filename); + exit(1); + } + g_free(fw_filename); } - g_free(fw_filename); /* load kernel */ if (machine->kernel_filename) { diff --git a/hw/ppc/pnv_pnor.c b/hw/ppc/pnv_pnor.c index bfb1e92b0392..c29bf7eea197 100644 --- a/hw/ppc/pnv_pnor.c +++ b/hw/ppc/pnv_pnor.c @@ -14,8 +14,161 @@ #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "hw/loader.h" +#include "hw/ppc/ffs.h" #include "hw/ppc/pnv_pnor.h" #include "hw/qdev-properties.h" +#include "libxz/xz.h" + +static uint32_t ffs_checksum(void *data, size_t size) +{ + uint32_t i, csum = 0; + + for (i = csum = 0; i < (size / 4); i++) { + csum ^= ((uint32_t *)data)[i]; + } + return csum; +} + +static int ffs_check_convert_header(struct ffs_hdr *dst, struct ffs_hdr *src) +{ + dst->magic = be32_to_cpu(src->magic); + if (dst->magic != FFS_MAGIC) { + return -1; + } + dst->version = be32_to_cpu(src->version); + if (dst->version != FFS_VERSION_1) { + return -1; + } + if (ffs_checksum(src, FFS_HDR_SIZE) != 0) { + return -1; + } + dst->size = be32_to_cpu(src->size); + dst->entry_size = be32_to_cpu(src->entry_size); + dst->entry_count = be32_to_cpu(src->entry_count); + dst->block_size = be32_to_cpu(src->block_size); + dst->block_count = be32_to_cpu(src->block_count); + + return 0; +} + +static int ffs_check_convert_entry(struct ffs_entry *dst, struct ffs_entry *src) +{ + if (ffs_checksum(src, FFS_ENTRY_SIZE) != 0) { + return -1; + } + + memcpy(dst->name, src->name, sizeof(dst->name)); + dst->base = be32_to_cpu(src->base); + dst->size = be32_to_cpu(src->size); + dst->pid = be32_to_cpu(src->pid); + dst->id = be32_to_cpu(src->id); + dst->type = be32_to_cpu(src->type); + dst->flags = be32_to_cpu(src->flags); + dst->actual = be32_to_cpu(src->actual); + dst->user.datainteg = be16_to_cpu(src->user.datainteg); + + return 0; +} + +static int decompress(void *dst, size_t dst_size, void *src, size_t src_size, + Error **errp) +{ + struct xz_dec *s; + struct xz_buf b; + int ret = 0; + + /* Initialize the xz library first */ + xz_crc32_init(); + s = xz_dec_init(XZ_SINGLE, 0); + if (!s) { + error_report("failed to initialize xz"); + return -1; + } + + b.in = src; + b.in_pos = 0; + b.in_size = src_size; + b.out = dst; + b.out_pos = 0; + b.out_size = dst_size; + + /* Start decompressing */ + ret = xz_dec_run(s, &b); + if (ret != XZ_STREAM_END) { + error_setg(errp, "failed to decompress : %d", ret); + ret = -1; + } else { + ret = 0; + } + + /* Clean up memory */ + xz_dec_end(s); + return ret; +} + +int pnv_pnor_load_skiboot(PnvPnor *s, hwaddr addr, size_t max_size, + Error **errp) +{ + int rc; + void *buffer = g_malloc0(max_size); + + rc = decompress(buffer, max_size, &s->storage[s->skiboot_addr], + s->skiboot_size, errp); + if (rc == 0) { + rom_add_blob_fixed("pnor.skiboot", buffer, max_size, addr); + } + g_free(buffer); + return rc; +} + +#define SECUREBOOT_HEADER_MAGIC 0x17082011 +#define SECUREBOOT_HEADER_SIZE 4096 + +static void pnv_pnor_find_skiboot(PnvPnor *s, Error **errp) +{ + uint8_t *storage = s->storage; + struct ffs_hdr hdr; + struct ffs_entry ent; + uint32_t magic; + uint32_t i; + int rc; + + rc = ffs_check_convert_header(&hdr, (struct ffs_hdr *) storage); + if (rc) { + error_setg(errp, "bad header"); + return; + } + + for (i = 0; i < hdr.entry_count; i++) { + uint32_t esize = hdr.entry_size; + uint32_t offset = FFS_HDR_SIZE + i * esize; + struct ffs_entry *src_ent = (struct ffs_entry *)(storage + offset); + + rc = ffs_check_convert_entry(&ent, src_ent); + if (rc) { + error_report("bad partition entry %d", i); + continue; + } + + if (strcmp("PAYLOAD", ent.name)) { + continue; + } + + s->skiboot_addr = ent.base * 0x1000, + s->skiboot_size = ent.size * 0x1000; + + /* Check for secure boot header */ + magic = be32_to_cpu(*(uint32_t *)&s->storage[s->skiboot_addr]); + if (magic == SECUREBOOT_HEADER_MAGIC) { + s->skiboot_addr += SECUREBOOT_HEADER_SIZE; + s->skiboot_size -= SECUREBOOT_HEADER_SIZE; + } + + return; + } + + error_setg(errp, "pnv_pnor: no skiboot partition !?"); +} static uint64_t pnv_pnor_read(void *opaque, hwaddr addr, unsigned size) { @@ -97,6 +250,9 @@ static void pnv_pnor_realize(DeviceState *dev, Error **errp) error_setg(errp, "failed to read the initial flash content"); return; } + + /* Read partitions to validate contents */ + pnv_pnor_find_skiboot(s, errp); } else { s->storage = blk_blockalign(NULL, s->size); memset(s->storage, 0xFF, s->size); -- 2.21.0