• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include <commonlib/bsd/compression.h>
4 #include <console/console.h>
5 #include <bootmem.h>
6 #include <program_loading.h>
7 #include <fit.h>
8 #include <endian.h>
9 
10 /* Implements a Berkeley Boot Loader (BBL) compatible payload loading */
11 
12 #define MAX_KERNEL_SIZE (64*MiB)
13 
14 #if CONFIG(ARCH_RISCV_RV32)
15 #define SECTION_ALIGN (4 * MiB)
16 #endif
17 #if CONFIG(ARCH_RISCV_RV64)
18 #define SECTION_ALIGN (2 * MiB)
19 #endif
20 
get_kernel_size(const struct fit_image_node * node)21 static size_t get_kernel_size(const struct fit_image_node *node)
22 {
23 	/*
24 	 * Since we don't have a way to determine the uncompressed size of the
25 	 * kernel, we have to keep as much memory as possible free for use by
26 	 * the kernel immediately after the end of the kernel image. The amount
27 	 * of space required will vary depending on selected features, and is
28 	 * effectively unbound.
29 	 */
30 
31 	printk(BIOS_INFO,
32 	       "FIT: Leaving additional %u MiB of free space after kernel.\n",
33 	       MAX_KERNEL_SIZE >> 20);
34 
35 	return node->size + MAX_KERNEL_SIZE;
36 }
37 
38 /**
39  * Place the region in free memory range.
40  *
41  * The caller has to set region->offset to the minimum allowed address.
42  */
fit_place_mem(const struct range_entry * r,void * arg)43 static bool fit_place_mem(const struct range_entry *r, void *arg)
44 {
45 	struct region *region = arg;
46 	resource_t start;
47 
48 	if (range_entry_tag(r) != BM_MEM_RAM)
49 		return true;
50 
51 	/* Section must be aligned at page boundary */
52 	start = ALIGN_UP(MAX(region->offset, range_entry_base(r)), SECTION_ALIGN);
53 
54 	if (start + region->size < range_entry_end(r)) {
55 		region->offset = (size_t)start;
56 		return false;
57 	}
58 
59 	return true;
60 }
61 
fit_payload_arch(struct prog * payload,struct fit_config_node * config,struct region * kernel,struct region * fdt,struct region * initrd)62 bool fit_payload_arch(struct prog *payload, struct fit_config_node *config,
63 		      struct region *kernel,
64 		      struct region *fdt,
65 		      struct region *initrd)
66 {
67 	void *arg = NULL;
68 
69 	if (!config->fdt || !fdt) {
70 		printk(BIOS_CRIT, "Providing a valid FDT is mandatory to "
71 		       "boot a RISC-V kernel!\n");
72 		return false;
73 		/* TODO: Fall back to the ROM FDT? */
74 	}
75 
76 	/* Update kernel size from image header, if possible */
77 	kernel->size = get_kernel_size(config->kernel);
78 	printk(BIOS_DEBUG, "FIT: Using kernel size of 0x%zx bytes\n",
79 	       kernel->size);
80 
81 	/*
82 	 * The code assumes that bootmem_walk provides a sorted list of memory
83 	 * regions, starting from the lowest address.
84 	 * The order of the calls here doesn't matter, as the placement is
85 	 * enforced in the called functions.
86 	 * For details check code on top.
87 	 */
88 	kernel->offset = 0;
89 	if (!bootmem_walk(fit_place_mem, kernel))
90 		return false;
91 
92 	/* Mark as reserved for future allocations. */
93 	bootmem_add_range(kernel->offset, kernel->size, BM_MEM_PAYLOAD);
94 
95 	/* Place FDT and INITRD after kernel. */
96 
97 	/* Place INITRD */
98 	if (config->ramdisk) {
99 		initrd->offset = kernel->offset + kernel->size;
100 
101 		if (!bootmem_walk(fit_place_mem, initrd))
102 			return false;
103 		/* Mark as reserved for future allocations. */
104 		bootmem_add_range(initrd->offset, initrd->size, BM_MEM_PAYLOAD);
105 	}
106 
107 	/* Place FDT */
108 	fdt->offset = kernel->offset + kernel->size;
109 
110 	if (!bootmem_walk(fit_place_mem, fdt))
111 		return false;
112 	/* Mark as reserved for future allocations. */
113 	bootmem_add_range(fdt->offset, fdt->size, BM_MEM_PAYLOAD);
114 
115 	/* Kernel expects FDT as argument */
116 	arg = (void *)fdt->offset;
117 
118 	prog_set_entry(payload, (void *)kernel->offset, arg);
119 
120 	bootmem_dump_ranges();
121 
122 	return true;
123 }
124