Commit | Line | Data |
---|---|---|
0f96a99d TI |
1 | /* |
2 | * fake_mem.c | |
3 | * | |
4 | * Copyright (C) 2015 FUJITSU LIMITED | |
5 | * Author: Taku Izumi <izumi.taku@jp.fujitsu.com> | |
6 | * | |
7 | * This code introduces new boot option named "efi_fake_mem" | |
8 | * By specifying this parameter, you can add arbitrary attribute to | |
9 | * specific memory range by updating original (firmware provided) EFI | |
10 | * memmap. | |
11 | * | |
12 | * This program is free software; you can redistribute it and/or modify it | |
13 | * under the terms and conditions of the GNU General Public License, | |
14 | * version 2, as published by the Free Software Foundation. | |
15 | * | |
16 | * This program is distributed in the hope it will be useful, but WITHOUT | |
17 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
18 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
19 | * more details. | |
20 | * | |
21 | * You should have received a copy of the GNU General Public License along with | |
22 | * this program; if not, see <http://www.gnu.org/licenses/>. | |
23 | * | |
24 | * The full GNU General Public License is included in this distribution in | |
25 | * the file called "COPYING". | |
26 | */ | |
27 | ||
28 | #include <linux/kernel.h> | |
29 | #include <linux/efi.h> | |
30 | #include <linux/init.h> | |
31 | #include <linux/memblock.h> | |
32 | #include <linux/types.h> | |
33 | #include <linux/sort.h> | |
34 | #include <asm/efi.h> | |
35 | ||
36 | #define EFI_MAX_FAKEMEM CONFIG_EFI_MAX_FAKE_MEM | |
37 | ||
60863c0d | 38 | static struct efi_mem_range fake_mems[EFI_MAX_FAKEMEM]; |
0f96a99d TI |
39 | static int nr_fake_mem; |
40 | ||
41 | static int __init cmp_fake_mem(const void *x1, const void *x2) | |
42 | { | |
60863c0d MF |
43 | const struct efi_mem_range *m1 = x1; |
44 | const struct efi_mem_range *m2 = x2; | |
0f96a99d TI |
45 | |
46 | if (m1->range.start < m2->range.start) | |
47 | return -1; | |
48 | if (m1->range.start > m2->range.start) | |
49 | return 1; | |
50 | return 0; | |
51 | } | |
52 | ||
53 | void __init efi_fake_memmap(void) | |
54 | { | |
884f4f66 | 55 | int new_nr_map = efi.memmap.nr_map; |
0f96a99d | 56 | efi_memory_desc_t *md; |
78b9bc94 | 57 | phys_addr_t new_memmap_phy; |
0f96a99d | 58 | void *new_memmap; |
0f96a99d TI |
59 | int i; |
60 | ||
4971531a | 61 | if (!nr_fake_mem) |
0f96a99d TI |
62 | return; |
63 | ||
64 | /* count up the number of EFI memory descriptor */ | |
c8c1a4c5 MF |
65 | for (i = 0; i < nr_fake_mem; i++) { |
66 | for_each_efi_memory_desc(md) { | |
67 | struct range *r = &fake_mems[i].range; | |
68 | ||
60863c0d | 69 | new_nr_map += efi_memmap_split_count(md, r); |
0f96a99d TI |
70 | } |
71 | } | |
72 | ||
73 | /* allocate memory for new EFI memmap */ | |
884f4f66 | 74 | new_memmap_phy = memblock_alloc(efi.memmap.desc_size * new_nr_map, |
0f96a99d TI |
75 | PAGE_SIZE); |
76 | if (!new_memmap_phy) | |
77 | return; | |
78 | ||
79 | /* create new EFI memmap */ | |
80 | new_memmap = early_memremap(new_memmap_phy, | |
884f4f66 | 81 | efi.memmap.desc_size * new_nr_map); |
0f96a99d | 82 | if (!new_memmap) { |
884f4f66 | 83 | memblock_free(new_memmap_phy, efi.memmap.desc_size * new_nr_map); |
0f96a99d TI |
84 | return; |
85 | } | |
86 | ||
c8c1a4c5 | 87 | for (i = 0; i < nr_fake_mem; i++) |
60863c0d | 88 | efi_memmap_insert(&efi.memmap, new_memmap, &fake_mems[i]); |
0f96a99d TI |
89 | |
90 | /* swap into new EFI memmap */ | |
9479c7ce | 91 | early_memunmap(new_memmap, efi.memmap.desc_size * new_nr_map); |
9479c7ce | 92 | |
c45f4da3 | 93 | efi_memmap_install(new_memmap_phy, new_nr_map); |
0f96a99d TI |
94 | |
95 | /* print new EFI memmap */ | |
96 | efi_print_memmap(); | |
97 | } | |
98 | ||
99 | static int __init setup_fake_mem(char *p) | |
100 | { | |
101 | u64 start = 0, mem_size = 0, attribute = 0; | |
102 | int i; | |
103 | ||
104 | if (!p) | |
105 | return -EINVAL; | |
106 | ||
107 | while (*p != '\0') { | |
108 | mem_size = memparse(p, &p); | |
109 | if (*p == '@') | |
110 | start = memparse(p+1, &p); | |
111 | else | |
112 | break; | |
113 | ||
114 | if (*p == ':') | |
115 | attribute = simple_strtoull(p+1, &p, 0); | |
116 | else | |
117 | break; | |
118 | ||
119 | if (nr_fake_mem >= EFI_MAX_FAKEMEM) | |
120 | break; | |
121 | ||
122 | fake_mems[nr_fake_mem].range.start = start; | |
123 | fake_mems[nr_fake_mem].range.end = start + mem_size - 1; | |
124 | fake_mems[nr_fake_mem].attribute = attribute; | |
125 | nr_fake_mem++; | |
126 | ||
127 | if (*p == ',') | |
128 | p++; | |
129 | } | |
130 | ||
60863c0d | 131 | sort(fake_mems, nr_fake_mem, sizeof(struct efi_mem_range), |
0f96a99d TI |
132 | cmp_fake_mem, NULL); |
133 | ||
134 | for (i = 0; i < nr_fake_mem; i++) | |
135 | pr_info("efi_fake_mem: add attr=0x%016llx to [mem 0x%016llx-0x%016llx]", | |
136 | fake_mems[i].attribute, fake_mems[i].range.start, | |
137 | fake_mems[i].range.end); | |
138 | ||
139 | return *p == '\0' ? 0 : -EINVAL; | |
140 | } | |
141 | ||
142 | early_param("efi_fake_mem", setup_fake_mem); |