/ src / lib / selfboot.c
selfboot.c
  1  /* SPDX-License-Identifier: GPL-2.0-only */
  2  
  3  #include <commonlib/bsd/compression.h>
  4  #include <commonlib/endian.h>
  5  #include <console/console.h>
  6  #include <string.h>
  7  #include <symbols.h>
  8  #include <cbfs.h>
  9  #include <lib.h>
 10  #include <bootmem.h>
 11  #include <program_loading.h>
 12  #include <timestamp.h>
 13  #include <cbmem.h>
 14  #include <types.h>
 15  
 16  /* The type syntax for C is essentially unparsable. -- Rob Pike */
 17  typedef int (*checker_t)(struct cbfs_payload_segment *cbfssegs, void *args);
 18  
 19  /* Decode a serialized cbfs payload segment
 20   * from memory into native endianness.
 21   */
 22  static void cbfs_decode_payload_segment(struct cbfs_payload_segment *segment,
 23  		const struct cbfs_payload_segment *src)
 24  {
 25  	segment->type        = read_be32(&src->type);
 26  	segment->compression = read_be32(&src->compression);
 27  	segment->offset      = read_be32(&src->offset);
 28  	segment->load_addr   = read_be64(&src->load_addr);
 29  	segment->len         = read_be32(&src->len);
 30  	segment->mem_len     = read_be32(&src->mem_len);
 31  }
 32  
 33  static int segment_targets_type(void *dest, unsigned long memsz,
 34  		enum bootmem_type dest_type)
 35  {
 36  	/* No bootmem to check in earlier stages, caller should not use
 37  	   selfload_check(). */
 38  	if (!ENV_RAMSTAGE) {
 39  		printk(BIOS_ERR,
 40  		       "Callers not supposed to call selfload_check() in romstage");
 41  		return 0;
 42  	}
 43  
 44  	uintptr_t d = (uintptr_t) dest;
 45  	if (bootmem_region_targets_type(d, memsz, dest_type))
 46  		return 1;
 47  
 48  	if (payload_arch_usable_ram_quirk(d, memsz))
 49  		return 1;
 50  
 51  	printk(BIOS_ERR, "SELF segment doesn't target RAM: %p, %lu bytes\n", dest, memsz);
 52  	bootmem_dump_ranges();
 53  	return 0;
 54  }
 55  
 56  static int load_one_segment(uint8_t *dest,
 57  			    uint8_t *src,
 58  			    size_t len,
 59  			    size_t memsz,
 60  			    uint32_t compression,
 61  			    int flags)
 62  {
 63  	unsigned char *middle, *end;
 64  	printk(BIOS_DEBUG, "Loading Segment: addr: %p memsz: 0x%016zx filesz: 0x%016zx\n",
 65  	       dest, memsz, len);
 66  
 67  	/* Compute the boundaries of the segment */
 68  	end = dest + memsz;
 69  
 70  	/* Copy data from the initial buffer */
 71  	switch (compression) {
 72  	case CBFS_COMPRESS_LZMA: {
 73  		printk(BIOS_DEBUG, "using LZMA\n");
 74  		timestamp_add_now(TS_ULZMA_START);
 75  		len = ulzman(src, len, dest, memsz);
 76  		timestamp_add_now(TS_ULZMA_END);
 77  		if (!len) /* Decompression Error. */
 78  			return 0;
 79  		break;
 80  	}
 81  	case CBFS_COMPRESS_LZ4: {
 82  		printk(BIOS_DEBUG, "using LZ4\n");
 83  		timestamp_add_now(TS_ULZ4F_START);
 84  		len = ulz4fn(src, len, dest, memsz);
 85  		timestamp_add_now(TS_ULZ4F_END);
 86  		if (!len) /* Decompression Error. */
 87  			return 0;
 88  		break;
 89  	}
 90  	case CBFS_COMPRESS_NONE: {
 91  		printk(BIOS_DEBUG, "it's not compressed!\n");
 92  		memcpy(dest, src, len);
 93  		break;
 94  	}
 95  	default:
 96  		printk(BIOS_INFO, "CBFS:  Unknown compression type %d\n", compression);
 97  		return 0;
 98  	}
 99  	/* Calculate middle after any changes to len. */
100  	middle = dest + len;
101  	printk(BIOS_SPEW, "[ 0x%08lx, %08lx, 0x%08lx) <- %08lx\n",
102  		(unsigned long)dest,
103  		(unsigned long)middle,
104  		(unsigned long)end,
105  		(unsigned long)src);
106  
107  	/* Zero the extra bytes between middle & end */
108  	if (middle < end) {
109  		printk(BIOS_DEBUG,
110  			"Clearing Segment: addr: 0x%016lx memsz: 0x%016lx\n",
111  			(unsigned long)middle,
112  			(unsigned long)(end - middle));
113  
114  		/* Zero the extra bytes */
115  		memset(middle, 0, end - middle);
116  	}
117  
118  	/*
119  	 * Each architecture can perform additional operations
120  	 * on the loaded segment
121  	 */
122  	prog_segment_loaded((uintptr_t)dest, memsz, flags);
123  
124  	return 1;
125  }
126  
127  /* Note: this function is a bit dangerous so is not exported.
128   * It assumes you're smart enough not to call it with the very
129   * last segment, since it uses seg + 1 */
130  static int last_loadable_segment(struct cbfs_payload_segment *seg)
131  {
132  	return read_be32(&(seg + 1)->type) == PAYLOAD_SEGMENT_ENTRY;
133  }
134  
135  static int check_payload_segments(struct cbfs_payload_segment *cbfssegs,
136  				  enum bootmem_type dest_type)
137  {
138  	uint8_t *dest;
139  	size_t memsz;
140  	struct cbfs_payload_segment *seg, segment;
141  
142  	/* dest_type == INVALID means we're not supposed to check anything. */
143  	if (dest_type == BM_MEM_INVALID)
144  		return 0;
145  
146  	for (seg = cbfssegs;; ++seg) {
147  		printk(BIOS_DEBUG, "Checking segment from ROM address %p\n", seg);
148  		cbfs_decode_payload_segment(&segment, seg);
149  		dest = (uint8_t *)(uintptr_t)segment.load_addr;
150  		memsz = segment.mem_len;
151  		if (segment.type == PAYLOAD_SEGMENT_ENTRY)
152  			break;
153  		if (!segment_targets_type(dest, memsz, dest_type))
154  			return -1;
155  	}
156  	return 0;
157  }
158  
159  static int load_payload_segments(struct cbfs_payload_segment *cbfssegs, uintptr_t *entry)
160  {
161  	uint8_t *dest, *src;
162  	size_t filesz, memsz;
163  	uint32_t compression;
164  	struct cbfs_payload_segment *first_segment, *seg, segment;
165  	int flags = 0;
166  
167  	for (first_segment = seg = cbfssegs;; ++seg) {
168  		printk(BIOS_DEBUG, "Loading segment from ROM address %p\n", seg);
169  
170  		cbfs_decode_payload_segment(&segment, seg);
171  		dest = (uint8_t *)(uintptr_t)segment.load_addr;
172  		memsz = segment.mem_len;
173  		compression = segment.compression;
174  		filesz = segment.len;
175  
176  		switch (segment.type) {
177  		case PAYLOAD_SEGMENT_CODE:
178  		case PAYLOAD_SEGMENT_DATA:
179  			printk(BIOS_DEBUG, "  %s (compression=%x)\n",
180  				segment.type == PAYLOAD_SEGMENT_CODE
181  				?  "code" : "data", segment.compression);
182  			src = ((uint8_t *)first_segment) + segment.offset;
183  			printk(BIOS_DEBUG,
184  				"  New segment dstaddr %p memsize 0x%zx srcaddr %p filesize 0x%zx\n",
185  			       dest, memsz, src, filesz);
186  
187  			/* Clean up the values */
188  			if (filesz > memsz)  {
189  				filesz = memsz;
190  				printk(BIOS_DEBUG, "  cleaned up filesize 0x%zx\n", filesz);
191  			}
192  			break;
193  
194  		case PAYLOAD_SEGMENT_BSS:
195  			printk(BIOS_DEBUG, "  BSS %p (%d byte)\n", (void *)
196  				(intptr_t)segment.load_addr, segment.mem_len);
197  			filesz = 0;
198  			src = ((uint8_t *)first_segment) + segment.offset;
199  			compression = CBFS_COMPRESS_NONE;
200  			break;
201  
202  		case PAYLOAD_SEGMENT_ENTRY:
203  			printk(BIOS_DEBUG, "  Entry Point %p\n", (void *)
204  				(intptr_t)segment.load_addr);
205  
206  			*entry = segment.load_addr;
207  			/* Per definition, a payload always has the entry point
208  			 * as last segment. Thus, we use the occurrence of the
209  			 * entry point as break condition for the loop.
210  			 */
211  			return 0;
212  
213  		default:
214  			/* We found something that we don't know about. Throw
215  			 * hands into the sky and run away!
216  			 */
217  			printk(BIOS_EMERG, "Bad segment type %x\n", segment.type);
218  			return -1;
219  		}
220  		/* Note that the 'seg + 1' is safe as we only call this
221  		 * function on "not the last" * items, since entry
222  		 * is always last. */
223  		if (last_loadable_segment(seg))
224  			flags = SEG_FINAL;
225  		if (!load_one_segment(dest, src, filesz, memsz, compression, flags))
226  			return -1;
227  	}
228  
229  	return 1;
230  }
231  
232  __weak int payload_arch_usable_ram_quirk(uint64_t start, uint64_t size)
233  {
234  	return 0;
235  }
236  
237  bool selfload_mapped(struct prog *payload, void *mapping,
238  		     enum bootmem_type dest_type)
239  {
240  	uintptr_t entry = 0;
241  	struct cbfs_payload_segment *cbfssegs;
242  
243  	cbfssegs = &((struct cbfs_payload *)mapping)->segments;
244  
245  	if (check_payload_segments(cbfssegs, dest_type))
246  		return false;
247  
248  	if (load_payload_segments(cbfssegs, &entry))
249  		return false;
250  
251  	printk(BIOS_SPEW, "Loaded segments\n");
252  
253  	/* Pass cbtables to payload if architecture desires it. */
254  	prog_set_entry(payload, (void *)entry, cbmem_find(CBMEM_ID_CBTABLE));
255  
256  	return true;
257  }
258  
259  bool selfload_check(struct prog *payload, enum bootmem_type dest_type)
260  {
261  	if (prog_locate_hook(payload))
262  		return false;
263  
264  	payload->cbfs_type = CBFS_TYPE_SELF;
265  	void *mapping = cbfs_type_map(prog_name(payload), NULL, &payload->cbfs_type);
266  	if (!mapping)
267  		return false;
268  
269  	bool ret = selfload_mapped(payload, mapping, dest_type);
270  
271  	cbfs_unmap(mapping);
272  	return ret;
273  }
274  
275  bool selfload(struct prog *payload)
276  {
277  	return selfload_check(payload, BM_MEM_INVALID);
278  }