1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/init.h> |
3 | #include <linux/async.h> |
4 | #include <linux/fs.h> |
5 | #include <linux/slab.h> |
6 | #include <linux/types.h> |
7 | #include <linux/fcntl.h> |
8 | #include <linux/delay.h> |
9 | #include <linux/string.h> |
10 | #include <linux/dirent.h> |
11 | #include <linux/syscalls.h> |
12 | #include <linux/utime.h> |
13 | #include <linux/file.h> |
14 | #include <linux/kstrtox.h> |
15 | #include <linux/memblock.h> |
16 | #include <linux/mm.h> |
17 | #include <linux/namei.h> |
18 | #include <linux/init_syscalls.h> |
19 | #include <linux/task_work.h> |
20 | #include <linux/umh.h> |
21 | |
22 | static __initdata bool csum_present; |
23 | static __initdata u32 io_csum; |
24 | |
25 | static ssize_t __init xwrite(struct file *file, const unsigned char *p, |
26 | size_t count, loff_t *pos) |
27 | { |
28 | ssize_t out = 0; |
29 | |
30 | /* sys_write only can write MAX_RW_COUNT aka 2G-4K bytes at most */ |
31 | while (count) { |
32 | ssize_t rv = kernel_write(file, p, count, pos); |
33 | |
34 | if (rv < 0) { |
35 | if (rv == -EINTR || rv == -EAGAIN) |
36 | continue; |
37 | return out ? out : rv; |
38 | } else if (rv == 0) |
39 | break; |
40 | |
41 | if (csum_present) { |
42 | ssize_t i; |
43 | |
44 | for (i = 0; i < rv; i++) |
45 | io_csum += p[i]; |
46 | } |
47 | |
48 | p += rv; |
49 | out += rv; |
50 | count -= rv; |
51 | } |
52 | |
53 | return out; |
54 | } |
55 | |
56 | static __initdata char *message; |
57 | static void __init error(char *x) |
58 | { |
59 | if (!message) |
60 | message = x; |
61 | } |
62 | |
63 | #define panic_show_mem(fmt, ...) \ |
64 | ({ show_mem(); panic(fmt, ##__VA_ARGS__); }) |
65 | |
66 | /* link hash */ |
67 | |
68 | #define N_ALIGN(len) ((((len) + 1) & ~3) + 2) |
69 | |
70 | static __initdata struct hash { |
71 | int ino, minor, major; |
72 | umode_t mode; |
73 | struct hash *next; |
74 | char name[N_ALIGN(PATH_MAX)]; |
75 | } *head[32]; |
76 | |
77 | static inline int hash(int major, int minor, int ino) |
78 | { |
79 | unsigned long tmp = ino + minor + (major << 3); |
80 | tmp += tmp >> 5; |
81 | return tmp & 31; |
82 | } |
83 | |
84 | static char __init *find_link(int major, int minor, int ino, |
85 | umode_t mode, char *name) |
86 | { |
87 | struct hash **p, *q; |
88 | for (p = head + hash(major, minor, ino); *p; p = &(*p)->next) { |
89 | if ((*p)->ino != ino) |
90 | continue; |
91 | if ((*p)->minor != minor) |
92 | continue; |
93 | if ((*p)->major != major) |
94 | continue; |
95 | if (((*p)->mode ^ mode) & S_IFMT) |
96 | continue; |
97 | return (*p)->name; |
98 | } |
99 | q = kmalloc(size: sizeof(struct hash), GFP_KERNEL); |
100 | if (!q) |
101 | panic_show_mem("can't allocate link hash entry" ); |
102 | q->major = major; |
103 | q->minor = minor; |
104 | q->ino = ino; |
105 | q->mode = mode; |
106 | strcpy(p: q->name, q: name); |
107 | q->next = NULL; |
108 | *p = q; |
109 | return NULL; |
110 | } |
111 | |
112 | static void __init free_hash(void) |
113 | { |
114 | struct hash **p, *q; |
115 | for (p = head; p < head + 32; p++) { |
116 | while (*p) { |
117 | q = *p; |
118 | *p = q->next; |
119 | kfree(objp: q); |
120 | } |
121 | } |
122 | } |
123 | |
124 | #ifdef CONFIG_INITRAMFS_PRESERVE_MTIME |
125 | static void __init do_utime(char *filename, time64_t mtime) |
126 | { |
127 | struct timespec64 t[2] = { { .tv_sec = mtime }, { .tv_sec = mtime } }; |
128 | init_utimes(filename, ts: t); |
129 | } |
130 | |
131 | static void __init do_utime_path(const struct path *path, time64_t mtime) |
132 | { |
133 | struct timespec64 t[2] = { { .tv_sec = mtime }, { .tv_sec = mtime } }; |
134 | vfs_utimes(path, times: t); |
135 | } |
136 | |
137 | static __initdata LIST_HEAD(dir_list); |
138 | struct dir_entry { |
139 | struct list_head list; |
140 | time64_t mtime; |
141 | char name[]; |
142 | }; |
143 | |
144 | static void __init dir_add(const char *name, time64_t mtime) |
145 | { |
146 | size_t nlen = strlen(name) + 1; |
147 | struct dir_entry *de; |
148 | |
149 | de = kmalloc(size: sizeof(struct dir_entry) + nlen, GFP_KERNEL); |
150 | if (!de) |
151 | panic_show_mem("can't allocate dir_entry buffer" ); |
152 | INIT_LIST_HEAD(list: &de->list); |
153 | strscpy(p: de->name, q: name, size: nlen); |
154 | de->mtime = mtime; |
155 | list_add(new: &de->list, head: &dir_list); |
156 | } |
157 | |
158 | static void __init dir_utime(void) |
159 | { |
160 | struct dir_entry *de, *tmp; |
161 | list_for_each_entry_safe(de, tmp, &dir_list, list) { |
162 | list_del(entry: &de->list); |
163 | do_utime(filename: de->name, mtime: de->mtime); |
164 | kfree(objp: de); |
165 | } |
166 | } |
167 | #else |
168 | static void __init do_utime(char *filename, time64_t mtime) {} |
169 | static void __init do_utime_path(const struct path *path, time64_t mtime) {} |
170 | static void __init dir_add(const char *name, time64_t mtime) {} |
171 | static void __init dir_utime(void) {} |
172 | #endif |
173 | |
174 | static __initdata time64_t mtime; |
175 | |
176 | /* cpio header parsing */ |
177 | |
178 | static __initdata unsigned long ino, major, minor, nlink; |
179 | static __initdata umode_t mode; |
180 | static __initdata unsigned long body_len, name_len; |
181 | static __initdata uid_t uid; |
182 | static __initdata gid_t gid; |
183 | static __initdata unsigned rdev; |
184 | static __initdata u32 hdr_csum; |
185 | |
186 | static void __init (char *s) |
187 | { |
188 | unsigned long parsed[13]; |
189 | char buf[9]; |
190 | int i; |
191 | |
192 | buf[8] = '\0'; |
193 | for (i = 0, s += 6; i < 13; i++, s += 8) { |
194 | memcpy(buf, s, 8); |
195 | parsed[i] = simple_strtoul(buf, NULL, 16); |
196 | } |
197 | ino = parsed[0]; |
198 | mode = parsed[1]; |
199 | uid = parsed[2]; |
200 | gid = parsed[3]; |
201 | nlink = parsed[4]; |
202 | mtime = parsed[5]; /* breaks in y2106 */ |
203 | body_len = parsed[6]; |
204 | major = parsed[7]; |
205 | minor = parsed[8]; |
206 | rdev = new_encode_dev(MKDEV(parsed[9], parsed[10])); |
207 | name_len = parsed[11]; |
208 | hdr_csum = parsed[12]; |
209 | } |
210 | |
211 | /* FSM */ |
212 | |
213 | static __initdata enum state { |
214 | Start, |
215 | Collect, |
216 | , |
217 | SkipIt, |
218 | GotName, |
219 | CopyFile, |
220 | GotSymlink, |
221 | Reset |
222 | } state, next_state; |
223 | |
224 | static __initdata char *victim; |
225 | static unsigned long byte_count __initdata; |
226 | static __initdata loff_t , ; |
227 | |
228 | static inline void __init eat(unsigned n) |
229 | { |
230 | victim += n; |
231 | this_header += n; |
232 | byte_count -= n; |
233 | } |
234 | |
235 | static __initdata char *collected; |
236 | static long remains __initdata; |
237 | static __initdata char *collect; |
238 | |
239 | static void __init read_into(char *buf, unsigned size, enum state next) |
240 | { |
241 | if (byte_count >= size) { |
242 | collected = victim; |
243 | eat(n: size); |
244 | state = next; |
245 | } else { |
246 | collect = collected = buf; |
247 | remains = size; |
248 | next_state = next; |
249 | state = Collect; |
250 | } |
251 | } |
252 | |
253 | static __initdata char *, *symlink_buf, *name_buf; |
254 | |
255 | static int __init do_start(void) |
256 | { |
257 | read_into(buf: header_buf, size: 110, next: GotHeader); |
258 | return 0; |
259 | } |
260 | |
261 | static int __init do_collect(void) |
262 | { |
263 | unsigned long n = remains; |
264 | if (byte_count < n) |
265 | n = byte_count; |
266 | memcpy(collect, victim, n); |
267 | eat(n); |
268 | collect += n; |
269 | if ((remains -= n) != 0) |
270 | return 1; |
271 | state = next_state; |
272 | return 0; |
273 | } |
274 | |
275 | static int __init (void) |
276 | { |
277 | if (!memcmp(p: collected, q: "070701" , size: 6)) { |
278 | csum_present = false; |
279 | } else if (!memcmp(p: collected, q: "070702" , size: 6)) { |
280 | csum_present = true; |
281 | } else { |
282 | if (memcmp(p: collected, q: "070707" , size: 6) == 0) |
283 | error(x: "incorrect cpio method used: use -H newc option" ); |
284 | else |
285 | error(x: "no cpio magic" ); |
286 | return 1; |
287 | } |
288 | parse_header(s: collected); |
289 | next_header = this_header + N_ALIGN(name_len) + body_len; |
290 | next_header = (next_header + 3) & ~3; |
291 | state = SkipIt; |
292 | if (name_len <= 0 || name_len > PATH_MAX) |
293 | return 0; |
294 | if (S_ISLNK(mode)) { |
295 | if (body_len > PATH_MAX) |
296 | return 0; |
297 | collect = collected = symlink_buf; |
298 | remains = N_ALIGN(name_len) + body_len; |
299 | next_state = GotSymlink; |
300 | state = Collect; |
301 | return 0; |
302 | } |
303 | if (S_ISREG(mode) || !body_len) |
304 | read_into(buf: name_buf, N_ALIGN(name_len), next: GotName); |
305 | return 0; |
306 | } |
307 | |
308 | static int __init do_skip(void) |
309 | { |
310 | if (this_header + byte_count < next_header) { |
311 | eat(n: byte_count); |
312 | return 1; |
313 | } else { |
314 | eat(n: next_header - this_header); |
315 | state = next_state; |
316 | return 0; |
317 | } |
318 | } |
319 | |
320 | static int __init do_reset(void) |
321 | { |
322 | while (byte_count && *victim == '\0') |
323 | eat(n: 1); |
324 | if (byte_count && (this_header & 3)) |
325 | error(x: "broken padding" ); |
326 | return 1; |
327 | } |
328 | |
329 | static void __init clean_path(char *path, umode_t fmode) |
330 | { |
331 | struct kstat st; |
332 | |
333 | if (!init_stat(filename: path, stat: &st, AT_SYMLINK_NOFOLLOW) && |
334 | (st.mode ^ fmode) & S_IFMT) { |
335 | if (S_ISDIR(st.mode)) |
336 | init_rmdir(pathname: path); |
337 | else |
338 | init_unlink(pathname: path); |
339 | } |
340 | } |
341 | |
342 | static int __init maybe_link(void) |
343 | { |
344 | if (nlink >= 2) { |
345 | char *old = find_link(major, minor, ino, mode, name: collected); |
346 | if (old) { |
347 | clean_path(path: collected, fmode: 0); |
348 | return (init_link(oldname: old, newname: collected) < 0) ? -1 : 1; |
349 | } |
350 | } |
351 | return 0; |
352 | } |
353 | |
354 | static __initdata struct file *wfile; |
355 | static __initdata loff_t wfile_pos; |
356 | |
357 | static int __init do_name(void) |
358 | { |
359 | state = SkipIt; |
360 | next_state = Reset; |
361 | if (strcmp(collected, "TRAILER!!!" ) == 0) { |
362 | free_hash(); |
363 | return 0; |
364 | } |
365 | clean_path(path: collected, fmode: mode); |
366 | if (S_ISREG(mode)) { |
367 | int ml = maybe_link(); |
368 | if (ml >= 0) { |
369 | int openflags = O_WRONLY|O_CREAT; |
370 | if (ml != 1) |
371 | openflags |= O_TRUNC; |
372 | wfile = filp_open(collected, openflags, mode); |
373 | if (IS_ERR(ptr: wfile)) |
374 | return 0; |
375 | wfile_pos = 0; |
376 | io_csum = 0; |
377 | |
378 | vfs_fchown(file: wfile, user: uid, group: gid); |
379 | vfs_fchmod(file: wfile, mode); |
380 | if (body_len) |
381 | vfs_truncate(&wfile->f_path, body_len); |
382 | state = CopyFile; |
383 | } |
384 | } else if (S_ISDIR(mode)) { |
385 | init_mkdir(pathname: collected, mode); |
386 | init_chown(filename: collected, user: uid, group: gid, flags: 0); |
387 | init_chmod(filename: collected, mode); |
388 | dir_add(name: collected, mtime); |
389 | } else if (S_ISBLK(mode) || S_ISCHR(mode) || |
390 | S_ISFIFO(mode) || S_ISSOCK(mode)) { |
391 | if (maybe_link() == 0) { |
392 | init_mknod(filename: collected, mode, dev: rdev); |
393 | init_chown(filename: collected, user: uid, group: gid, flags: 0); |
394 | init_chmod(filename: collected, mode); |
395 | do_utime(filename: collected, mtime); |
396 | } |
397 | } |
398 | return 0; |
399 | } |
400 | |
401 | static int __init do_copy(void) |
402 | { |
403 | if (byte_count >= body_len) { |
404 | if (xwrite(file: wfile, p: victim, count: body_len, pos: &wfile_pos) != body_len) |
405 | error(x: "write error" ); |
406 | |
407 | do_utime_path(path: &wfile->f_path, mtime); |
408 | fput(wfile); |
409 | if (csum_present && io_csum != hdr_csum) |
410 | error(x: "bad data checksum" ); |
411 | eat(n: body_len); |
412 | state = SkipIt; |
413 | return 0; |
414 | } else { |
415 | if (xwrite(file: wfile, p: victim, count: byte_count, pos: &wfile_pos) != byte_count) |
416 | error(x: "write error" ); |
417 | body_len -= byte_count; |
418 | eat(n: byte_count); |
419 | return 1; |
420 | } |
421 | } |
422 | |
423 | static int __init do_symlink(void) |
424 | { |
425 | collected[N_ALIGN(name_len) + body_len] = '\0'; |
426 | clean_path(path: collected, fmode: 0); |
427 | init_symlink(oldname: collected + N_ALIGN(name_len), newname: collected); |
428 | init_chown(filename: collected, user: uid, group: gid, AT_SYMLINK_NOFOLLOW); |
429 | do_utime(filename: collected, mtime); |
430 | state = SkipIt; |
431 | next_state = Reset; |
432 | return 0; |
433 | } |
434 | |
435 | static __initdata int (*actions[])(void) = { |
436 | [Start] = do_start, |
437 | [Collect] = do_collect, |
438 | [GotHeader] = do_header, |
439 | [SkipIt] = do_skip, |
440 | [GotName] = do_name, |
441 | [CopyFile] = do_copy, |
442 | [GotSymlink] = do_symlink, |
443 | [Reset] = do_reset, |
444 | }; |
445 | |
446 | static long __init write_buffer(char *buf, unsigned long len) |
447 | { |
448 | byte_count = len; |
449 | victim = buf; |
450 | |
451 | while (!actions[state]()) |
452 | ; |
453 | return len - byte_count; |
454 | } |
455 | |
456 | static long __init flush_buffer(void *bufv, unsigned long len) |
457 | { |
458 | char *buf = bufv; |
459 | long written; |
460 | long origLen = len; |
461 | if (message) |
462 | return -1; |
463 | while ((written = write_buffer(buf, len)) < len && !message) { |
464 | char c = buf[written]; |
465 | if (c == '0') { |
466 | buf += written; |
467 | len -= written; |
468 | state = Start; |
469 | } else if (c == 0) { |
470 | buf += written; |
471 | len -= written; |
472 | state = Reset; |
473 | } else |
474 | error(x: "junk within compressed archive" ); |
475 | } |
476 | return origLen; |
477 | } |
478 | |
479 | static unsigned long my_inptr __initdata; /* index of next byte to be processed in inbuf */ |
480 | |
481 | #include <linux/decompress/generic.h> |
482 | |
483 | static char * __init unpack_to_rootfs(char *buf, unsigned long len) |
484 | { |
485 | long written; |
486 | decompress_fn decompress; |
487 | const char *compress_name; |
488 | static __initdata char msg_buf[64]; |
489 | |
490 | header_buf = kmalloc(size: 110, GFP_KERNEL); |
491 | symlink_buf = kmalloc(PATH_MAX + N_ALIGN(PATH_MAX) + 1, GFP_KERNEL); |
492 | name_buf = kmalloc(N_ALIGN(PATH_MAX), GFP_KERNEL); |
493 | |
494 | if (!header_buf || !symlink_buf || !name_buf) |
495 | panic_show_mem("can't allocate buffers" ); |
496 | |
497 | state = Start; |
498 | this_header = 0; |
499 | message = NULL; |
500 | while (!message && len) { |
501 | loff_t saved_offset = this_header; |
502 | if (*buf == '0' && !(this_header & 3)) { |
503 | state = Start; |
504 | written = write_buffer(buf, len); |
505 | buf += written; |
506 | len -= written; |
507 | continue; |
508 | } |
509 | if (!*buf) { |
510 | buf++; |
511 | len--; |
512 | this_header++; |
513 | continue; |
514 | } |
515 | this_header = 0; |
516 | decompress = decompress_method(inbuf: buf, len, name: &compress_name); |
517 | pr_debug("Detected %s compressed data\n" , compress_name); |
518 | if (decompress) { |
519 | int res = decompress(buf, len, NULL, flush_buffer, NULL, |
520 | &my_inptr, error); |
521 | if (res) |
522 | error(x: "decompressor failed" ); |
523 | } else if (compress_name) { |
524 | if (!message) { |
525 | snprintf(buf: msg_buf, size: sizeof msg_buf, |
526 | fmt: "compression method %s not configured" , |
527 | compress_name); |
528 | message = msg_buf; |
529 | } |
530 | } else |
531 | error(x: "invalid magic at start of compressed archive" ); |
532 | if (state != Reset) |
533 | error(x: "junk at the end of compressed archive" ); |
534 | this_header = saved_offset + my_inptr; |
535 | buf += my_inptr; |
536 | len -= my_inptr; |
537 | } |
538 | dir_utime(); |
539 | kfree(objp: name_buf); |
540 | kfree(objp: symlink_buf); |
541 | kfree(objp: header_buf); |
542 | return message; |
543 | } |
544 | |
545 | static int __initdata do_retain_initrd; |
546 | |
547 | static int __init retain_initrd_param(char *str) |
548 | { |
549 | if (*str) |
550 | return 0; |
551 | do_retain_initrd = 1; |
552 | return 1; |
553 | } |
554 | __setup("retain_initrd" , retain_initrd_param); |
555 | |
556 | #ifdef CONFIG_ARCH_HAS_KEEPINITRD |
557 | static int __init keepinitrd_setup(char *__unused) |
558 | { |
559 | do_retain_initrd = 1; |
560 | return 1; |
561 | } |
562 | __setup("keepinitrd" , keepinitrd_setup); |
563 | #endif |
564 | |
565 | static bool __initdata initramfs_async = true; |
566 | static int __init initramfs_async_setup(char *str) |
567 | { |
568 | return kstrtobool(s: str, res: &initramfs_async) == 0; |
569 | } |
570 | __setup("initramfs_async=" , initramfs_async_setup); |
571 | |
572 | extern char __initramfs_start[]; |
573 | extern unsigned long __initramfs_size; |
574 | #include <linux/initrd.h> |
575 | #include <linux/kexec.h> |
576 | |
577 | void __init reserve_initrd_mem(void) |
578 | { |
579 | phys_addr_t start; |
580 | unsigned long size; |
581 | |
582 | /* Ignore the virtul address computed during device tree parsing */ |
583 | initrd_start = initrd_end = 0; |
584 | |
585 | if (!phys_initrd_size) |
586 | return; |
587 | /* |
588 | * Round the memory region to page boundaries as per free_initrd_mem() |
589 | * This allows us to detect whether the pages overlapping the initrd |
590 | * are in use, but more importantly, reserves the entire set of pages |
591 | * as we don't want these pages allocated for other purposes. |
592 | */ |
593 | start = round_down(phys_initrd_start, PAGE_SIZE); |
594 | size = phys_initrd_size + (phys_initrd_start - start); |
595 | size = round_up(size, PAGE_SIZE); |
596 | |
597 | if (!memblock_is_region_memory(base: start, size)) { |
598 | pr_err("INITRD: 0x%08llx+0x%08lx is not a memory region" , |
599 | (u64)start, size); |
600 | goto disable; |
601 | } |
602 | |
603 | if (memblock_is_region_reserved(base: start, size)) { |
604 | pr_err("INITRD: 0x%08llx+0x%08lx overlaps in-use memory region\n" , |
605 | (u64)start, size); |
606 | goto disable; |
607 | } |
608 | |
609 | memblock_reserve(base: start, size); |
610 | /* Now convert initrd to virtual addresses */ |
611 | initrd_start = (unsigned long)__va(phys_initrd_start); |
612 | initrd_end = initrd_start + phys_initrd_size; |
613 | initrd_below_start_ok = 1; |
614 | |
615 | return; |
616 | disable: |
617 | pr_cont(" - disabling initrd\n" ); |
618 | initrd_start = 0; |
619 | initrd_end = 0; |
620 | } |
621 | |
622 | void __weak __init free_initrd_mem(unsigned long start, unsigned long end) |
623 | { |
624 | #ifdef CONFIG_ARCH_KEEP_MEMBLOCK |
625 | unsigned long aligned_start = ALIGN_DOWN(start, PAGE_SIZE); |
626 | unsigned long aligned_end = ALIGN(end, PAGE_SIZE); |
627 | |
628 | memblock_free((void *)aligned_start, aligned_end - aligned_start); |
629 | #endif |
630 | |
631 | free_reserved_area(start: (void *)start, end: (void *)end, POISON_FREE_INITMEM, |
632 | s: "initrd" ); |
633 | } |
634 | |
635 | #ifdef CONFIG_KEXEC_CORE |
636 | static bool __init kexec_free_initrd(void) |
637 | { |
638 | unsigned long crashk_start = (unsigned long)__va(crashk_res.start); |
639 | unsigned long crashk_end = (unsigned long)__va(crashk_res.end); |
640 | |
641 | /* |
642 | * If the initrd region is overlapped with crashkernel reserved region, |
643 | * free only memory that is not part of crashkernel region. |
644 | */ |
645 | if (initrd_start >= crashk_end || initrd_end <= crashk_start) |
646 | return false; |
647 | |
648 | /* |
649 | * Initialize initrd memory region since the kexec boot does not do. |
650 | */ |
651 | memset((void *)initrd_start, 0, initrd_end - initrd_start); |
652 | if (initrd_start < crashk_start) |
653 | free_initrd_mem(start: initrd_start, end: crashk_start); |
654 | if (initrd_end > crashk_end) |
655 | free_initrd_mem(start: crashk_end, end: initrd_end); |
656 | return true; |
657 | } |
658 | #else |
659 | static inline bool kexec_free_initrd(void) |
660 | { |
661 | return false; |
662 | } |
663 | #endif /* CONFIG_KEXEC_CORE */ |
664 | |
665 | #ifdef CONFIG_BLK_DEV_RAM |
666 | static void __init populate_initrd_image(char *err) |
667 | { |
668 | ssize_t written; |
669 | struct file *file; |
670 | loff_t pos = 0; |
671 | |
672 | unpack_to_rootfs(buf: __initramfs_start, len: __initramfs_size); |
673 | |
674 | printk(KERN_INFO "rootfs image is not initramfs (%s); looks like an initrd\n" , |
675 | err); |
676 | file = filp_open("/initrd.image" , O_WRONLY | O_CREAT, 0700); |
677 | if (IS_ERR(ptr: file)) |
678 | return; |
679 | |
680 | written = xwrite(file, p: (char *)initrd_start, count: initrd_end - initrd_start, |
681 | pos: &pos); |
682 | if (written != initrd_end - initrd_start) |
683 | pr_err("/initrd.image: incomplete write (%zd != %ld)\n" , |
684 | written, initrd_end - initrd_start); |
685 | fput(file); |
686 | } |
687 | #endif /* CONFIG_BLK_DEV_RAM */ |
688 | |
689 | static void __init do_populate_rootfs(void *unused, async_cookie_t cookie) |
690 | { |
691 | /* Load the built in initramfs */ |
692 | char *err = unpack_to_rootfs(buf: __initramfs_start, len: __initramfs_size); |
693 | if (err) |
694 | panic_show_mem("%s" , err); /* Failed to decompress INTERNAL initramfs */ |
695 | |
696 | if (!initrd_start || IS_ENABLED(CONFIG_INITRAMFS_FORCE)) |
697 | goto done; |
698 | |
699 | if (IS_ENABLED(CONFIG_BLK_DEV_RAM)) |
700 | printk(KERN_INFO "Trying to unpack rootfs image as initramfs...\n" ); |
701 | else |
702 | printk(KERN_INFO "Unpacking initramfs...\n" ); |
703 | |
704 | err = unpack_to_rootfs(buf: (char *)initrd_start, len: initrd_end - initrd_start); |
705 | if (err) { |
706 | #ifdef CONFIG_BLK_DEV_RAM |
707 | populate_initrd_image(err); |
708 | #else |
709 | printk(KERN_EMERG "Initramfs unpacking failed: %s\n" , err); |
710 | #endif |
711 | } |
712 | |
713 | done: |
714 | /* |
715 | * If the initrd region is overlapped with crashkernel reserved region, |
716 | * free only memory that is not part of crashkernel region. |
717 | */ |
718 | if (!do_retain_initrd && initrd_start && !kexec_free_initrd()) |
719 | free_initrd_mem(start: initrd_start, end: initrd_end); |
720 | initrd_start = 0; |
721 | initrd_end = 0; |
722 | |
723 | flush_delayed_fput(); |
724 | task_work_run(); |
725 | } |
726 | |
727 | static ASYNC_DOMAIN_EXCLUSIVE(initramfs_domain); |
728 | static async_cookie_t initramfs_cookie; |
729 | |
730 | void wait_for_initramfs(void) |
731 | { |
732 | if (!initramfs_cookie) { |
733 | /* |
734 | * Something before rootfs_initcall wants to access |
735 | * the filesystem/initramfs. Probably a bug. Make a |
736 | * note, avoid deadlocking the machine, and let the |
737 | * caller's access fail as it used to. |
738 | */ |
739 | pr_warn_once("wait_for_initramfs() called before rootfs_initcalls\n" ); |
740 | return; |
741 | } |
742 | async_synchronize_cookie_domain(cookie: initramfs_cookie + 1, domain: &initramfs_domain); |
743 | } |
744 | EXPORT_SYMBOL_GPL(wait_for_initramfs); |
745 | |
746 | static int __init populate_rootfs(void) |
747 | { |
748 | initramfs_cookie = async_schedule_domain(func: do_populate_rootfs, NULL, |
749 | domain: &initramfs_domain); |
750 | usermodehelper_enable(); |
751 | if (!initramfs_async) |
752 | wait_for_initramfs(); |
753 | return 0; |
754 | } |
755 | rootfs_initcall(populate_rootfs); |
756 | |