Nico Huber | 413b0d9 | 2013-06-19 12:41:19 +0200 | [diff] [blame] | 1 | /* |
| 2 | * ifdfake - Create an Intel Firmware Descriptor with just a section layout |
| 3 | * |
| 4 | * Copyright (C) 2013 secunet Security Networks AG |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by |
| 8 | * the Free Software Foundation; version 2 of the License. |
| 9 | * |
| 10 | * This program is distributed in the hope that it will be useful, |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | * GNU General Public License for more details. |
Nico Huber | 413b0d9 | 2013-06-19 12:41:19 +0200 | [diff] [blame] | 14 | */ |
| 15 | |
| 16 | #include <errno.h> |
| 17 | #include <stdio.h> |
| 18 | #include <stdint.h> |
| 19 | #include <stdlib.h> |
| 20 | #include <string.h> |
| 21 | #include <getopt.h> |
| 22 | |
Nico Huber | e70bfee | 2016-11-16 16:01:50 +0100 | [diff] [blame] | 23 | #define REGION_COUNT 5 |
| 24 | |
Nico Huber | 413b0d9 | 2013-06-19 12:41:19 +0200 | [diff] [blame] | 25 | #define FDBAR_OFFSET 0x10 |
| 26 | #define FRBA_OFFSET 0x40 |
| 27 | |
| 28 | typedef struct { |
| 29 | uint32_t base, limit, size; |
| 30 | } region_t; |
| 31 | |
| 32 | static void write_image(const region_t regions[], const char *const image) |
| 33 | { |
| 34 | FILE *const f = fopen(image, "w"); |
| 35 | if (!f) { |
| 36 | perror("Could not open file"); |
| 37 | exit(EXIT_FAILURE); |
| 38 | } |
| 39 | |
| 40 | if (fseek(f, 0x1000 - 1, SEEK_SET)) { |
| 41 | perror("Failed to seek to end of descriptor"); |
| 42 | exit(EXIT_FAILURE); |
| 43 | } |
| 44 | char zero = '\0'; |
| 45 | if (fwrite(&zero, 1, 1, f) != 1) { |
| 46 | fprintf(stderr, "Failed to write at end of descriptor.\n"); |
| 47 | exit(EXIT_FAILURE); |
| 48 | } |
| 49 | |
| 50 | if (fseek(f, FDBAR_OFFSET, SEEK_SET)) { |
| 51 | perror("Failed to seek to fdbar"); |
| 52 | exit(EXIT_FAILURE); |
| 53 | } |
| 54 | |
| 55 | struct { |
| 56 | uint32_t flvalsig; |
| 57 | uint32_t flmap0; |
| 58 | } fdbar; |
| 59 | memset(&fdbar, 0x00, sizeof(fdbar)); |
| 60 | fdbar.flvalsig = 0x0ff0a55a; |
Nico Huber | e70bfee | 2016-11-16 16:01:50 +0100 | [diff] [blame] | 61 | fdbar.flmap0 = (REGION_COUNT - 1) << 24 | (FRBA_OFFSET >> 4) << 16; |
Nico Huber | 413b0d9 | 2013-06-19 12:41:19 +0200 | [diff] [blame] | 62 | if (fwrite(&fdbar, sizeof(fdbar), 1, f) != 1) { |
| 63 | fprintf(stderr, "Failed to write fdbar.\n"); |
| 64 | exit(EXIT_FAILURE); |
| 65 | } |
| 66 | |
| 67 | int i; |
Nico Huber | e70bfee | 2016-11-16 16:01:50 +0100 | [diff] [blame] | 68 | uint32_t frba[REGION_COUNT]; |
| 69 | for (i = 0; i < REGION_COUNT; ++i) { |
Nico Huber | 413b0d9 | 2013-06-19 12:41:19 +0200 | [diff] [blame] | 70 | if (regions[i].size) |
| 71 | frba[i] = ((regions[i].limit & 0xfff000) << (16 - 12)) | |
| 72 | ((regions[i].base & 0xfff000) >> 12); |
| 73 | else |
| 74 | frba[i] = 0x00000fff; |
| 75 | } |
| 76 | |
| 77 | if (fseek(f, FRBA_OFFSET, SEEK_SET)) { |
| 78 | perror("Failed to seek to frba"); |
| 79 | exit(EXIT_FAILURE); |
| 80 | } |
| 81 | if (fwrite(frba, sizeof(frba), 1, f) != 1) { |
| 82 | fprintf(stderr, "Failed to write frba.\n"); |
| 83 | exit(EXIT_FAILURE); |
| 84 | } |
| 85 | |
| 86 | fclose(f); |
| 87 | } |
| 88 | |
| 89 | static int parse_region(const char *_arg, region_t *const region) |
| 90 | { |
| 91 | char *const start = strdup(_arg); |
Patrick Georgi | a9992d3 | 2015-09-11 13:48:24 +0200 | [diff] [blame] | 92 | int size_spec = 0; |
| 93 | unsigned long first, second; |
Nico Huber | 413b0d9 | 2013-06-19 12:41:19 +0200 | [diff] [blame] | 94 | if (!start) { |
| 95 | fprintf(stderr, "Out of memory.\n"); |
| 96 | exit(EXIT_FAILURE); |
| 97 | } |
| 98 | |
Patrick Georgi | a9992d3 | 2015-09-11 13:48:24 +0200 | [diff] [blame] | 99 | char *colon = strchr(start, ':'); |
Nico Huber | 413b0d9 | 2013-06-19 12:41:19 +0200 | [diff] [blame] | 100 | if (!colon) { |
Patrick Georgi | a9992d3 | 2015-09-11 13:48:24 +0200 | [diff] [blame] | 101 | colon = strchr(start, '+'); |
| 102 | if (!colon) { |
| 103 | free(start); |
| 104 | return -1; |
| 105 | } |
| 106 | size_spec = 1; |
Nico Huber | 413b0d9 | 2013-06-19 12:41:19 +0200 | [diff] [blame] | 107 | } |
| 108 | *colon = '\0'; |
| 109 | |
| 110 | char *const end = colon + 1; |
| 111 | |
| 112 | errno = 0; |
Patrick Georgi | a9992d3 | 2015-09-11 13:48:24 +0200 | [diff] [blame] | 113 | first = strtoul(start, NULL, 0); |
| 114 | second = strtoul(end, NULL, 0); |
| 115 | |
| 116 | if (size_spec) { |
| 117 | region->base = first; |
| 118 | region->size = second; |
| 119 | region->limit = region->base + region->size - 1; |
| 120 | } else { |
| 121 | region->base = first; |
| 122 | region->limit = second; |
| 123 | region->size = region->limit - region->base + 1; |
| 124 | } |
Nico Huber | 413b0d9 | 2013-06-19 12:41:19 +0200 | [diff] [blame] | 125 | |
| 126 | free(start); |
| 127 | if (errno) { |
| 128 | perror("Failed to parse region"); |
| 129 | return -1; |
| 130 | } else { |
| 131 | return 0; |
| 132 | } |
| 133 | } |
| 134 | |
| 135 | static void print_usage(const char *name) |
| 136 | { |
| 137 | printf("usage: %s [(-b|-m|-g|-p) <start>:<end>]... <output file>\n", name); |
| 138 | printf("\n" |
| 139 | " -b | --bios <start>:<end> BIOS region\n" |
| 140 | " -m | --me <start>:<end> Intel ME region\n" |
| 141 | " -g | --gbe <start>:<end> Gigabit Ethernet region\n" |
| 142 | " -p | --platform <start>:<end> Platform Data region\n" |
| 143 | " -h | --help print this help\n\n" |
| 144 | "<start> and <end> bounds are given in bytes, the <end> bound is inclusive.\n" |
| 145 | "All regions must be multiples of 4K in size and 4K aligned.\n" |
| 146 | "The descriptor region always resides in the first 4K.\n\n" |
| 147 | "An IFD created with ifdfake won't work as a replacement for a real IFD.\n" |
| 148 | "Never try to flash such an IFD to your board!\n\n"); |
| 149 | } |
| 150 | |
| 151 | int main(int argc, char *argv[]) |
| 152 | { |
| 153 | int opt, option_index = 0, idx; |
Nico Huber | e70bfee | 2016-11-16 16:01:50 +0100 | [diff] [blame] | 154 | region_t regions[REGION_COUNT]; |
Nico Huber | 413b0d9 | 2013-06-19 12:41:19 +0200 | [diff] [blame] | 155 | |
| 156 | memset(regions, 0x00, sizeof(regions)); |
| 157 | |
| 158 | static struct option long_options[] = { |
| 159 | {"bios", 1, NULL, 'b'}, |
| 160 | {"me", 1, NULL, 'm'}, |
| 161 | {"gbe", 1, NULL, 'g'}, |
| 162 | {"platform", 1, NULL, 'p'}, |
| 163 | {"help", 0, NULL, 'h'}, |
| 164 | {0, 0, 0, 0} |
| 165 | }; |
| 166 | |
| 167 | while ((opt = getopt_long(argc, argv, "b:m:g:p:h?", |
| 168 | long_options, &option_index)) != EOF) { |
| 169 | switch (opt) { |
| 170 | case 'b': case 'm': case 'g': case 'p': |
| 171 | switch (opt) { |
| 172 | case 'b': idx = 1; break; |
| 173 | case 'm': idx = 2; break; |
| 174 | case 'g': idx = 3; break; |
| 175 | case 'p': idx = 4; break; |
| 176 | default: idx = 0; break; /* can't happen */ |
| 177 | } |
| 178 | if (parse_region(optarg, ®ions[idx])) { |
| 179 | print_usage(argv[0]); |
| 180 | exit(EXIT_FAILURE); |
| 181 | } |
| 182 | break; |
| 183 | case 'h': |
| 184 | case '?': |
| 185 | default: |
| 186 | print_usage(argv[0]); |
| 187 | exit(EXIT_SUCCESS); |
| 188 | break; |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | if (optind + 1 != argc) { |
| 193 | fprintf(stderr, "No output file given.\n\n"); |
| 194 | print_usage(argv[0]); |
| 195 | exit(EXIT_FAILURE); |
| 196 | } |
| 197 | |
| 198 | regions[0].base = 0x00000000; |
| 199 | regions[0].limit = 0x00000fff; |
| 200 | regions[0].size = 0x00001000; |
Nico Huber | e70bfee | 2016-11-16 16:01:50 +0100 | [diff] [blame] | 201 | for (idx = 1; idx < REGION_COUNT; ++idx) { |
Nico Huber | 413b0d9 | 2013-06-19 12:41:19 +0200 | [diff] [blame] | 202 | if (regions[idx].size) { |
| 203 | if (regions[idx].base & 0xfff) |
| 204 | fprintf(stderr, "Region %d is " |
| 205 | "not 4K aligned.\n", idx); |
| 206 | else if (regions[idx].size & 0xfff) |
| 207 | fprintf(stderr, "Region %d size is " |
| 208 | "no multiple of 4K.\n", idx); |
| 209 | else if (regions[idx].limit <= regions[idx].base) |
| 210 | fprintf(stderr, "Region %d is empty.\n", idx); |
| 211 | else |
| 212 | continue; |
| 213 | print_usage(argv[0]); |
| 214 | exit(EXIT_FAILURE); |
| 215 | } |
| 216 | } |
| 217 | |
| 218 | write_image(regions, argv[optind]); |
| 219 | |
| 220 | return 0; |
| 221 | } |