blob: 3ef13c6488acb76a0dfd4f57cadb5ab29da30af5 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2018 MediaTek Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24#include <assert.h>
25#include <debug.h>
26#include <err.h>
27#include <kernel/mutex.h>
28#include <kernel/thread.h>
29#include <lib/kcmdline.h>
30#include <libfdt.h>
31#include <list.h>
32#include <malloc.h>
33#include <stddef.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <trace.h>
38
39#if LK_DEBUGLEVEL > 0
40#define LOCAL_TRACE 1
41#else
42#define LOCAL_TRACE 0
43#endif
44
45#define CMDLINE_OVERFLOW_STR "[ERROR] CMDLINE overflow"
46
47struct subst_entry {
48 struct list_node node;
49 char *old_arg;
50 char *new_arg;
51};
52
53/* variable for keeping substition arg */
54struct list_node subst_list = LIST_INITIAL_VALUE(subst_list);
55
56/* variable for keeping append arg */
57static char *cmdline_buf;
58static char *cmdline_tail;
59static char *cmdline_end;
60
61static mutex_t lock = MUTEX_INITIAL_VALUE(lock);
62
63static inline void validate_cmdline_boundary(const char *tail, const char *end)
64{
65 if (tail >= end) {
66 dprintf(CRITICAL, CMDLINE_OVERFLOW_STR"\n");
67 panic(CMDLINE_OVERFLOW_STR);
68 }
69}
70
71static void dump_cmdline(void *fdt)
72{
73 int len;
74 int chosen_node_offset;
75 const char *cmdline;
76
77 chosen_node_offset = fdt_path_offset(fdt, "/chosen");
78 if (chosen_node_offset < 0) {
79 LTRACEF("can't find chosen node.\n");
80 return;
81 }
82
83 cmdline = fdt_getprop(fdt, chosen_node_offset, "bootargs", &len);
84 if (!cmdline) {
85 LTRACEF("fdt_getprop bootargs failed.\n");
86 return;
87 }
88
89 LTRACEF("cmdline len=%zd, str=\"%s\"\n", strlen(cmdline), cmdline);
90}
91
92int kcmdline_init(void)
93{
94 if (cmdline_buf)
95 return ERR_ALREADY_EXISTS;
96
97 cmdline_buf = (char *)malloc(CMDLINE_LEN);
98 if (!cmdline_buf)
99 return ERR_NO_MEMORY;
100
101 memset(cmdline_buf, 0, CMDLINE_LEN);
102 cmdline_tail = cmdline_buf;
103 cmdline_end = cmdline_buf + CMDLINE_LEN;
104
105 return NO_ERROR;
106}
107
108int kcmdline_finalized(void *fdt, size_t size)
109{
110 int ret;
111 int len;
112 int offset;
113 const void *fdt_bootargs;
114 char *temp_ptr = NULL;
115 size_t append_arg_len = 0;
116 struct subst_entry *entry;
117 struct subst_entry *temp;
118
119 if (!cmdline_buf)
120 return ERR_NOT_READY;
121
122 if (!fdt || !size) {
123 LTRACEF("Invalid args: fdt(%p), size(%zd)\n", fdt, size);
124 return ERR_INVALID_ARGS;
125 }
126
127 ret = NO_ERROR;
128 mutex_acquire(&lock);
129
130 if ((*cmdline_buf == 0x0) && list_is_empty(&subst_list))
131 goto exit;
132
133 /* cmdline_buf is filled with bootargs before kcmdline_finalize is called */
134 if (*cmdline_buf != 0x0) {
135 append_arg_len = strlen(cmdline_buf);
136 temp_ptr = (char *)malloc(append_arg_len + 1);
137 if (!temp_ptr) {
138 ret = ERR_NO_MEMORY;
139 goto exit;
140 }
141 snprintf(temp_ptr, (append_arg_len + 1), "%s", cmdline_buf);
142 LTRACEF("append_arg_len:%zu cmdline_buf:%s\n",
143 append_arg_len, cmdline_buf);
144 }
145
146 ret = fdt_open_into(fdt, fdt, size);
147 if (ret) {
148 LTRACEF("fdt_open_into failed\n");
149 goto exit;
150 }
151 ret = fdt_check_header(fdt);
152 if (ret) {
153 LTRACEF("fdt_check_header failed\n");
154 goto exit;
155 }
156
157 /* Reset cmdline_tail */
158 cmdline_tail = cmdline_buf;
159 offset = fdt_path_offset(fdt, "/chosen");
160 fdt_bootargs = fdt_getprop(fdt, offset, "bootargs", &len);
161 if (!fdt_bootargs) {
162 ret = ERR_NOT_FOUND;
163 goto exit;
164 }
165
166 /* add appended string to final string */
167 validate_cmdline_boundary(cmdline_tail + append_arg_len +
168 strlen(fdt_bootargs) + 2, cmdline_end);
169 cmdline_tail += snprintf(cmdline_tail, CMDLINE_LEN, "%s",
170 (char *)fdt_bootargs);
171 if (temp_ptr) {
172 cmdline_tail += snprintf(cmdline_tail, cmdline_end - cmdline_tail,
173 " %s", temp_ptr);
174 }
175
176 /* subst string in final string */
177 list_for_every_entry_safe(&subst_list, entry, temp,
178 struct subst_entry, node) {
179 char *pos = strstr(cmdline_buf, entry->old_arg);
180 size_t old_len = strlen(entry->old_arg);
181 if (pos && ((*(pos + old_len) == ' ') || (*(pos + old_len) == '\0'))) {
182 size_t new_len = strlen(entry->new_arg);
183 char *p;
184
185 /* erase old arg with space */
186 memset(pos, ' ', old_len);
187 if (old_len >= new_len) {
188 p = pos; /* replace old arg with new arg */
189 } else {
190 /* append new arg in the end of cmdline */
191 validate_cmdline_boundary(cmdline_tail + new_len + 2,
192 cmdline_end);
193 p = cmdline_tail;
194 cmdline_tail += (new_len + 1);
195 *p++ = ' ';
196 }
197 memcpy(p, entry->new_arg, new_len);
198 }
199
200 /* free memory and delete node */
201 free(entry->old_arg);
202 free(entry->new_arg);
203 list_delete(&entry->node);
204 free(entry);
205 entry = NULL;
206 }
207
208 ret = fdt_setprop(fdt, offset, "bootargs", cmdline_buf,
209 strlen(cmdline_buf) + 1);
210 if (ret != 0) {
211 dprintf(CRITICAL, "fdt_setprop error.\n");
212 ret = ERR_GENERIC;
213 goto exit;
214 }
215
216 ret = fdt_pack(fdt);
217 if (ret != 0) {
218 dprintf(CRITICAL, "fdt_pack error.\n");
219 ret = ERR_GENERIC;
220 goto exit;
221 }
222
223 free(cmdline_buf);
224 cmdline_buf = NULL;
225
226#if LOCAL_TRACE
227 dump_cmdline(fdt);
228#endif
229
230exit:
231 free(temp_ptr);
232 mutex_release(&lock);
233
234 return ret;
235}
236
237void kcmdline_print(void)
238{
239 struct subst_entry *entry;
240
241 if (!cmdline_buf)
242 return;
243
244 mutex_acquire(&lock);
245 LTRACEF("append cmdline: %s\n", cmdline_buf);
246 LTRACEF("append cmdline size: %zd\n", strlen(cmdline_buf));
247 LTRACEF("subst list:\n");
248
249 /* traverse list to show subst_list */
250 list_for_every_entry(&subst_list, entry, struct subst_entry, node) {
251 LTRACEF("old_arg: %s, new_arg: %s\n", entry->old_arg, entry->new_arg);
252 }
253 mutex_release(&lock);
254}
255
256int kcmdline_append(const char *append_arg)
257{
258 size_t append_arg_len;
259
260 if (!cmdline_buf)
261 return ERR_NOT_READY;
262
263 if (!append_arg)
264 return ERR_INVALID_ARGS;
265
266 mutex_acquire(&lock);
267 append_arg_len = strlen(append_arg);
268 validate_cmdline_boundary(cmdline_tail + append_arg_len + 1, cmdline_end);
269 cmdline_tail += snprintf(cmdline_tail, cmdline_end - cmdline_tail, " %s",
270 append_arg);
271 mutex_release(&lock);
272
273 return NO_ERROR;
274}
275
276int kcmdline_subst(const char *old_arg, const char *new_arg)
277{
278 struct subst_entry *entry;
279
280 if (!cmdline_buf)
281 return ERR_NOT_READY;
282
283 if (!old_arg || !new_arg) {
284 LTRACEF("Invalid args: old_arg(%p), new_arg(%p)\n", old_arg, new_arg);
285 return ERR_INVALID_ARGS;
286 }
287
288 entry = (struct subst_entry *)malloc(sizeof(struct subst_entry));
289 if (!entry)
290 return ERR_NO_MEMORY;
291
292 memset(entry, 0, sizeof(struct subst_entry));
293 entry->old_arg = strdup(old_arg);
294 entry->new_arg = strdup(new_arg);
295
296 if (!entry->old_arg || !entry->new_arg) {
297 free(entry->old_arg);
298 free(entry->new_arg);
299 free(entry);
300
301 return ERR_NO_MEMORY;
302 }
303
304 mutex_acquire(&lock);
305 list_add_tail(&subst_list, &entry->node);
306 mutex_release(&lock);
307
308 return NO_ERROR;
309}
310