fake_mem.c 2.8 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
T
Taku Izumi 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
 * fake_mem.c
 *
 * Copyright (C) 2015 FUJITSU LIMITED
 * Author: Taku Izumi <izumi.taku@jp.fujitsu.com>
 *
 * This code introduces new boot option named "efi_fake_mem"
 * By specifying this parameter, you can add arbitrary attribute to
 * specific memory range by updating original (firmware provided) EFI
 * memmap.
 */

#include <linux/kernel.h>
#include <linux/efi.h>
#include <linux/init.h>
#include <linux/memblock.h>
#include <linux/types.h>
#include <linux/sort.h>
20
#include "fake_mem.h"
T
Taku Izumi 已提交
21

22 23
struct efi_mem_range efi_fake_mems[EFI_MAX_FAKEMEM];
int nr_fake_mem;
T
Taku Izumi 已提交
24 25 26

static int __init cmp_fake_mem(const void *x1, const void *x2)
{
27 28
	const struct efi_mem_range *m1 = x1;
	const struct efi_mem_range *m2 = x2;
T
Taku Izumi 已提交
29 30 31 32 33 34 35 36 37 38

	if (m1->range.start < m2->range.start)
		return -1;
	if (m1->range.start > m2->range.start)
		return 1;
	return 0;
}

void __init efi_fake_memmap(void)
{
39
	int new_nr_map = efi.memmap.nr_map;
T
Taku Izumi 已提交
40
	efi_memory_desc_t *md;
41
	phys_addr_t new_memmap_phy;
T
Taku Izumi 已提交
42 43 44
	void *new_memmap;
	int i;

45
	if (!efi_enabled(EFI_MEMMAP) || !nr_fake_mem)
T
Taku Izumi 已提交
46 47 48
		return;

	/* count up the number of EFI memory descriptor */
49 50
	for (i = 0; i < nr_fake_mem; i++) {
		for_each_efi_memory_desc(md) {
51
			struct range *r = &efi_fake_mems[i].range;
52

53
			new_nr_map += efi_memmap_split_count(md, r);
T
Taku Izumi 已提交
54 55 56 57
		}
	}

	/* allocate memory for new EFI memmap */
58
	new_memmap_phy = efi_memmap_alloc(new_nr_map);
T
Taku Izumi 已提交
59 60 61 62 63
	if (!new_memmap_phy)
		return;

	/* create new EFI memmap */
	new_memmap = early_memremap(new_memmap_phy,
64
				    efi.memmap.desc_size * new_nr_map);
T
Taku Izumi 已提交
65
	if (!new_memmap) {
66
		memblock_free(new_memmap_phy, efi.memmap.desc_size * new_nr_map);
T
Taku Izumi 已提交
67 68 69
		return;
	}

70
	for (i = 0; i < nr_fake_mem; i++)
71
		efi_memmap_insert(&efi.memmap, new_memmap, &efi_fake_mems[i]);
T
Taku Izumi 已提交
72 73

	/* swap into new EFI memmap */
74 75
	early_memunmap(new_memmap, efi.memmap.desc_size * new_nr_map);

76
	efi_memmap_install(new_memmap_phy, new_nr_map);
T
Taku Izumi 已提交
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104

	/* print new EFI memmap */
	efi_print_memmap();
}

static int __init setup_fake_mem(char *p)
{
	u64 start = 0, mem_size = 0, attribute = 0;
	int i;

	if (!p)
		return -EINVAL;

	while (*p != '\0') {
		mem_size = memparse(p, &p);
		if (*p == '@')
			start = memparse(p+1, &p);
		else
			break;

		if (*p == ':')
			attribute = simple_strtoull(p+1, &p, 0);
		else
			break;

		if (nr_fake_mem >= EFI_MAX_FAKEMEM)
			break;

105 106 107
		efi_fake_mems[nr_fake_mem].range.start = start;
		efi_fake_mems[nr_fake_mem].range.end = start + mem_size - 1;
		efi_fake_mems[nr_fake_mem].attribute = attribute;
T
Taku Izumi 已提交
108 109 110 111 112 113
		nr_fake_mem++;

		if (*p == ',')
			p++;
	}

114
	sort(efi_fake_mems, nr_fake_mem, sizeof(struct efi_mem_range),
T
Taku Izumi 已提交
115 116 117 118
	     cmp_fake_mem, NULL);

	for (i = 0; i < nr_fake_mem; i++)
		pr_info("efi_fake_mem: add attr=0x%016llx to [mem 0x%016llx-0x%016llx]",
119 120
			efi_fake_mems[i].attribute, efi_fake_mems[i].range.start,
			efi_fake_mems[i].range.end);
T
Taku Izumi 已提交
121 122 123 124 125

	return *p == '\0' ? 0 : -EINVAL;
}

early_param("efi_fake_mem", setup_fake_mem);