fdt.c 10.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
/*
 * FDT related Helper functions used by the EFI stub on multiple
 * architectures. This should be #included by the EFI stub
 * implementation files.
 *
 * Copyright 2013 Linaro Limited; author Roy Franz
 *
 * This file is part of the Linux kernel, and is made available
 * under the terms of the GNU General Public License version 2.
 *
 */

13 14 15 16
#include <linux/efi.h>
#include <linux/libfdt.h>
#include <asm/efi.h>

17 18
#include "efistub.h"

19 20 21 22
static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
			       unsigned long orig_fdt_size,
			       void *fdt, int new_fdt_size, char *cmdline_ptr,
			       u64 initrd_addr, u64 initrd_size)
23
{
24
	int node, num_rsv;
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
	int status;
	u32 fdt_val32;
	u64 fdt_val64;

	/* Do some checks on provided FDT, if it exists*/
	if (orig_fdt) {
		if (fdt_check_header(orig_fdt)) {
			pr_efi_err(sys_table, "Device Tree header not valid!\n");
			return EFI_LOAD_ERROR;
		}
		/*
		 * We don't get the size of the FDT if we get if from a
		 * configuration table.
		 */
		if (orig_fdt_size && fdt_totalsize(orig_fdt) > orig_fdt_size) {
			pr_efi_err(sys_table, "Truncated device tree! foo!\n");
			return EFI_LOAD_ERROR;
		}
	}

	if (orig_fdt)
		status = fdt_open_into(orig_fdt, fdt, new_fdt_size);
	else
		status = fdt_create_empty_tree(fdt, new_fdt_size);

	if (status != 0)
		goto fdt_set_fail;

53 54 55 56 57 58 59 60
	/*
	 * Delete all memory reserve map entries. When booting via UEFI,
	 * kernel will use the UEFI memory map to find reserved regions.
	 */
	num_rsv = fdt_num_mem_rsv(fdt);
	while (num_rsv-- > 0)
		fdt_del_mem_rsv(fdt, num_rsv);

61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
	node = fdt_subnode_offset(fdt, 0, "chosen");
	if (node < 0) {
		node = fdt_add_subnode(fdt, 0, "chosen");
		if (node < 0) {
			status = node; /* node is error code when negative */
			goto fdt_set_fail;
		}
	}

	if ((cmdline_ptr != NULL) && (strlen(cmdline_ptr) > 0)) {
		status = fdt_setprop(fdt, node, "bootargs", cmdline_ptr,
				     strlen(cmdline_ptr) + 1);
		if (status)
			goto fdt_set_fail;
	}

	/* Set initrd address/end in device tree, if present */
	if (initrd_size != 0) {
		u64 initrd_image_end;
		u64 initrd_image_start = cpu_to_fdt64(initrd_addr);

		status = fdt_setprop(fdt, node, "linux,initrd-start",
				     &initrd_image_start, sizeof(u64));
		if (status)
			goto fdt_set_fail;
		initrd_image_end = cpu_to_fdt64(initrd_addr + initrd_size);
		status = fdt_setprop(fdt, node, "linux,initrd-end",
				     &initrd_image_end, sizeof(u64));
		if (status)
			goto fdt_set_fail;
	}

	/* Add FDT entries for EFI runtime services in chosen node. */
	node = fdt_subnode_offset(fdt, 0, "chosen");
	fdt_val64 = cpu_to_fdt64((u64)(unsigned long)sys_table);
	status = fdt_setprop(fdt, node, "linux,uefi-system-table",
			     &fdt_val64, sizeof(fdt_val64));
	if (status)
		goto fdt_set_fail;

101
	fdt_val64 = U64_MAX; /* placeholder */
102 103 104 105 106
	status = fdt_setprop(fdt, node, "linux,uefi-mmap-start",
			     &fdt_val64,  sizeof(fdt_val64));
	if (status)
		goto fdt_set_fail;

107
	fdt_val32 = U32_MAX; /* placeholder */
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
	status = fdt_setprop(fdt, node, "linux,uefi-mmap-size",
			     &fdt_val32,  sizeof(fdt_val32));
	if (status)
		goto fdt_set_fail;

	status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-size",
			     &fdt_val32, sizeof(fdt_val32));
	if (status)
		goto fdt_set_fail;

	status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-ver",
			     &fdt_val32, sizeof(fdt_val32));
	if (status)
		goto fdt_set_fail;

123 124 125 126 127 128 129 130 131 132 133 134 135 136
	if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
		efi_status_t efi_status;

		efi_status = efi_get_random_bytes(sys_table, sizeof(fdt_val64),
						  (u8 *)&fdt_val64);
		if (efi_status == EFI_SUCCESS) {
			status = fdt_setprop(fdt, node, "kaslr-seed",
					     &fdt_val64, sizeof(fdt_val64));
			if (status)
				goto fdt_set_fail;
		} else if (efi_status != EFI_NOT_FOUND) {
			return efi_status;
		}
	}
137 138 139 140 141 142 143 144 145
	return EFI_SUCCESS;

fdt_set_fail:
	if (status == -FDT_ERR_NOSPACE)
		return EFI_BUFFER_TOO_SMALL;

	return EFI_LOAD_ERROR;
}

146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
static efi_status_t update_fdt_memmap(void *fdt, struct efi_boot_memmap *map)
{
	int node = fdt_path_offset(fdt, "/chosen");
	u64 fdt_val64;
	u32 fdt_val32;
	int err;

	if (node < 0)
		return EFI_LOAD_ERROR;

	fdt_val64 = cpu_to_fdt64((unsigned long)*map->map);
	err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-start",
				  &fdt_val64, sizeof(fdt_val64));
	if (err)
		return EFI_LOAD_ERROR;

	fdt_val32 = cpu_to_fdt32(*map->map_size);
	err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-size",
				  &fdt_val32, sizeof(fdt_val32));
	if (err)
		return EFI_LOAD_ERROR;

	fdt_val32 = cpu_to_fdt32(*map->desc_size);
	err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-desc-size",
				  &fdt_val32, sizeof(fdt_val32));
	if (err)
		return EFI_LOAD_ERROR;

	fdt_val32 = cpu_to_fdt32(*map->desc_ver);
	err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-desc-ver",
				  &fdt_val32, sizeof(fdt_val32));
	if (err)
		return EFI_LOAD_ERROR;

	return EFI_SUCCESS;
}

183 184 185 186
#ifndef EFI_FDT_ALIGN
#define EFI_FDT_ALIGN EFI_PAGE_SIZE
#endif

187 188 189
struct exit_boot_struct {
	efi_memory_desc_t *runtime_map;
	int *runtime_entry_count;
190
	void *new_fdt_addr;
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
};

static efi_status_t exit_boot_func(efi_system_table_t *sys_table_arg,
				   struct efi_boot_memmap *map,
				   void *priv)
{
	struct exit_boot_struct *p = priv;
	/*
	 * Update the memory map with virtual addresses. The function will also
	 * populate @runtime_map with copies of just the EFI_MEMORY_RUNTIME
	 * entries so that we can pass it straight to SetVirtualAddressMap()
	 */
	efi_get_virtmap(*map->map, *map->map_size, *map->desc_size,
			p->runtime_map, p->runtime_entry_count);

206
	return update_fdt_memmap(p->new_fdt_addr, map);
207 208
}

209 210 211 212
#ifndef MAX_FDT_SIZE
#define MAX_FDT_SIZE	SZ_2M
#endif

213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
/*
 * Allocate memory for a new FDT, then add EFI, commandline, and
 * initrd related fields to the FDT.  This routine increases the
 * FDT allocation size until the allocated memory is large
 * enough.  EFI allocations are in EFI_PAGE_SIZE granules,
 * which are fixed at 4K bytes, so in most cases the first
 * allocation should succeed.
 * EFI boot services are exited at the end of this function.
 * There must be no allocations between the get_memory_map()
 * call and the exit_boot_services() call, so the exiting of
 * boot services is very tightly tied to the creation of the FDT
 * with the final memory map in it.
 */

efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
					    void *handle,
					    unsigned long *new_fdt_addr,
					    unsigned long max_addr,
					    u64 initrd_addr, u64 initrd_size,
					    char *cmdline_ptr,
					    unsigned long fdt_addr,
					    unsigned long fdt_size)
{
236
	unsigned long map_size, desc_size, buff_size;
237 238
	u32 desc_ver;
	unsigned long mmap_key;
239
	efi_memory_desc_t *memory_map, *runtime_map;
240
	efi_status_t status;
241
	int runtime_entry_count = 0;
242
	struct efi_boot_memmap map;
243
	struct exit_boot_struct priv;
244 245 246 247 248 249 250

	map.map =	&runtime_map;
	map.map_size =	&map_size;
	map.desc_size =	&desc_size;
	map.desc_ver =	&desc_ver;
	map.key_ptr =	&mmap_key;
	map.buff_size =	&buff_size;
251 252 253 254 255 256 257

	/*
	 * Get a copy of the current memory map that we will use to prepare
	 * the input for SetVirtualAddressMap(). We don't have to worry about
	 * subsequent allocations adding entries, since they could not affect
	 * the number of EFI_MEMORY_RUNTIME regions.
	 */
258
	status = efi_get_memory_map(sys_table, &map);
259 260 261 262 263 264 265
	if (status != EFI_SUCCESS) {
		pr_efi_err(sys_table, "Unable to retrieve UEFI memory map.\n");
		return status;
	}

	pr_efi(sys_table,
	       "Exiting boot services and installing virtual address map...\n");
266

267
	map.map = &memory_map;
268 269 270 271 272 273 274 275
	status = efi_high_alloc(sys_table, MAX_FDT_SIZE, EFI_FDT_ALIGN,
				new_fdt_addr, max_addr);
	if (status != EFI_SUCCESS) {
		pr_efi_err(sys_table,
			   "Unable to allocate memory for new device tree.\n");
		goto fail;
	}

276
	/*
277 278
	 * Now that we have done our final memory allocation (and free)
	 * we can get the memory map key needed for exit_boot_services().
279
	 */
280 281 282
	status = efi_get_memory_map(sys_table, &map);
	if (status != EFI_SUCCESS)
		goto fail_free_new_fdt;
283

284 285 286
	status = update_fdt(sys_table, (void *)fdt_addr, fdt_size,
			    (void *)*new_fdt_addr, MAX_FDT_SIZE, cmdline_ptr,
			    initrd_addr, initrd_size);
287

288 289 290
	if (status != EFI_SUCCESS) {
		pr_efi_err(sys_table, "Unable to construct new device tree.\n");
		goto fail_free_new_fdt;
291 292
	}

293 294
	priv.runtime_map = runtime_map;
	priv.runtime_entry_count = &runtime_entry_count;
295
	priv.new_fdt_addr = (void *)*new_fdt_addr;
296 297
	status = efi_exit_boot_services(sys_table, handle, &map, &priv,
					exit_boot_func);
298

299 300
	if (status == EFI_SUCCESS) {
		efi_set_virtual_address_map_t *svam;
301

302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329
		/* Install the new virtual address map */
		svam = sys_table->runtime->set_virtual_address_map;
		status = svam(runtime_entry_count * desc_size, desc_size,
			      desc_ver, runtime_map);

		/*
		 * We are beyond the point of no return here, so if the call to
		 * SetVirtualAddressMap() failed, we need to signal that to the
		 * incoming kernel but proceed normally otherwise.
		 */
		if (status != EFI_SUCCESS) {
			int l;

			/*
			 * Set the virtual address field of all
			 * EFI_MEMORY_RUNTIME entries to 0. This will signal
			 * the incoming kernel that no virtual translation has
			 * been installed.
			 */
			for (l = 0; l < map_size; l += desc_size) {
				efi_memory_desc_t *p = (void *)memory_map + l;

				if (p->attribute & EFI_MEMORY_RUNTIME)
					p->virt_addr = 0;
			}
		}
		return EFI_SUCCESS;
	}
330 331 332 333

	pr_efi_err(sys_table, "Exit boot services failed.\n");

fail_free_new_fdt:
334
	efi_free(sys_table, MAX_FDT_SIZE, *new_fdt_addr);
335 336

fail:
337
	sys_table->boottime->free_pool(runtime_map);
338 339 340
	return EFI_LOAD_ERROR;
}

341
void *get_fdt(efi_system_table_t *sys_table, unsigned long *fdt_size)
342 343 344 345 346 347 348 349 350 351 352 353
{
	efi_guid_t fdt_guid = DEVICE_TREE_GUID;
	efi_config_table_t *tables;
	void *fdt;
	int i;

	tables = (efi_config_table_t *) sys_table->tables;
	fdt = NULL;

	for (i = 0; i < sys_table->nr_tables; i++)
		if (efi_guidcmp(tables[i].guid, fdt_guid) == 0) {
			fdt = (void *) tables[i].table;
354 355 356 357 358
			if (fdt_check_header(fdt) != 0) {
				pr_efi_err(sys_table, "Invalid header detected on UEFI supplied FDT, ignoring ...\n");
				return NULL;
			}
			*fdt_size = fdt_totalsize(fdt);
359 360 361 362 363
			break;
	 }

	return fdt;
}