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