xref: /aosp_15_r20/external/vboot_reference/futility/dump_kernel_config_lib.c (revision 8617a60d3594060b7ecbd21bc622a7c14f3cf2bc)
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