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