1 /*
2 * Copyright (c) 2014 Travis Geiselbrecht
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8 #include <lib/bootargs.h>
9
10 #include <lk/trace.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <lk/err.h>
14 #include <string.h>
15 #include <lk/init.h>
16
17 #if WITH_KERNEL_VM
18 #include <kernel/vm.h>
19 #endif
20
21 #define LOCAL_TRACE 0
22
23 /* saved by the main entry point in lk */
24 extern ulong lk_boot_args[4];
25
26 static bool boot_args_valid = false;
27 static struct lk_boot_arg *boot_args;
28
29 #define LK_BOOT_ARG_MAGIC 'lkbt'
30
31 /* lk style boot args are spread across the 4 incoming argument pointers as follows:
32 *
33 * [0] = LK_BOOT_ARG_MAGIC
34 * [1] = pointer to lk_boot_arg list
35 * [2] = unused
36 * [3] = xor of ULONG_MAX and the previous 3
37 */
38
39 #define LK_BOOT_ARG_TYPE_INITIAL 'args'
40 #define LK_BOOT_ARG_TYPE_COMMAND_LINE 'cmdl'
41 #define LK_BOOT_ARG_TYPE_BOOTIMAGE 'bimg'
42 #define LK_BOOT_ARG_TYPE_END 0
43
44 struct lk_boot_arg {
45 uint32_t type;
46 uint32_t len;
47
48 uint8_t data[];
49 };
50
51 struct lk_boot_arg_bootimage {
52 uint64_t offset;
53 size_t len;
54
55 char device[];
56 };
57
find_end(void * buf,size_t buf_len)58 static void *find_end(void *buf, size_t buf_len) {
59 uint8_t *ptr = (uint8_t *)buf;
60 struct lk_boot_arg *arg = (struct lk_boot_arg *)ptr;
61
62 while (arg->type != LK_BOOT_ARG_TYPE_END) {
63 ptr += sizeof(struct lk_boot_arg) + arg->len;
64
65 if ((uintptr_t)ptr - (uintptr_t)buf > buf_len)
66 return NULL;
67
68 arg = (struct lk_boot_arg *)ptr;
69 }
70
71 return ptr;
72 }
73
bootargs_start(void * buf,size_t buf_len)74 status_t bootargs_start(void *buf, size_t buf_len) {
75 if (buf_len < sizeof(struct lk_boot_arg))
76 return ERR_NO_MEMORY;
77
78 memset(buf, 0, buf_len);
79
80 struct lk_boot_arg *arg = (struct lk_boot_arg *)buf;
81 arg->type = LK_BOOT_ARG_TYPE_INITIAL;
82 arg->len = 0;
83
84 return NO_ERROR;
85 }
86
bootargs_add_command_line(void * buf,size_t buf_len,const char * str)87 status_t bootargs_add_command_line(void *buf, size_t buf_len, const char *str) {
88 struct lk_boot_arg *arg = find_end(buf, buf_len);
89 if (!arg)
90 return ERR_NO_MEMORY;
91
92 arg->type = LK_BOOT_ARG_TYPE_COMMAND_LINE;
93 arg->len = ROUNDUP(strlen(str) + 1, 4);
94 memset(arg->data, 0, arg->len);
95 strcpy((char *)arg->data, str);
96
97 return NO_ERROR;
98 }
99
bootargs_add_bootimage_pointer(void * buf,size_t buf_len,const char * device,uint64_t offset,size_t len)100 status_t bootargs_add_bootimage_pointer(void *buf, size_t buf_len, const char *device, uint64_t offset, size_t len) {
101 struct lk_boot_arg *arg = find_end(buf, buf_len);
102 if (!arg)
103 return ERR_NO_MEMORY;
104
105 arg->type = LK_BOOT_ARG_TYPE_BOOTIMAGE;
106 size_t string_len = device ? ROUNDUP(strlen(device) + 1, 4) : 0;
107 arg->len = string_len + sizeof(struct lk_boot_arg_bootimage);
108
109 struct lk_boot_arg_bootimage *bi = (struct lk_boot_arg_bootimage *)arg->data;
110
111 bi->offset = offset;
112 bi->len = len;
113 if (device) {
114 memset(bi->device, 0, string_len);
115 memcpy(bi->device, device, strlen(device));
116 }
117
118 return NO_ERROR;
119 }
120
bootargs_generate_lk_arg_values(ulong buf,ulong args[4])121 void bootargs_generate_lk_arg_values(ulong buf, ulong args[4]) {
122 args[0] = LK_BOOT_ARG_MAGIC;
123 args[1] = buf;
124 args[2] = 0;
125 args[3] = ULONG_MAX ^ args[0] ^ args[1] ^ args[2];
126 }
127
bootargs_init_hook(uint level)128 static void bootargs_init_hook(uint level) {
129 LTRACE_ENTRY;
130
131 /* see if there are any lk style boot arguments here */
132 if (lk_boot_args[0] != LK_BOOT_ARG_MAGIC) {
133 LTRACEF("failed magic check\n");
134 return;
135 }
136
137 if (lk_boot_args[3] != (ULONG_MAX ^ lk_boot_args[0] ^ lk_boot_args[1] ^ lk_boot_args[2])) {
138 LTRACEF("failed checksum\n");
139 return;
140 }
141
142 /* parse the boot arg pointer */
143 #if WITH_KERNEL_VM
144 boot_args = paddr_to_kvaddr(lk_boot_args[1]);
145 #else
146 boot_args = (void *)lk_boot_args[1];
147 #endif
148
149 if (!boot_args) {
150 LTRACEF("null or invalid boot pointer\n");
151 return;
152 }
153
154 /* see if the initial entry is the right one */
155 if (boot_args[0].type != LK_BOOT_ARG_TYPE_INITIAL) {
156 LTRACEF("bad initial arg\n");
157 return;
158 }
159
160 /* looks good */
161 boot_args_valid = true;
162
163 LTRACEF("valid args found\n");
164 }
165
bootargs_are_valid(void)166 bool bootargs_are_valid(void) {
167 return boot_args_valid;
168 }
169
find_tag(uint32_t tag)170 static struct lk_boot_arg *find_tag(uint32_t tag) {
171 if (!boot_args_valid)
172 return NULL;
173
174 struct lk_boot_arg *arg = boot_args;
175
176 while (arg->type != LK_BOOT_ARG_TYPE_END) {
177 if (arg->type == tag)
178 return arg;
179
180 arg = (struct lk_boot_arg *)((uintptr_t)arg + sizeof(struct lk_boot_arg) + arg->len);
181 }
182
183 return NULL;
184 }
185
bootargs_get_command_line(void)186 const char *bootargs_get_command_line(void) {
187 struct lk_boot_arg *arg = find_tag(LK_BOOT_ARG_TYPE_COMMAND_LINE);
188 if (!arg)
189 return NULL;
190
191 // XXX validate it
192
193 return (const char *)arg->data;
194 }
195
bootargs_get_bootimage_pointer(uint64_t * offset,size_t * len,const char ** device)196 status_t bootargs_get_bootimage_pointer(uint64_t *offset, size_t *len, const char **device) {
197 struct lk_boot_arg *arg = find_tag(LK_BOOT_ARG_TYPE_BOOTIMAGE);
198 if (!arg)
199 return ERR_NOT_FOUND;
200
201 // XXX validate it
202
203 struct lk_boot_arg_bootimage *bi = (struct lk_boot_arg_bootimage *)arg->data;
204
205 if (device) {
206 if (arg->len != sizeof(struct lk_boot_arg_bootimage)) {
207 /* string is present */
208 *device = bi->device;
209 } else {
210 *device = NULL;
211 }
212 }
213
214 if (offset)
215 *offset = bi->offset;
216 if (len)
217 *len = bi->len;
218
219 return NO_ERROR;
220 }
221
222 LK_INIT_HOOK(bootargs, bootargs_init_hook, LK_INIT_LEVEL_THREADING);
223
224