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