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

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

37
static void __init efi_fake_range(struct efi_mem_range *efi_range)
T
Taku Izumi 已提交
38
{
39
	struct efi_memory_map_data data = { 0 };
40
	int new_nr_map = efi.memmap.nr_map;
T
Taku Izumi 已提交
41 42 43 44
	efi_memory_desc_t *md;
	void *new_memmap;

	/* count up the number of EFI memory descriptor */
45 46
	for_each_efi_memory_desc(md)
		new_nr_map += efi_memmap_split_count(md, &efi_range->range);
T
Taku Izumi 已提交
47 48

	/* allocate memory for new EFI memmap */
49
	if (efi_memmap_alloc(new_nr_map, &data) != 0)
T
Taku Izumi 已提交
50 51 52
		return;

	/* create new EFI memmap */
53
	new_memmap = early_memremap(data.phys_map, data.size);
T
Taku Izumi 已提交
54
	if (!new_memmap) {
55
		__efi_memmap_free(data.phys_map, data.size, data.flags);
T
Taku Izumi 已提交
56 57 58
		return;
	}

59
	efi_memmap_insert(&efi.memmap, new_memmap, efi_range);
T
Taku Izumi 已提交
60 61

	/* swap into new EFI memmap */
62
	early_memunmap(new_memmap, data.size);
63

64
	efi_memmap_install(&data);
65 66 67 68 69 70 71 72 73 74 75
}

void __init efi_fake_memmap(void)
{
	int i;

	if (!efi_enabled(EFI_MEMMAP) || !nr_fake_mem)
		return;

	for (i = 0; i < nr_fake_mem; i++)
		efi_fake_range(&efi_fake_mems[i]);
T
Taku Izumi 已提交
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 101 102 103

	/* 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;

104 105 106
		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 已提交
107 108 109 110 111 112
		nr_fake_mem++;

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

113
	sort(efi_fake_mems, nr_fake_mem, sizeof(struct efi_mem_range),
T
Taku Izumi 已提交
114 115 116 117
	     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]",
118 119
			efi_fake_mems[i].attribute, efi_fake_mems[i].range.start,
			efi_fake_mems[i].range.end);
T
Taku Izumi 已提交
120 121 122 123 124

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

early_param("efi_fake_mem", setup_fake_mem);