/* * This file is part of the coreboot project. * * Copyright (C) 2015 - 2016 Advanced Micro Devices, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ /* * ROMSIG At ROMBASE + 0x20000: * 0 4 8 C * +------------+---------------+----------------+------------+ * | 0x55AA55AA |EC ROM Address |GEC ROM Address |USB3 ROM | * +------------+---------------+----------------+------------+ * | PSPDIR ADDR|PSPDIR ADDR |<-- Field 0x14 could be either * +------------+---------------+ 2nd PSP directory or PSP COMBO directory * EC ROM should be 64K aligned. * * PSP directory (Where "PSPDIR ADDR" points) * +------------+---------------+----------------+------------+ * | 'PSP$' | Fletcher | Count | Reserved | * +------------+---------------+----------------+------------+ * | 0 | size | Base address | Reserved | Pubkey * +------------+---------------+----------------+------------+ * | 1 | size | Base address | Reserved | Bootloader * +------------+---------------+----------------+------------+ * | 8 | size | Base address | Reserved | Smu Firmware * +------------+---------------+----------------+------------+ * | 3 | size | Base address | Reserved | Recovery Firmware * +------------+---------------+----------------+------------+ * | | * | | * | Other PSP Firmware | * | | * | | * +------------+---------------+----------------+------------+ * * PSP Combo directory * +------------+---------------+----------------+------------+ * | 'PSP2' | Fletcher | Count |Look up mode| * +------------+---------------+----------------+------------+ * | R e s e r v e d | * +------------+---------------+----------------+------------+ * | ID-Sel | PSP ID | PSPDIR ADDR | | 2nd PSP directory * +------------+---------------+----------------+------------+ * | ID-Sel | PSP ID | PSPDIR ADDR | | 3rd PSP directory * +------------+---------------+----------------+------------+ * | | * | Other PSP | * | | * +------------+---------------+----------------+------------+ * */ #include #include #include #include #include #include #include #include #include #ifndef CONFIG_ROM_SIZE #define CONFIG_ROM_SIZE 0x400000 #endif #define AMD_ROMSIG_OFFSET 0x20000 #define MIN_ROM_KB 256 #define ALIGN(val, by) (((val) + (by)-1)&~((by)-1)) /* Reserved for future. TODO: PSP2 is for Combo BIOS, which is the idea that one image supports 2 kinds of APU. */ #define PSP2 1 #if PSP2 /* Use PSP combo directory or not. * Currently we dont have to squeeze 3 PSP directories into 1 image. So * we skip the combo directory. */ #define PSP_COMBO 0 #endif typedef unsigned int uint32_t; typedef unsigned char uint8_t; typedef unsigned short uint16_t; /* * Creates the OSI Fletcher checksum. See 8473-1, Appendix C, section C.3. * The checksum field of the passed PDU does not need to be reset to zero. * * The "Fletcher Checksum" was proposed in a paper by John G. Fletcher of * Lawrence Livermore Labs. The Fletcher Checksum was proposed as an * alternative to cyclical redundancy checks because it provides error- * detection properties similar to cyclical redundancy checks but at the * cost of a simple summation technique. Its characteristics were first * published in IEEE Transactions on Communications in January 1982. One * version has been adopted by ISO for use in the class-4 transport layer * of the network protocol. * * This program expects: * stdin: The input file to compute a checksum for. The input file * not be longer than 256 bytes. * stdout: Copied from the input file with the Fletcher's Checksum * inserted 8 bytes after the beginning of the file. * stderr: Used to print out error messages. */ static uint32_t fletcher32(const uint16_t *pptr, int length) { uint32_t c0; uint32_t c1; uint32_t checksum; int index; c0 = 0xFFFF; c1 = 0xFFFF; for (index = 0; index < length; index++) { /* * Ignore the contents of the checksum field. */ c0 += *(pptr++); c1 += c0; if ((index % 360) == 0) { c0 = (c0 & 0xFFFF) + (c0 >> 16); // Sum0 modulo 65535 + the overflow c1 = (c1 & 0xFFFF) + (c1 >> 16); // Sum1 modulo 65535 + the overflow } } c0 = (c0 & 0xFFFF) + (c0 >> 16); // Sum0 modulo 65535 + the overflow c1 = (c1 & 0xFFFF) + (c1 >> 16); // Sum1 modulo 65535 + the overflow checksum = (c1 << 16) | c0; return checksum; } static void usage(void) { printf("Create AMD Firmware combination\n"); } typedef enum _amd_fw_type { AMD_FW_PSP_PUBKEY = 0, AMD_FW_PSP_BOOTLOADER = 1, AMD_FW_PSP_SMU_FIRMWARE = 8, AMD_FW_PSP_RECOVERY = 3, AMD_FW_PSP_RTM_PUBKEY = 5, AMD_FW_PSP_SECURED_OS = 2, AMD_FW_PSP_NVRAM = 4, AMD_FW_PSP_SECURED_DEBUG = 9, AMD_FW_PSP_TRUSTLETS = 12, AMD_FW_PSP_TRUSTLETKEY = 13, AMD_FW_PSP_SMU_FIRMWARE2 = 18, AMD_PSP_FUSE_CHAIN = 11, AMD_FW_PSP_SMUSCS = 95, AMD_FW_IMC, AMD_FW_GEC, AMD_FW_XHCI, AMD_FW_INVALID, } amd_fw_type; typedef struct _amd_fw_entry { amd_fw_type type; char *filename; } amd_fw_entry; static amd_fw_entry amd_psp_fw_table[] = { { .type = AMD_FW_PSP_PUBKEY }, { .type = AMD_FW_PSP_BOOTLOADER }, { .type = AMD_FW_PSP_SMU_FIRMWARE }, { .type = AMD_FW_PSP_RECOVERY }, { .type = AMD_FW_PSP_RTM_PUBKEY }, { .type = AMD_FW_PSP_SECURED_OS }, { .type = AMD_FW_PSP_NVRAM }, { .type = AMD_FW_PSP_SECURED_DEBUG }, { .type = AMD_FW_PSP_TRUSTLETS }, { .type = AMD_FW_PSP_TRUSTLETKEY }, { .type = AMD_FW_PSP_SMU_FIRMWARE2 }, { .type = AMD_FW_PSP_SMUSCS }, { .type = AMD_PSP_FUSE_CHAIN }, { .type = AMD_FW_INVALID }, }; #if PSP2 static amd_fw_entry amd_psp2_fw_table[] = { { .type = AMD_FW_PSP_PUBKEY }, { .type = AMD_FW_PSP_BOOTLOADER }, { .type = AMD_FW_PSP_SMU_FIRMWARE }, { .type = AMD_FW_PSP_RECOVERY }, { .type = AMD_FW_PSP_RTM_PUBKEY }, { .type = AMD_FW_PSP_SECURED_OS }, { .type = AMD_FW_PSP_NVRAM }, { .type = AMD_FW_PSP_SECURED_DEBUG }, { .type = AMD_FW_PSP_TRUSTLETS }, { .type = AMD_FW_PSP_TRUSTLETKEY }, { .type = AMD_FW_PSP_SMU_FIRMWARE2 }, { .type = AMD_FW_PSP_SMUSCS }, { .type = AMD_PSP_FUSE_CHAIN }, { .type = AMD_FW_INVALID }, }; #endif static amd_fw_entry amd_fw_table[] = { { .type = AMD_FW_XHCI }, { .type = AMD_FW_IMC }, { .type = AMD_FW_GEC }, { .type = AMD_FW_INVALID }, }; static void fill_psp_head(uint32_t *pspdir, uint32_t count) { pspdir[0] = 0x50535024; /* 'PSP$' */ pspdir[2] = count; /* size */ pspdir[3] = 0; pspdir[1] = fletcher32((uint16_t *)&pspdir[1], (count *16 + 16)/2 - 2); } static uint32_t integrate_firmwares(char *base, uint32_t pos, uint32_t *romsig, amd_fw_entry *fw_table, uint32_t rom_size) { int fd; struct stat fd_stat; int i; uint32_t rom_base_address = 0xFFFFFFFF - rom_size + 1; for (i = 0; fw_table[i].type != AMD_FW_INVALID; i ++) { if (fw_table[i].filename != NULL) { fd = open (fw_table[i].filename, O_RDONLY); fstat(fd, &fd_stat); switch (fw_table[i].type) { case AMD_FW_IMC: pos = ALIGN(pos, 0x10000U); romsig[1] = pos + rom_base_address; break; case AMD_FW_GEC: romsig[2] = pos + rom_base_address; break; case AMD_FW_XHCI: romsig[3] = pos + rom_base_address; break; default: /* Error */ break; } if (pos + fd_stat.st_size > rom_size) { printf("Error: Specified ROM size of %d" " will not fit %s. Exiting.\n", rom_size, fw_table[i].filename); free(base); exit(1); } read(fd, (void *)(base + pos), (size_t)fd_stat.st_size); pos += fd_stat.st_size; close (fd); pos = ALIGN(pos, 0x100U); } } return pos; } static uint32_t integrate_psp_firmwares(char *base, uint32_t pos, uint32_t *pspdir, amd_fw_entry *fw_table, uint32_t rom_size) { int fd; struct stat fd_stat; unsigned int i; uint32_t rom_base_address = 0xFFFFFFFF - rom_size + 1; for (i = 0; fw_table[i].type != AMD_FW_INVALID; i ++) { if (fw_table[i].type == AMD_PSP_FUSE_CHAIN) { pspdir[4+4*i+0] = fw_table[i].type; pspdir[4+4*i+1] = 0xFFFFFFFF; pspdir[4+4*i+2] = 1; pspdir[4+4*i+3] = 0; } else if (fw_table[i].filename != NULL) { pspdir[4+4*i+0] = fw_table[i].type; fd = open (fw_table[i].filename, O_RDONLY); fstat(fd, &fd_stat); pspdir[4+4*i+1] = (uint32_t)fd_stat.st_size; pspdir[4+4*i+2] = pos + rom_base_address; pspdir[4+4*i+3] = 0; if (pos + fd_stat.st_size > rom_size) { printf("Error: Specified ROM size of %d" " will not fit %s. Exiting.\n", rom_size, fw_table[i].filename); free (base); exit(1); } read(fd, (void *)(base + pos), (size_t)fd_stat.st_size); pos += fd_stat.st_size; close (fd); pos = ALIGN(pos, 0x100U); } else { /* This APU doesn't have this firmware. */ } } fill_psp_head(pspdir, i); return pos; } #if PSP2 static const char *optstring = "x:i:g:p:b:s:r:k:c:n:d:t:u:w:m:P:B:S:R:K:C:N:D:T:U:W:M:o:f:h"; #else static const char *optstring = "x:i:g:p:b:s:r:k:c:n:d:t:u:w:m:o:f:h"; #endif static struct option long_options[] = { {"xhci", required_argument, 0, 'x' }, {"imc", required_argument, 0, 'i' }, {"gec", required_argument, 0, 'g' }, /* PSP */ {"pubkey", required_argument, 0, 'p' }, {"bootloader", required_argument, 0, 'b' }, {"smufirmware", required_argument, 0, 's' }, {"recovery", required_argument, 0, 'r' }, {"rtmpubkey", required_argument, 0, 'k' }, {"secureos", required_argument, 0, 'c' }, {"nvram", required_argument, 0, 'n' }, {"securedebug", required_argument, 0, 'd' }, {"trustlets", required_argument, 0, 't' }, {"trustletkey", required_argument, 0, 'u' }, {"smufirmware2", required_argument, 0, 'w' }, {"smuscs", required_argument, 0, 'm' }, /* TODO: PSP2 */ #if PSP2 {"pubkey2", required_argument, 0, 'P' }, {"bootloader2", required_argument, 0, 'B' }, {"smufirmware_2", required_argument, 0, 'S' }, {"recovery2", required_argument, 0, 'R' }, {"rtmpubkey2", required_argument, 0, 'K' }, {"secureos2", required_argument, 0, 'C' }, {"nvram2", required_argument, 0, 'N' }, {"securedebug2", required_argument, 0, 'D' }, {"trustlets2", required_argument, 0, 'T' }, {"trustletkey2", required_argument, 0, 'U' }, {"smufirmware2_2",required_argument, 0, 'W' }, {"smuscs2", required_argument, 0, 'M' }, #endif {"output", required_argument, 0, 'o' }, {"flashsize", required_argument, 0, 'f' }, {"help", no_argument, 0, 'h' }, {NULL, 0, 0, 0 } }; static void register_fw_filename(amd_fw_type type, char filename[], int pspflag) { unsigned int i; for (i = 0; i < sizeof(amd_fw_table)/sizeof(amd_fw_entry); i++) { if (amd_fw_table[i].type == type) { amd_fw_table[i].filename = filename; return; } } if (pspflag == 1) { for (i = 0; i < sizeof(amd_psp_fw_table)/sizeof(amd_fw_entry); i++) { if (amd_psp_fw_table[i].type == type) { amd_psp_fw_table[i].filename = filename; return; } } } #if PSP2 if (pspflag == 2) { for (i = 0; i < sizeof(amd_psp2_fw_table)/sizeof(amd_fw_entry); i++) { if (amd_psp2_fw_table[i].type == type) { amd_psp2_fw_table[i].filename = filename; return; } } } #endif } int main(int argc, char **argv) { int c, pspflag = 0; #if PSP2 int psp2flag = 0; uint32_t *psp2dir; char *tmp; #endif #if PSP_COMBO int psp2count; #endif char *rom = NULL; uint32_t current; uint32_t *amd_romsig, *pspdir; int targetfd; char *output = NULL; uint32_t rom_size = CONFIG_ROM_SIZE; uint32_t rom_base_address; while (1) { int optindex = 0; c = getopt_long(argc, argv, optstring, long_options, &optindex); if (c == -1) break; switch (c) { case 'x': register_fw_filename(AMD_FW_XHCI, optarg, 0); break; case 'i': register_fw_filename(AMD_FW_IMC, optarg, 0); break; case 'g': register_fw_filename(AMD_FW_GEC, optarg, 0); break; case 'p': register_fw_filename(AMD_FW_PSP_PUBKEY, optarg, 1); pspflag = 1; break; case 'b': register_fw_filename(AMD_FW_PSP_BOOTLOADER, optarg, 1); pspflag = 1; break; case 's': register_fw_filename(AMD_FW_PSP_SMU_FIRMWARE, optarg, 1); pspflag = 1; break; case 'r': register_fw_filename(AMD_FW_PSP_RECOVERY, optarg, 1); pspflag = 1; break; case 'k': register_fw_filename(AMD_FW_PSP_RTM_PUBKEY, optarg, 1); pspflag = 1; break; case 'c': register_fw_filename(AMD_FW_PSP_SECURED_OS, optarg, 1); pspflag = 1; break; case 'n': register_fw_filename(AMD_FW_PSP_NVRAM, optarg, 1); pspflag = 1; break; case 'd': register_fw_filename(AMD_FW_PSP_SECURED_DEBUG, optarg, 1); pspflag = 1; break; case 't': register_fw_filename(AMD_FW_PSP_TRUSTLETS, optarg, 1); pspflag = 1; break; case 'u': register_fw_filename(AMD_FW_PSP_TRUSTLETKEY, optarg, 1); pspflag = 1; break; case 'w': register_fw_filename(AMD_FW_PSP_SMU_FIRMWARE2, optarg, 1); pspflag = 1; break; case 'm': register_fw_filename(AMD_FW_PSP_SMUSCS, optarg, 1); pspflag = 1; break; #if PSP2 case 'P': register_fw_filename(AMD_FW_PSP_PUBKEY, optarg, 2); psp2flag = 1; break; case 'B': register_fw_filename(AMD_FW_PSP_BOOTLOADER, optarg, 2); psp2flag = 1; break; case 'S': register_fw_filename(AMD_FW_PSP_SMU_FIRMWARE, optarg, 2); psp2flag = 1; break; case 'R': register_fw_filename(AMD_FW_PSP_RECOVERY, optarg, 2); psp2flag = 1; break; case 'K': register_fw_filename(AMD_FW_PSP_RTM_PUBKEY, optarg, 2); psp2flag = 1; break; case 'C': register_fw_filename(AMD_FW_PSP_SECURED_OS, optarg, 2); psp2flag = 1; break; case 'N': register_fw_filename(AMD_FW_PSP_NVRAM, optarg, 2); psp2flag = 1; break; case 'D': register_fw_filename(AMD_FW_PSP_SECURED_DEBUG, optarg, 2); psp2flag = 1; break; case 'T': register_fw_filename(AMD_FW_PSP_TRUSTLETS, optarg, 2); psp2flag = 1; break; case 'U': register_fw_filename(AMD_FW_PSP_TRUSTLETKEY, optarg, 2); psp2flag = 1; break; case 'W': register_fw_filename(AMD_FW_PSP_SMU_FIRMWARE2, optarg, 2); psp2flag = 1; break; case 'M': register_fw_filename(AMD_FW_PSP_SMUSCS, optarg, 2); psp2flag = 1; break; #endif case 'o': output = optarg; break; case 'f': rom_size = (uint32_t)strtoul(optarg, &tmp, 16); if (*tmp != '\0') { printf("Error: ROM size specified" " incorrectly (%s)\n\n", optarg); return 1; } break; case 'h': usage(); return 1; default: break; } } if (!output) { printf("Error: Output value is not specified.\n"); usage(); exit(1); } if (rom_size % 1024 != 0) { printf("Error: ROM Size (%d bytes) should be a multiple of" " 1024 bytes.\n", rom_size); exit(1); } if (rom_size < MIN_ROM_KB * 1024) { printf("Error: ROM Size (%dKB) must be at least %dKB.\n", rom_size / 1024, MIN_ROM_KB); exit(1); } printf(" AMDFWTOOL Using ROM size of %dKB\n", rom_size / 1024); rom_base_address = 0xFFFFFFFF - rom_size + 1; rom = malloc(rom_size); if (!rom) exit(1); memset (rom, 0xFF, rom_size); current = AMD_ROMSIG_OFFSET; amd_romsig = (void *)(rom + AMD_ROMSIG_OFFSET); amd_romsig[0] = 0x55AA55AA; /* romsig */ amd_romsig[1] = 0; amd_romsig[2] = 0; amd_romsig[3] = 0; current += 0x20; /* size of ROMSIG */ current = ALIGN(current, 0x1000U); current = integrate_firmwares(rom, current, amd_romsig, amd_fw_table, rom_size); if (pspflag == 1) { current = ALIGN(current, 0x10000U); pspdir = (void *)(rom + current); amd_romsig[4] = current + rom_base_address; current += 0x200; /* Conservative size of pspdir */ current = integrate_psp_firmwares(rom, current, pspdir, amd_psp_fw_table, rom_size); } #if PSP2 if (psp2flag == 1) { current = ALIGN(current, 0x10000U); /* PSP2 dir */ psp2dir = (void *)(rom + current); amd_romsig[5] = current + rom_base_address; current += 0x200; /* Add conservative size of psp2dir. */ #if PSP_COMBO /* TODO: remove the hardcode. */ psp2count = 1; /* Start from 1. */ /* for (; psp2count <= PSP2COUNT; psp2count++, current=ALIGN(current, 0x100)) { */ /* Now the psp2dir is psp combo dir. */ psp2dir[psp2count*4 + 0 + 4] = 0; /* 0 -Compare PSP ID, 1 -Compare chip family ID */ psp2dir[psp2count*4 + 1 + 4] = 0x10220B00; /* TODO: PSP ID. Documentation is needed. */ psp2dir[psp2count * 4 + 2 + 4] = current + rom_base_address; pspdir = rom + current; psp2dir[psp2count*4 + 3 + 4] = 0; current += 0x200; /* Add conservative size of pspdir. Start of PSP entries. */ current = integrate_psp_firmwares(rom, current, pspdir, amd_psp2_fw_table, rom_size); /* } */ /* End of loop */ /* fill the PSP combo head */ psp2dir[0] = 0x50535032; /* 'PSP2' */ psp2dir[2] = psp2count; /* Count */ psp2dir[3] = 1; /* 0-Dynamic look up through all entries, 1-PSP/chip ID match */ psp2dir[4] = 0; /* reserved 4 dwords. */ psp2dir[5] = 0; psp2dir[6] = 0; psp2dir[7] = 0; psp2dir[1] = fletcher32((uint16_t *)&psp2dir[1], (psp2count*16 + 32)/2 - 2); #else current = integrate_psp_firmwares(rom, current, psp2dir, amd_psp2_fw_table, rom_size); #endif } #endif targetfd = open(output, O_RDWR | O_CREAT | O_TRUNC, 0666); write(targetfd, amd_romsig, current - AMD_ROMSIG_OFFSET); close(targetfd); free(rom); return 0; }