blob: c2b4a0fa45076776f980ff4c2422af8b22e8e299 [file] [log] [blame]
Randall Spanglerae87b922011-05-12 13:27:28 -07001/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
Randall Spangler7d6898d2010-06-11 09:22:13 -07002 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 *
5 * Verified boot kernel utility
6 */
7
Bill Richardsona08b5c92010-06-30 21:59:43 -07008#include <errno.h>
Randall Spangler7d6898d2010-06-11 09:22:13 -07009#include <getopt.h>
10#include <inttypes.h> /* For PRIu64 */
Bill Richardson249677d2010-06-23 11:16:37 -070011#include <stdarg.h>
Randall Spangler7d6898d2010-06-11 09:22:13 -070012#include <stddef.h>
13#include <stdio.h>
14#include <stdlib.h>
Bill Richardsona08b5c92010-06-30 21:59:43 -070015#include <string.h>
16#include <sys/stat.h>
17#include <sys/types.h>
Randall Spangler7d6898d2010-06-11 09:22:13 -070018#include <unistd.h>
19
20#include "cryptolib.h"
21#include "host_common.h"
22#include "kernel_blob.h"
23#include "vboot_common.h"
24
25
Bill Richardson249677d2010-06-23 11:16:37 -070026/* Global opt */
27static int opt_debug = 0;
28
Bill Richardsona08b5c92010-06-30 21:59:43 -070029static const int DEFAULT_PADDING = 65536;
Bill Richardson249677d2010-06-23 11:16:37 -070030
Randall Spangler7d6898d2010-06-11 09:22:13 -070031/* Command line options */
32enum {
33 OPT_MODE_PACK = 1000,
Bill Richardsona08b5c92010-06-30 21:59:43 -070034 OPT_MODE_REPACK,
Randall Spangler7d6898d2010-06-11 09:22:13 -070035 OPT_MODE_VERIFY,
Che-Liang Chiou03762032011-02-22 11:16:51 +080036 OPT_ARCH,
Bill Richardsona08b5c92010-06-30 21:59:43 -070037 OPT_OLDBLOB,
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +080038 OPT_KLOADADDR,
Randall Spangler7d6898d2010-06-11 09:22:13 -070039 OPT_KEYBLOCK,
40 OPT_SIGNPUBKEY,
41 OPT_SIGNPRIVATE,
42 OPT_VERSION,
43 OPT_VMLINUZ,
44 OPT_BOOTLOADER,
45 OPT_CONFIG,
Bill Richardsona08b5c92010-06-30 21:59:43 -070046 OPT_VBLOCKONLY,
Randall Spangler7d6898d2010-06-11 09:22:13 -070047 OPT_PAD,
vbendebb2b0fcc2010-07-15 15:09:47 -070048 OPT_VERBOSE,
Randall Spanglerae87b922011-05-12 13:27:28 -070049 OPT_MINVERSION,
Randall Spangler7d6898d2010-06-11 09:22:13 -070050};
51
Che-Liang Chiou03762032011-02-22 11:16:51 +080052enum {
53 ARCH_ARM,
54 ARCH_X86 /* default */
55};
56
Randall Spangler7d6898d2010-06-11 09:22:13 -070057static struct option long_opts[] = {
58 {"pack", 1, 0, OPT_MODE_PACK },
Bill Richardsona08b5c92010-06-30 21:59:43 -070059 {"repack", 1, 0, OPT_MODE_REPACK },
Randall Spangler7d6898d2010-06-11 09:22:13 -070060 {"verify", 1, 0, OPT_MODE_VERIFY },
Che-Liang Chiou03762032011-02-22 11:16:51 +080061 {"arch", 1, 0, OPT_ARCH },
Bill Richardsona08b5c92010-06-30 21:59:43 -070062 {"oldblob", 1, 0, OPT_OLDBLOB },
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +080063 {"kloadaddr", 1, 0, OPT_KLOADADDR },
Randall Spangler7d6898d2010-06-11 09:22:13 -070064 {"keyblock", 1, 0, OPT_KEYBLOCK },
65 {"signpubkey", 1, 0, OPT_SIGNPUBKEY },
66 {"signprivate", 1, 0, OPT_SIGNPRIVATE },
67 {"version", 1, 0, OPT_VERSION },
Randall Spanglerae87b922011-05-12 13:27:28 -070068 {"minversion", 1, 0, OPT_MINVERSION },
Randall Spangler7d6898d2010-06-11 09:22:13 -070069 {"vmlinuz", 1, 0, OPT_VMLINUZ },
70 {"bootloader", 1, 0, OPT_BOOTLOADER },
71 {"config", 1, 0, OPT_CONFIG },
Bill Richardsona08b5c92010-06-30 21:59:43 -070072 {"vblockonly", 0, 0, OPT_VBLOCKONLY },
Randall Spangler7d6898d2010-06-11 09:22:13 -070073 {"pad", 1, 0, OPT_PAD },
vbendebb2b0fcc2010-07-15 15:09:47 -070074 {"verbose", 0, 0, OPT_VERBOSE },
Bill Richardson249677d2010-06-23 11:16:37 -070075 {"debug", 0, &opt_debug, 1 },
Randall Spangler7d6898d2010-06-11 09:22:13 -070076 {NULL, 0, 0, 0}
77};
78
79
80/* Print help and return error */
Bill Richardsona08b5c92010-06-30 21:59:43 -070081static int PrintHelp(char *progname) {
82 fprintf(stderr,
83 "This program creates, signs, and verifies the kernel blob\n");
84 fprintf(stderr,
85 "\n"
86 "Usage: %s --pack <file> [PARAMETERS]\n"
87 "\n"
88 " Required parameters:\n"
89 " --keyblock <file> Key block in .keyblock format\n"
Bill Richardson4f36ef32010-08-09 17:50:14 -070090 " --signprivate <file>"
91 " Private key to sign kernel data, in .vbprivk format\n"
Bill Richardsona08b5c92010-06-30 21:59:43 -070092 " --version <number> Kernel version\n"
93 " --vmlinuz <file> Linux kernel bzImage file\n"
94 " --bootloader <file> Bootloader stub\n"
vbendebb2b0fcc2010-07-15 15:09:47 -070095 " --config <file> Command line file\n"
Che-Liang Chiou03762032011-02-22 11:16:51 +080096 " --arch <arch> Cpu architecture (default x86)\n"
Bill Richardsona08b5c92010-06-30 21:59:43 -070097 "\n"
98 " Optional:\n"
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +080099 " --kloadaddr <address> Assign kernel body load address\n"
Bill Richardsona08b5c92010-06-30 21:59:43 -0700100 " --pad <number> Verification padding size in bytes\n"
101 " --vblockonly Emit just the verification blob\n",
102 progname);
103 fprintf(stderr,
104 "\nOR\n\n"
105 "Usage: %s --repack <file> [PARAMETERS]\n"
106 "\n"
vbendeb858fffb2010-10-06 09:51:44 -0700107 " Required parameters (of --keyblock, --config, and --version \n"
108 " at least one is required):\n"
Bill Richardsona08b5c92010-06-30 21:59:43 -0700109 " --keyblock <file> Key block in .keyblock format\n"
Bill Richardson4f36ef32010-08-09 17:50:14 -0700110 " --signprivate <file>"
111 " Private key to sign kernel data, in .vbprivk format\n"
Bill Richardsona08b5c92010-06-30 21:59:43 -0700112 " --oldblob <file> Previously packed kernel blob\n"
vbendebb2b0fcc2010-07-15 15:09:47 -0700113 " --config <file> New command line file\n"
vbendeb858fffb2010-10-06 09:51:44 -0700114 " --version <number> Kernel version\n"
Bill Richardsona08b5c92010-06-30 21:59:43 -0700115 "\n"
116 " Optional:\n"
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +0800117 " --kloadaddr <address> Assign kernel body load address\n"
Bill Richardsona08b5c92010-06-30 21:59:43 -0700118 " --pad <number> Verification padding size in bytes\n"
119 " --vblockonly Emit just the verification blob\n",
120 progname);
121 fprintf(stderr,
122 "\nOR\n\n"
123 "Usage: %s --verify <file> [PARAMETERS]\n"
124 "\n"
vbendebb2b0fcc2010-07-15 15:09:47 -0700125 " Optional:\n"
Bill Richardson4f36ef32010-08-09 17:50:14 -0700126 " --signpubkey <file>"
127 " Public key to verify kernel keyblock, in .vbpubk format\n"
vbendebb2b0fcc2010-07-15 15:09:47 -0700128 " --verbose Print a more detailed report\n"
Will Drewry9342f882010-10-26 10:22:05 -0500129 " --keyblock <file>"
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +0800130 " Outputs the verified key block, in .keyblock format\n"
131 " --kloadaddr <address> Assign kernel body load address\n"
Randall Spangler7ecb39d2011-05-12 15:37:45 -0700132 " --pad <number> Verification padding size in bytes\n"
Randall Spanglerae87b922011-05-12 13:27:28 -0700133 " --minversion <number> Minimum combined kernel key version\n"
134 " and kernel version\n"
Bill Richardsona08b5c92010-06-30 21:59:43 -0700135 "\n",
136 progname);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700137 return 1;
138}
139
Bill Richardson249677d2010-06-23 11:16:37 -0700140static void Debug(const char *format, ...) {
141 if (!opt_debug)
142 return;
143
144 va_list ap;
145 va_start(ap, format);
146 fprintf(stderr, "DEBUG: ");
147 vfprintf(stderr, format, ap);
148 va_end(ap);
149}
150
Bill Richardson2f6a71f2010-10-14 09:25:39 -0700151/* Return an explanation when fread() fails. */
152static const char *error_fread(FILE *fp) {
153 const char *retval = "beats me why";
154 if (feof(fp))
155 retval = "EOF";
156 else if (ferror(fp))
157 retval = strerror(errno);
158 clearerr(fp);
159 return retval;
160}
Randall Spangler7d6898d2010-06-11 09:22:13 -0700161
162/* Return the smallest integral multiple of [alignment] that is equal
163 * to or greater than [val]. Used to determine the number of
164 * pages/sectors/blocks/whatever needed to contain [val]
165 * items/bytes/etc. */
166static uint64_t roundup(uint64_t val, uint64_t alignment) {
167 uint64_t rem = val % alignment;
168 if ( rem )
169 return val + (alignment - rem);
170 return val;
171}
172
173
174/* Match regexp /\b--\b/ to delimit the start of the kernel commandline. If we
175 * don't find one, we'll use the whole thing. */
176static unsigned int find_cmdline_start(char *input, unsigned int max_len) {
177 int start = 0;
178 int i;
179 for(i = 0; i < max_len - 1 && input[i]; i++) {
180 if ('-' == input[i] && '-' == input[i + 1]) { /* found a "--" */
181 if ((i == 0 || ' ' == input[i - 1]) && /* nothing before it */
182 (i + 2 >= max_len || ' ' == input[i+2])) { /* nothing after it */
183 start = i+2; /* note: hope there's a trailing '\0' */
184 break;
185 }
186 }
187 }
188 while(' ' == input[start]) /* skip leading spaces */
189 start++;
190
191 return start;
192}
193
194
Bill Richardsona08b5c92010-06-30 21:59:43 -0700195typedef struct blob_s {
196 /* Stuff needed by VbKernelPreambleHeader */
197 uint64_t kernel_version;
198 uint64_t bootloader_address;
199 uint64_t bootloader_size;
200 /* Raw kernel blob data */
201 uint64_t blob_size;
202 uint8_t *blob;
vbendebb2b0fcc2010-07-15 15:09:47 -0700203
204 /* these fields are not always initialized */
205 VbKernelPreambleHeader* preamble;
206 VbKeyBlockHeader* key_block;
207 uint8_t *buf;
208
Bill Richardsona08b5c92010-06-30 21:59:43 -0700209} blob_t;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700210
vbendebb2b0fcc2010-07-15 15:09:47 -0700211/* Given a blob return the location of the kernel command line buffer. */
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +0800212static char* BpCmdLineLocation(blob_t *bp, uint64_t kernel_body_load_address)
vbendebb2b0fcc2010-07-15 15:09:47 -0700213{
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +0800214 return (char*)(bp->blob + bp->bootloader_address - kernel_body_load_address -
vbendebb2b0fcc2010-07-15 15:09:47 -0700215 CROS_CONFIG_SIZE - CROS_PARAMS_SIZE);
216}
Bill Richardsona08b5c92010-06-30 21:59:43 -0700217
218static void FreeBlob(blob_t *bp) {
219 if (bp) {
220 if (bp->blob)
221 Free(bp->blob);
vbendebb2b0fcc2010-07-15 15:09:47 -0700222 if (bp->buf)
223 Free(bp->buf);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700224 Free(bp);
225 }
226}
227
vbendebb2b0fcc2010-07-15 15:09:47 -0700228/*
229 * Read the kernel command line from a file. Get rid of \n characters along
230 * the way and verify that the line fits into a 4K buffer.
231 *
232 * Return the buffer contaning the line on success (and set the line length
233 * using the passed in parameter), or NULL in case something goes wrong.
234 */
235static uint8_t* ReadConfigFile(const char* config_file, uint64_t* config_size)
236{
237 uint8_t* config_buf;
238 int ii;
239
240 config_buf = ReadFile(config_file, config_size);
241 Debug(" config file size=0x%" PRIx64 "\n", *config_size);
242 if (CROS_CONFIG_SIZE <= *config_size) { /* need room for trailing '\0' */
243 error("Config file %s is too large (>= %d bytes)\n",
244 config_file, CROS_CONFIG_SIZE);
245 return NULL;
246 }
247
248 /* Replace newlines with spaces */
249 for (ii = 0; ii < *config_size; ii++) {
250 if ('\n' == config_buf[ii]) {
251 config_buf[ii] = ' ';
252 }
253 }
254 return config_buf;
255}
256
Bill Richardsona08b5c92010-06-30 21:59:43 -0700257/* Create a blob from its components */
258static blob_t *NewBlob(uint64_t version,
259 const char* vmlinuz,
260 const char* bootloader_file,
Che-Liang Chiou03762032011-02-22 11:16:51 +0800261 const char* config_file,
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +0800262 int arch,
263 uint64_t kernel_body_load_address) {
Che-Liang Chiou03762032011-02-22 11:16:51 +0800264 blob_t* bp;
265 struct linux_kernel_header* lh = 0;
266 struct linux_kernel_params* params = 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700267 uint8_t* config_buf;
268 uint64_t config_size;
269 uint8_t* bootloader_buf;
270 uint64_t bootloader_size;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700271 uint8_t* kernel_buf;
272 uint64_t kernel_size;
273 uint64_t kernel32_start = 0;
274 uint64_t kernel32_size = 0;
275 uint32_t cmdline_addr;
276 uint8_t* blob = NULL;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700277 uint64_t now = 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700278
Randall Spangler7d6898d2010-06-11 09:22:13 -0700279 if (!vmlinuz || !bootloader_file || !config_file) {
280 error("Must specify all input files\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700281 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700282 }
283
Bill Richardsona08b5c92010-06-30 21:59:43 -0700284 bp = (blob_t *)Malloc(sizeof(blob_t));
285 if (!bp) {
286 error("Couldn't allocate bytes for blob_t.\n");
287 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700288 }
vbendebb2b0fcc2010-07-15 15:09:47 -0700289
290 Memset(bp, 0, sizeof(*bp));
Bill Richardsona08b5c92010-06-30 21:59:43 -0700291 bp->kernel_version = version;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700292
293 /* Read the config file */
Bill Richardson249677d2010-06-23 11:16:37 -0700294 Debug("Reading %s\n", config_file);
vbendebb2b0fcc2010-07-15 15:09:47 -0700295 config_buf = ReadConfigFile(config_file, &config_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700296 if (!config_buf)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700297 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700298
299 /* Read the bootloader */
Bill Richardson249677d2010-06-23 11:16:37 -0700300 Debug("Reading %s\n", bootloader_file);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700301 bootloader_buf = ReadFile(bootloader_file, &bootloader_size);
302 if (!bootloader_buf)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700303 return 0;
Bill Richardson249677d2010-06-23 11:16:37 -0700304 Debug(" bootloader file size=0x%" PRIx64 "\n", bootloader_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700305
306 /* Read the kernel */
Bill Richardson249677d2010-06-23 11:16:37 -0700307 Debug("Reading %s\n", vmlinuz);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700308 kernel_buf = ReadFile(vmlinuz, &kernel_size);
309 if (!kernel_buf)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700310 return 0;
Bill Richardson249677d2010-06-23 11:16:37 -0700311 Debug(" kernel file size=0x%" PRIx64 "\n", kernel_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700312 if (!kernel_size) {
313 error("Empty kernel file\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700314 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700315 }
316
Che-Liang Chiou03762032011-02-22 11:16:51 +0800317 if (arch == ARCH_X86) {
318 /* The first part of vmlinuz is a header, followed by a real-mode
319 * boot stub. We only want the 32-bit part. */
320 lh = (struct linux_kernel_header *)kernel_buf;
321 kernel32_start = (lh->setup_sects + 1) << 9;
322 if (kernel32_start >= kernel_size) {
323 error("Malformed kernel\n");
324 return 0;
325 }
326 } else
327 kernel32_start = 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700328 kernel32_size = kernel_size - kernel32_start;
Bill Richardson249677d2010-06-23 11:16:37 -0700329 Debug(" kernel32_start=0x%" PRIx64 "\n", kernel32_start);
330 Debug(" kernel32_size=0x%" PRIx64 "\n", kernel32_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700331
332 /* Allocate and zero the blob we need. */
Bill Richardsona08b5c92010-06-30 21:59:43 -0700333 bp->blob_size = roundup(kernel32_size, CROS_ALIGN) +
Randall Spangler7d6898d2010-06-11 09:22:13 -0700334 CROS_CONFIG_SIZE +
335 CROS_PARAMS_SIZE +
336 roundup(bootloader_size, CROS_ALIGN);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700337 blob = (uint8_t *)Malloc(bp->blob_size);
338 Debug("blob_size=0x%" PRIx64 "\n", bp->blob_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700339 if (!blob) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700340 error("Couldn't allocate %ld bytes.\n", bp->blob_size);
341 return 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700342 }
Bill Richardsona08b5c92010-06-30 21:59:43 -0700343 Memset(blob, 0, bp->blob_size);
344 bp->blob = blob;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700345
346 /* Copy the 32-bit kernel. */
Bill Richardson249677d2010-06-23 11:16:37 -0700347 Debug("kernel goes at blob+=0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700348 if (kernel32_size)
349 Memcpy(blob + now, kernel_buf + kernel32_start, kernel32_size);
350 now += roundup(now + kernel32_size, CROS_ALIGN);
351
Bill Richardson249677d2010-06-23 11:16:37 -0700352 Debug("config goes at blob+0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700353 /* Find the load address of the commandline. We'll need it later. */
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +0800354 cmdline_addr = kernel_body_load_address + now +
Randall Spangler7d6898d2010-06-11 09:22:13 -0700355 find_cmdline_start((char *)config_buf, config_size);
Bill Richardson249677d2010-06-23 11:16:37 -0700356 Debug(" cmdline_addr=0x%" PRIx64 "\n", cmdline_addr);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700357
358 /* Copy the config. */
359 if (config_size)
360 Memcpy(blob + now, config_buf, config_size);
361 now += CROS_CONFIG_SIZE;
362
363 /* The zeropage data is next. Overlay the linux_kernel_header onto it, and
364 * tweak a few fields. */
Bill Richardson249677d2010-06-23 11:16:37 -0700365 Debug("params goes at blob+=0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700366 params = (struct linux_kernel_params *)(blob + now);
Che-Liang Chiou03762032011-02-22 11:16:51 +0800367 if (arch == ARCH_X86)
368 Memcpy(&(params->setup_sects), &(lh->setup_sects),
369 sizeof(*lh) - offsetof(struct linux_kernel_header, setup_sects));
370 else
371 Memset(&(params->setup_sects), 0,
372 sizeof(*lh) - offsetof(struct linux_kernel_header, setup_sects));
Randall Spangler7d6898d2010-06-11 09:22:13 -0700373 params->boot_flag = 0;
374 params->ramdisk_image = 0; /* we don't support initrd */
375 params->ramdisk_size = 0;
376 params->type_of_loader = 0xff;
377 params->cmd_line_ptr = cmdline_addr;
Che-Liang Chiou475bf442010-08-23 11:20:44 +0800378 /* A fake e820 memory map with 2 entries */
379 params->n_e820_entry = 2;
380 params->e820_entries[0].start_addr = 0x00000000;
381 params->e820_entries[0].segment_size = 0x00001000;
382 params->e820_entries[0].segment_type = E820_TYPE_RAM;
383 params->e820_entries[1].start_addr = 0xfffff000;
384 params->e820_entries[1].segment_size = 0x00001000;
385 params->e820_entries[1].segment_type = E820_TYPE_RESERVED;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700386 now += CROS_PARAMS_SIZE;
387
388 /* Finally, append the bootloader. Remember where it will load in
389 * memory, too. */
Bill Richardson249677d2010-06-23 11:16:37 -0700390 Debug("bootloader goes at blob+=0x%" PRIx64 "\n", now);
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +0800391 bp->bootloader_address = kernel_body_load_address + now;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700392 bp->bootloader_size = roundup(bootloader_size, CROS_ALIGN);
393 Debug(" bootloader_address=0x%" PRIx64 "\n", bp->bootloader_address);
394 Debug(" bootloader_size=0x%" PRIx64 "\n", bp->bootloader_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700395 if (bootloader_size)
396 Memcpy(blob + now, bootloader_buf, bootloader_size);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700397 now += bp->bootloader_size;
Bill Richardson249677d2010-06-23 11:16:37 -0700398 Debug("end of blob is 0x%" PRIx64 "\n", now);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700399
400 /* Free input buffers */
401 Free(kernel_buf);
402 Free(config_buf);
403 Free(bootloader_buf);
404
Bill Richardsona08b5c92010-06-30 21:59:43 -0700405 /* Success */
406 return bp;
407}
408
409
410/* Pull the blob_t stuff out of a prepacked kernel blob file */
Randall Spangler7ecb39d2011-05-12 15:37:45 -0700411static blob_t *OldBlob(const char* filename, uint64_t pad) {
vbendebb2b0fcc2010-07-15 15:09:47 -0700412 FILE* fp = NULL;
413 blob_t *bp = NULL;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700414 struct stat statbuf;
415 VbKeyBlockHeader* key_block;
416 VbKernelPreambleHeader* preamble;
417 uint64_t now = 0;
vbendebb2b0fcc2010-07-15 15:09:47 -0700418 uint8_t* buf = NULL;
419 int ret_error = 1;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700420
421 if (!filename) {
422 error("Must specify prepacked blob to read\n");
423 return 0;
424 }
425
426 if (0 != stat(filename, &statbuf)) {
Bill Richardson2f6a71f2010-10-14 09:25:39 -0700427 error("Unable to stat %s: %s\n", filename, strerror(errno));
Bill Richardsona08b5c92010-06-30 21:59:43 -0700428 return 0;
429 }
430
431 Debug("%s size is 0x%" PRIx64 "\n", filename, statbuf.st_size);
Randall Spangler7ecb39d2011-05-12 15:37:45 -0700432 if (statbuf.st_size < pad) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700433 error("%s is too small to be a valid kernel blob\n");
434 return 0;
435 }
436
437 Debug("Reading %s\n", filename);
438 fp = fopen(filename, "rb");
439 if (!fp) {
440 error("Unable to open file %s: %s\n", filename, strerror(errno));
441 return 0;
442 }
443
Randall Spangler7ecb39d2011-05-12 15:37:45 -0700444 buf = Malloc(pad);
vbendebb2b0fcc2010-07-15 15:09:47 -0700445 if (!buf) {
446 error("Unable to allocate padding\n");
447 goto unwind_oldblob;
448 }
449
Randall Spangler7ecb39d2011-05-12 15:37:45 -0700450 if (1 != fread(buf, pad, 1, fp)) {
Bill Richardson2f6a71f2010-10-14 09:25:39 -0700451 error("Unable to read header from %s: %s\n", filename, error_fread(fp));
vbendebb2b0fcc2010-07-15 15:09:47 -0700452 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700453 }
454
455 /* Skip the key block */
456 key_block = (VbKeyBlockHeader*)buf;
457 Debug("Keyblock is 0x%" PRIx64 " bytes\n", key_block->key_block_size);
458 now += key_block->key_block_size;
459 if (now > statbuf.st_size) {
460 error("key_block_size advances past the end of the blob\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700461 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700462 }
Randall Spangler7ecb39d2011-05-12 15:37:45 -0700463 if (now > pad) {
464 error("key_block_size advances past %" PRIu64 " byte padding\n", pad);
465 goto unwind_oldblob;
466 }
Bill Richardsona08b5c92010-06-30 21:59:43 -0700467
468 /* Skip the preamble */
469 preamble = (VbKernelPreambleHeader*)(buf + now);
470 Debug("Preamble is 0x%" PRIx64 " bytes\n", preamble->preamble_size);
471 now += preamble->preamble_size;
472 if (now > statbuf.st_size) {
473 error("preamble_size advances past the end of the blob\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700474 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700475 }
Randall Spangler7ecb39d2011-05-12 15:37:45 -0700476 if (now > pad) {
477 error("preamble_size advances past %" PRIu64 " byte padding\n", pad);
478 goto unwind_oldblob;
479 }
Bill Richardsona08b5c92010-06-30 21:59:43 -0700480
481 /* Go find the kernel blob */
482 Debug("kernel blob is at offset 0x%" PRIx64 "\n", now);
483 if (0 != fseek(fp, now, SEEK_SET)) {
484 error("Unable to seek to 0x%" PRIx64 " in %s: %s\n", now, filename,
485 strerror(errno));
vbendebb2b0fcc2010-07-15 15:09:47 -0700486 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700487 }
488
489 /* Remember what we've got */
490 bp = (blob_t *)Malloc(sizeof(blob_t));
491 if (!bp) {
492 error("Couldn't allocate bytes for blob_t.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700493 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700494 }
495
vbendebb2b0fcc2010-07-15 15:09:47 -0700496 bp->buf = buf;
497 bp->key_block = key_block;
498 bp->preamble = preamble;
499
Bill Richardsona08b5c92010-06-30 21:59:43 -0700500 bp->kernel_version = preamble->kernel_version;
501 bp->bootloader_address = preamble->bootloader_address;
502 bp->bootloader_size = preamble->bootloader_size;
503 bp->blob_size = preamble->body_signature.data_size;
504
505 Debug(" kernel_version = %d\n", bp->kernel_version);
506 Debug(" bootloader_address = 0x%" PRIx64 "\n", bp->bootloader_address);
507 Debug(" bootloader_size = 0x%" PRIx64 "\n", bp->bootloader_size);
508 Debug(" blob_size = 0x%" PRIx64 "\n", bp->blob_size);
509
Bill Richardson2f6a71f2010-10-14 09:25:39 -0700510 if (!bp->blob_size) {
511 error("No kernel blob found\n");
512 goto unwind_oldblob;
513 }
514
Bill Richardsona08b5c92010-06-30 21:59:43 -0700515 bp->blob = (uint8_t *)Malloc(bp->blob_size);
516 if (!bp->blob) {
517 error("Couldn't allocate 0x%" PRIx64 " bytes for blob_t.\n", bp->blob_size);
vbendebb2b0fcc2010-07-15 15:09:47 -0700518 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700519 }
520
521 /* read it in */
522 if (1 != fread(bp->blob, bp->blob_size, 1, fp)) {
Bill Richardson2f6a71f2010-10-14 09:25:39 -0700523 error("Unable to read kernel blob from %s: %s\n", filename, error_fread(fp));
vbendebb2b0fcc2010-07-15 15:09:47 -0700524 goto unwind_oldblob;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700525 }
526
vbendebb2b0fcc2010-07-15 15:09:47 -0700527 ret_error = 0;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700528
vbendebb2b0fcc2010-07-15 15:09:47 -0700529 /* done */
530unwind_oldblob:
531 fclose(fp);
532 if (ret_error) {
533 if (bp) {
534 FreeBlob(bp);
535 bp = NULL;
536 } else if (buf) {
537 Free(buf);
538 }
539 }
Bill Richardsona08b5c92010-06-30 21:59:43 -0700540 return bp;
541}
542
543
544/* Pack a .kernel */
545static int Pack(const char* outfile, const char* keyblock_file,
546 const char* signprivate, blob_t *bp, uint64_t pad,
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +0800547 int vblockonly,
548 uint64_t kernel_body_load_address) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700549 VbPrivateKey* signing_key;
550 VbSignature* body_sig;
551 VbKernelPreambleHeader* preamble;
552 VbKeyBlockHeader* key_block;
553 uint64_t key_block_size;
554 FILE* f;
555 uint64_t i;
556
557 if (!outfile) {
558 error("Must specify output filename\n");
559 return 1;
560 }
vbendebb2b0fcc2010-07-15 15:09:47 -0700561 if ((!keyblock_file && !bp->key_block) || !signprivate) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700562 error("Must specify all keys\n");
563 return 1;
564 }
565 if (!bp) {
566 error("Refusing to pack invalid kernel blob\n");
567 return 1;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700568 }
vbendebb2b0fcc2010-07-15 15:09:47 -0700569
570 /* Get the key block and read the private key. */
571 if (keyblock_file) {
572 key_block = (VbKeyBlockHeader*)ReadFile(keyblock_file, &key_block_size);
573 if (!key_block) {
574 error("Error reading key block.\n");
575 return 1;
576 }
577 } else {
578 key_block = bp->key_block;
579 key_block_size = key_block->key_block_size;
580 }
581
Bill Richardsona08b5c92010-06-30 21:59:43 -0700582 if (pad < key_block->key_block_size) {
583 error("Pad too small\n");
584 return 1;
585 }
586
Bill Richardsonabf05502010-07-01 10:22:06 -0700587 signing_key = PrivateKeyRead(signprivate);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700588 if (!signing_key) {
589 error("Error reading signing key.\n");
590 return 1;
591 }
592
Randall Spangler7d6898d2010-06-11 09:22:13 -0700593 /* Sign the kernel data */
Bill Richardsona08b5c92010-06-30 21:59:43 -0700594 body_sig = CalculateSignature(bp->blob, bp->blob_size, signing_key);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700595 if (!body_sig) {
596 error("Error calculating body signature\n");
597 return 1;
598 }
599
600 /* Create preamble */
Bill Richardsona08b5c92010-06-30 21:59:43 -0700601 preamble = CreateKernelPreamble(bp->kernel_version,
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +0800602 kernel_body_load_address,
Bill Richardsona08b5c92010-06-30 21:59:43 -0700603 bp->bootloader_address,
604 bp->bootloader_size,
Randall Spangler7d6898d2010-06-11 09:22:13 -0700605 body_sig,
606 pad - key_block_size,
607 signing_key);
608 if (!preamble) {
609 error("Error creating preamble.\n");
610 return 1;
611 }
612
613 /* Write the output file */
Bill Richardson249677d2010-06-23 11:16:37 -0700614 Debug("writing %s...\n", outfile);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700615 f = fopen(outfile, "wb");
616 if (!f) {
617 error("Can't open output file %s\n", outfile);
618 return 1;
619 }
Bill Richardson249677d2010-06-23 11:16:37 -0700620 Debug("0x%" PRIx64 " bytes of key_block\n", key_block_size);
621 Debug("0x%" PRIx64 " bytes of preamble\n", preamble->preamble_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700622 i = ((1 != fwrite(key_block, key_block_size, 1, f)) ||
Bill Richardsona08b5c92010-06-30 21:59:43 -0700623 (1 != fwrite(preamble, preamble->preamble_size, 1, f)));
Randall Spangler7d6898d2010-06-11 09:22:13 -0700624 if (i) {
625 error("Can't write output file %s\n", outfile);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700626 fclose(f);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700627 unlink(outfile);
628 return 1;
629 }
630
Bill Richardsona08b5c92010-06-30 21:59:43 -0700631 if (!vblockonly) {
632 Debug("0x%" PRIx64 " bytes of blob\n", bp->blob_size);
633 i = (1 != fwrite(bp->blob, bp->blob_size, 1, f));
634 if (i) {
635 error("Can't write output file %s\n", outfile);
636 fclose(f);
637 unlink(outfile);
638 return 1;
639 }
640 }
641
642 fclose(f);
643
Randall Spangler7d6898d2010-06-11 09:22:13 -0700644 /* Success */
645 return 0;
646}
647
vbendebb2b0fcc2010-07-15 15:09:47 -0700648/*
649 * Replace kernel command line in a blob representing a kernel.
650 */
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +0800651static int ReplaceConfig(blob_t* bp, const char* config_file,
652 uint64_t kernel_body_load_address)
vbendebb2b0fcc2010-07-15 15:09:47 -0700653{
654 uint8_t* new_conf;
655 uint64_t config_size;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700656
vbendebb2b0fcc2010-07-15 15:09:47 -0700657 if (!config_file) {
658 return 0;
659 }
660
661 new_conf = ReadConfigFile(config_file, &config_size);
662 if (!new_conf) {
663 return 1;
664 }
665
666 /* fill the config buffer with zeros */
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +0800667 Memset(BpCmdLineLocation(bp, kernel_body_load_address), 0, CROS_CONFIG_SIZE);
668 Memcpy(BpCmdLineLocation(bp, kernel_body_load_address),
669 new_conf, config_size);
vbendebb2b0fcc2010-07-15 15:09:47 -0700670 Free(new_conf);
671 return 0;
672}
673
Will Drewry9342f882010-10-26 10:22:05 -0500674static int Verify(const char* infile, const char* signpubkey, int verbose,
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +0800675 const char* key_block_file,
Randall Spangler7ecb39d2011-05-12 15:37:45 -0700676 uint64_t kernel_body_load_address, uint64_t min_version,
677 uint64_t pad) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700678
679 VbKeyBlockHeader* key_block;
680 VbKernelPreambleHeader* preamble;
681 VbPublicKey* data_key;
Bill Richardson4f36ef32010-08-09 17:50:14 -0700682 VbPublicKey* sign_key = NULL;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700683 RSAPublicKey* rsa;
vbendebb2b0fcc2010-07-15 15:09:47 -0700684 blob_t* bp;
685 uint64_t now;
686 int rv = 1;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700687
Bill Richardson4f36ef32010-08-09 17:50:14 -0700688 if (!infile) {
689 error("Must specify filename\n");
Randall Spangler7d6898d2010-06-11 09:22:13 -0700690 return 1;
691 }
692
693 /* Read public signing key */
Bill Richardson4f36ef32010-08-09 17:50:14 -0700694 if (signpubkey) {
695 sign_key = PublicKeyRead(signpubkey);
696 if (!sign_key) {
697 error("Error reading signpubkey.\n");
698 return 1;
699 }
Randall Spangler7d6898d2010-06-11 09:22:13 -0700700 }
701
702 /* Read blob */
Randall Spangler7ecb39d2011-05-12 15:37:45 -0700703 bp = OldBlob(infile, pad);
vbendebb2b0fcc2010-07-15 15:09:47 -0700704 if (!bp) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700705 error("Error reading input file\n");
706 return 1;
707 }
708
709 /* Verify key block */
vbendebb2b0fcc2010-07-15 15:09:47 -0700710 key_block = bp->key_block;
Randall Spangler138acfe2010-08-17 15:45:21 -0700711 if (0 != KeyBlockVerify(key_block, bp->blob_size, sign_key,
712 (sign_key ? 0 : 1))) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700713 error("Error verifying key block.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700714 goto verify_exit;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700715 }
vbendebb2b0fcc2010-07-15 15:09:47 -0700716 now = key_block->key_block_size;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700717
Will Drewry9342f882010-10-26 10:22:05 -0500718 if (key_block_file) {
719 FILE* f = NULL;
720 f = fopen(key_block_file, "wb");
721 if (!f) {
722 error("Can't open key block file %s\n", key_block_file);
723 return 1;
724 }
725 if (1 != fwrite(key_block, key_block->key_block_size, 1, f)) {
726 error("Can't write key block file %s\n", key_block_file);
727 return 1;
728 }
729 fclose(f);
730 }
731
Randall Spangler7d6898d2010-06-11 09:22:13 -0700732 printf("Key block:\n");
733 data_key = &key_block->data_key;
Bill Richardson4f36ef32010-08-09 17:50:14 -0700734 if (verbose)
735 printf(" Signature: %s\n", sign_key ? "valid" : "ignored");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700736 printf(" Size: 0x%" PRIx64 "\n", key_block->key_block_size);
Bill Richardson60bcbe32010-09-09 14:53:56 -0700737 printf(" Flags: %" PRIu64 " ", key_block->key_block_flags);
738 if (key_block->key_block_flags & KEY_BLOCK_FLAG_DEVELOPER_0)
739 printf(" !DEV");
740 if (key_block->key_block_flags & KEY_BLOCK_FLAG_DEVELOPER_1)
741 printf(" DEV");
742 if (key_block->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_0)
743 printf(" !REC");
744 if (key_block->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_1)
745 printf(" REC");
746 printf("\n");
Randall Spangler7d6898d2010-06-11 09:22:13 -0700747 printf(" Data key algorithm: %" PRIu64 " %s\n", data_key->algorithm,
748 (data_key->algorithm < kNumAlgorithms ?
749 algo_strings[data_key->algorithm] : "(invalid)"));
750 printf(" Data key version: %" PRIu64 "\n", data_key->key_version);
Bill Richardson60bcbe32010-09-09 14:53:56 -0700751 printf(" Data key sha1sum: ");
752 PrintPubKeySha1Sum(data_key);
753 printf("\n");
Randall Spangler7d6898d2010-06-11 09:22:13 -0700754
Randall Spanglerae87b922011-05-12 13:27:28 -0700755 if (data_key->key_version < (min_version >> 16)) {
756 error("Data key version %" PRIu64 " is lower than minimum %" PRIu64".\n",
757 data_key->key_version, (min_version >> 16));
758 goto verify_exit;
759 }
760
Randall Spangler7d6898d2010-06-11 09:22:13 -0700761 rsa = PublicKeyToRSA(&key_block->data_key);
762 if (!rsa) {
763 error("Error parsing data key.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700764 goto verify_exit;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700765 }
766
767 /* Verify preamble */
vbendebb2b0fcc2010-07-15 15:09:47 -0700768 preamble = bp->preamble;
Randall Spangler87c13d82010-07-19 10:35:40 -0700769 if (0 != VerifyKernelPreamble(
Bill Richardson4f36ef32010-08-09 17:50:14 -0700770 preamble, bp->blob_size - key_block->key_block_size, rsa)) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700771 error("Error verifying preamble.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700772 goto verify_exit;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700773 }
774 now += preamble->preamble_size;
775
776 printf("Preamble:\n");
Bill Richardsona08b5c92010-06-30 21:59:43 -0700777 printf(" Size: 0x%" PRIx64 "\n", preamble->preamble_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700778 printf(" Header version: %" PRIu32 ".%" PRIu32"\n",
779 preamble->header_version_major, preamble->header_version_minor);
780 printf(" Kernel version: %" PRIu64 "\n", preamble->kernel_version);
Bill Richardson249677d2010-06-23 11:16:37 -0700781 printf(" Body load address: 0x%" PRIx64 "\n", preamble->body_load_address);
782 printf(" Body size: 0x%" PRIx64 "\n",
Randall Spangler7d6898d2010-06-11 09:22:13 -0700783 preamble->body_signature.data_size);
Randall Spangler87c13d82010-07-19 10:35:40 -0700784 printf(" Bootloader address: 0x%" PRIx64 "\n",
785 preamble->bootloader_address);
Bill Richardson249677d2010-06-23 11:16:37 -0700786 printf(" Bootloader size: 0x%" PRIx64 "\n", preamble->bootloader_size);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700787
Randall Spanglerae87b922011-05-12 13:27:28 -0700788 if (preamble->kernel_version < (min_version & 0xFFFF)) {
789 error("Kernel version %" PRIu64 " is lower than minimum %" PRIu64 ".\n",
790 preamble->kernel_version, (min_version & 0xFFFF));
791 goto verify_exit;
792 }
793
Randall Spangler7d6898d2010-06-11 09:22:13 -0700794 /* Verify body */
Randall Spangler87c13d82010-07-19 10:35:40 -0700795 if (0 != VerifyData(bp->blob, bp->blob_size, &preamble->body_signature,
796 rsa)) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700797 error("Error verifying kernel body.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700798 goto verify_exit;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700799 }
800 printf("Body verification succeeded.\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700801
802 rv = 0;
803
804 if (!verbose) {
805 goto verify_exit;
806 }
807
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +0800808 printf("Config:\n%s\n", BpCmdLineLocation(bp, kernel_body_load_address));
vbendebb2b0fcc2010-07-15 15:09:47 -0700809
810verify_exit:
811 FreeBlob(bp);
812 return rv;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700813}
814
815
816int main(int argc, char* argv[]) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700817 char* filename = NULL;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700818 char* oldfile = NULL;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700819 char* key_block_file = NULL;
820 char* signpubkey = NULL;
821 char* signprivate = NULL;
vbendeb00b90882010-10-21 13:46:16 -0700822 int version = -1;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700823 char* vmlinuz = NULL;
824 char* bootloader = NULL;
825 char* config_file = NULL;
Che-Liang Chiou03762032011-02-22 11:16:51 +0800826 int arch = ARCH_X86;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700827 int vblockonly = 0;
vbendebb2b0fcc2010-07-15 15:09:47 -0700828 int verbose = 0;
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +0800829 uint64_t kernel_body_load_address = CROS_32BIT_ENTRY_ADDR;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700830 uint64_t pad = DEFAULT_PADDING;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700831 int mode = 0;
832 int parse_error = 0;
Randall Spanglerae87b922011-05-12 13:27:28 -0700833 uint64_t min_version = 0;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700834 char* e;
Bill Richardsona08b5c92010-06-30 21:59:43 -0700835 int i,r;
836 blob_t *bp;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700837
Bill Richardsona08b5c92010-06-30 21:59:43 -0700838
839 char *progname = strrchr(argv[0], '/');
840 if (progname)
841 progname++;
842 else
843 progname = argv[0];
844
vbendebb2b0fcc2010-07-15 15:09:47 -0700845 while (((i = getopt_long(argc, argv, ":", long_opts, NULL)) != -1) &&
846 !parse_error) {
Randall Spangler7d6898d2010-06-11 09:22:13 -0700847 switch (i) {
vbendebb2b0fcc2010-07-15 15:09:47 -0700848 default:
Randall Spangler7d6898d2010-06-11 09:22:13 -0700849 case '?':
850 /* Unhandled option */
Randall Spangler7d6898d2010-06-11 09:22:13 -0700851 parse_error = 1;
852 break;
853
Bill Richardson4f36ef32010-08-09 17:50:14 -0700854 case 0:
855 /* silently handled option */
856 break;
857
Randall Spangler7d6898d2010-06-11 09:22:13 -0700858 case OPT_MODE_PACK:
Bill Richardsona08b5c92010-06-30 21:59:43 -0700859 case OPT_MODE_REPACK:
Randall Spangler7d6898d2010-06-11 09:22:13 -0700860 case OPT_MODE_VERIFY:
vbendebb2b0fcc2010-07-15 15:09:47 -0700861 if (mode && (mode != i)) {
862 fprintf(stderr, "Only single mode can be specified\n");
863 parse_error = 1;
864 break;
865 }
Randall Spangler7d6898d2010-06-11 09:22:13 -0700866 mode = i;
867 filename = optarg;
868 break;
869
Che-Liang Chiou03762032011-02-22 11:16:51 +0800870 case OPT_ARCH:
871 if (!strcasecmp(optarg, "x86"))
872 arch = ARCH_X86;
873 else if (!strcasecmp(optarg, "arm"))
874 arch = ARCH_ARM;
875 else {
876 fprintf(stderr, "Unknown architecture string: %s\n", optarg);
877 parse_error = 1;
878 }
879 break;
880
Bill Richardsona08b5c92010-06-30 21:59:43 -0700881 case OPT_OLDBLOB:
882 oldfile = optarg;
883 break;
884
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +0800885 case OPT_KLOADADDR:
886 kernel_body_load_address = strtoul(optarg, &e, 0);
887 if (!*optarg || (e && *e)) {
888 fprintf(stderr, "Invalid --kloadaddr\n");
889 parse_error = 1;
890 }
891 break;
892
Randall Spangler7d6898d2010-06-11 09:22:13 -0700893 case OPT_KEYBLOCK:
894 key_block_file = optarg;
895 break;
896
897 case OPT_SIGNPUBKEY:
898 signpubkey = optarg;
899 break;
900
901 case OPT_SIGNPRIVATE:
902 signprivate = optarg;
903 break;
904
905 case OPT_VMLINUZ:
906 vmlinuz = optarg;
907 break;
908
909 case OPT_BOOTLOADER:
910 bootloader = optarg;
911 break;
912
913 case OPT_CONFIG:
914 config_file = optarg;
915 break;
916
Bill Richardsona08b5c92010-06-30 21:59:43 -0700917 case OPT_VBLOCKONLY:
918 vblockonly = 1;
919 break;
920
Randall Spangler7d6898d2010-06-11 09:22:13 -0700921 case OPT_VERSION:
922 version = strtoul(optarg, &e, 0);
923 if (!*optarg || (e && *e)) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700924 fprintf(stderr, "Invalid --version\n");
Randall Spangler7d6898d2010-06-11 09:22:13 -0700925 parse_error = 1;
926 }
927 break;
928
Randall Spanglerae87b922011-05-12 13:27:28 -0700929 case OPT_MINVERSION:
930 min_version = strtoul(optarg, &e, 0);
931 if (!*optarg || (e && *e)) {
932 fprintf(stderr, "Invalid --minversion\n");
933 parse_error = 1;
934 }
935 break;
936
Randall Spangler7d6898d2010-06-11 09:22:13 -0700937 case OPT_PAD:
938 pad = strtoul(optarg, &e, 0);
939 if (!*optarg || (e && *e)) {
Bill Richardsona08b5c92010-06-30 21:59:43 -0700940 fprintf(stderr, "Invalid --pad\n");
Randall Spangler7d6898d2010-06-11 09:22:13 -0700941 parse_error = 1;
942 }
943 break;
vbendebb2b0fcc2010-07-15 15:09:47 -0700944
945 case OPT_VERBOSE:
946 verbose = 1;
947 break;
Randall Spangler7d6898d2010-06-11 09:22:13 -0700948 }
949 }
950
951 if (parse_error)
Bill Richardsona08b5c92010-06-30 21:59:43 -0700952 return PrintHelp(progname);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700953
954 switch(mode) {
955 case OPT_MODE_PACK:
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +0800956 bp = NewBlob(version, vmlinuz, bootloader, config_file, arch,
957 kernel_body_load_address);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700958 if (!bp)
959 return 1;
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +0800960 r = Pack(filename, key_block_file, signprivate, bp, pad, vblockonly,
961 kernel_body_load_address);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700962 FreeBlob(bp);
963 return r;
964
965 case OPT_MODE_REPACK:
vbendeb00b90882010-10-21 13:46:16 -0700966 if (!config_file && !key_block_file && (version<0)) {
vbendebb2b0fcc2010-07-15 15:09:47 -0700967 fprintf(stderr,
vbendeb858fffb2010-10-06 09:51:44 -0700968 "You must supply at least one of "
969 "--config, --keyblock or --version\n");
vbendebb2b0fcc2010-07-15 15:09:47 -0700970 return 1;
971 }
972
Randall Spangler7ecb39d2011-05-12 15:37:45 -0700973 bp = OldBlob(oldfile, pad);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700974 if (!bp)
975 return 1;
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +0800976 r = ReplaceConfig(bp, config_file, kernel_body_load_address);
vbendebb2b0fcc2010-07-15 15:09:47 -0700977 if (!r) {
vbendeb00b90882010-10-21 13:46:16 -0700978 if (version >= 0) {
Che-Liang Chiou03762032011-02-22 11:16:51 +0800979 bp->kernel_version = (uint64_t) version;
vbendeb858fffb2010-10-06 09:51:44 -0700980 }
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +0800981 r = Pack(filename, key_block_file, signprivate, bp, pad, vblockonly,
982 kernel_body_load_address);
vbendebb2b0fcc2010-07-15 15:09:47 -0700983 }
Bill Richardsona08b5c92010-06-30 21:59:43 -0700984 FreeBlob(bp);
985 return r;
986
Randall Spangler7d6898d2010-06-11 09:22:13 -0700987 case OPT_MODE_VERIFY:
Che-Liang Chiou2c0711b2011-03-22 13:15:19 +0800988 return Verify(filename, signpubkey, verbose, key_block_file,
Randall Spangler7ecb39d2011-05-12 15:37:45 -0700989 kernel_body_load_address, min_version, pad);
Bill Richardsona08b5c92010-06-30 21:59:43 -0700990
Randall Spangler7d6898d2010-06-11 09:22:13 -0700991 default:
Bill Richardsona08b5c92010-06-30 21:59:43 -0700992 fprintf(stderr,
993 "You must specify a mode: --pack, --repack or --verify\n");
994 return PrintHelp(progname);
Randall Spangler7d6898d2010-06-11 09:22:13 -0700995 }
996}