1 /* Copyright 2012 The ChromiumOS Authors
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 *
5 * Exports the kernel commandline from a given partition/image.
6 */
7
8 #include <fcntl.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <sys/mman.h>
12 #include <sys/stat.h>
13 #if !defined (__FreeBSD__) && !defined(__OpenBSD__)
14 #include <sys/sysmacros.h>
15 #endif
16 #include <sys/types.h>
17 #include <unistd.h>
18
19 #include "futility.h"
20 #include "host_common.h"
21 #include "kernel_blob.h"
22 #include "vboot_api.h"
23 #include "vboot_host.h"
24
25 typedef ssize_t (*ReadFullyFn)(void *ctx, void *buf, size_t count);
26
ReadFullyWithRead(void * ctx,void * buf,size_t count)27 static ssize_t ReadFullyWithRead(void *ctx, void *buf, size_t count)
28 {
29 ssize_t nr_read = 0;
30 int fd = *((int*)ctx);
31 while (nr_read < count) {
32 ssize_t to_read = count - nr_read;
33 ssize_t chunk = read(fd, buf + nr_read, to_read);
34 if (chunk < 0) {
35 return -1;
36 } else if (chunk == 0) {
37 break;
38 }
39 nr_read += chunk;
40 }
41 return nr_read;
42 }
43
44 /* Skip the stream by calling |read_fn| many times. Return 0 on success. */
SkipWithRead(void * ctx,ReadFullyFn read_fn,size_t count)45 static int SkipWithRead(void *ctx, ReadFullyFn read_fn, size_t count)
46 {
47 char buf[1024];
48 ssize_t nr_skipped = 0;
49 while (nr_skipped < count) {
50 ssize_t to_read = count - nr_skipped;
51 if (to_read > sizeof(buf)) {
52 to_read = sizeof(buf);
53 }
54 if (read_fn(ctx, buf, to_read) != to_read) {
55 return -1;
56 }
57 nr_skipped += to_read;
58 }
59 return 0;
60 }
61
FindKernelConfigFromStream(void * ctx,ReadFullyFn read_fn,uint64_t kernel_body_load_address)62 static char *FindKernelConfigFromStream(void *ctx, ReadFullyFn read_fn,
63 uint64_t kernel_body_load_address)
64 {
65 struct vb2_keyblock keyblock;
66 struct vb2_kernel_preamble preamble;
67 uint32_t now = 0;
68 uint32_t offset = 0;
69
70 /* Skip the keyblock */
71 if (read_fn(ctx, &keyblock, sizeof(keyblock)) != sizeof(keyblock)) {
72 FATAL("not enough data to fill keyblock header\n");
73 return NULL;
74 }
75 ssize_t to_skip = keyblock.keyblock_size - sizeof(keyblock);
76 if (to_skip < 0 || SkipWithRead(ctx, read_fn, to_skip)) {
77 FATAL("keyblock_size advances past the end of the blob\n");
78 return NULL;
79 }
80 now += keyblock.keyblock_size;
81
82 /* Open up the preamble */
83 if (read_fn(ctx, &preamble, sizeof(preamble)) != sizeof(preamble)) {
84 FATAL("not enough data to fill preamble\n");
85 return NULL;
86 }
87 to_skip = preamble.preamble_size - sizeof(preamble);
88 if (to_skip < 0 || SkipWithRead(ctx, read_fn, to_skip)) {
89 FATAL("preamble_size advances past the end of the blob\n");
90 return NULL;
91 }
92 now += preamble.preamble_size;
93
94 /* Read body_load_address from preamble if no
95 * kernel_body_load_address */
96 if (kernel_body_load_address == USE_PREAMBLE_LOAD_ADDR)
97 kernel_body_load_address = preamble.body_load_address;
98
99 /* The x86 kernels have a pointer to the kernel commandline in the
100 * zeropage table, but that's irrelevant for ARM. Both types keep the
101 * config blob in the same place, so just go find it. */
102 offset = preamble.bootloader_address -
103 (kernel_body_load_address + CROS_PARAMS_SIZE +
104 CROS_CONFIG_SIZE) + now;
105 to_skip = offset - now;
106 if (to_skip < 0 || SkipWithRead(ctx, read_fn, to_skip)) {
107 FATAL("params are outside of the memory blob: %x\n", offset);
108 return NULL;
109 }
110 char *ret = malloc(CROS_CONFIG_SIZE);
111 if (!ret) {
112 FATAL("No memory\n");
113 return NULL;
114 }
115 if (read_fn(ctx, ret, CROS_CONFIG_SIZE) != CROS_CONFIG_SIZE) {
116 FATAL("Cannot read kernel config\n");
117 free(ret);
118 ret = NULL;
119 }
120 return ret;
121 }
122
FindKernelConfig(const char * infile,uint64_t kernel_body_load_address)123 char *FindKernelConfig(const char *infile, uint64_t kernel_body_load_address)
124 {
125 char *newstr = NULL;
126
127 int fd = open(infile, O_RDONLY | O_CLOEXEC
128 #if !defined(__FreeBSD__) && !defined(__OpenBSD__)
129 | O_LARGEFILE
130 #endif
131 );
132 if (fd < 0) {
133 FATAL("Cannot open %s\n", infile);
134 return NULL;
135 }
136
137 void *ctx = &fd;
138 ReadFullyFn read_fn = ReadFullyWithRead;
139
140 newstr = FindKernelConfigFromStream(ctx, read_fn,
141 kernel_body_load_address);
142
143 close(fd);
144
145 return newstr;
146 }
147