4 * Copyright (C) 2015 FUJITSU LIMITED
5 * Author: Taku Izumi <izumi.taku@jp.fujitsu.com>
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
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.
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
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/>.
24 * The full GNU General Public License is included in this distribution in
25 * the file called "COPYING".
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>
36 #define EFI_MAX_FAKEMEM CONFIG_EFI_MAX_FAKE_MEM
42 static struct fake_mem fake_mems
[EFI_MAX_FAKEMEM
];
43 static int nr_fake_mem
;
45 static int __init
cmp_fake_mem(const void *x1
, const void *x2
)
47 const struct fake_mem
*m1
= x1
;
48 const struct fake_mem
*m2
= x2
;
50 if (m1
->range
.start
< m2
->range
.start
)
52 if (m1
->range
.start
> m2
->range
.start
)
57 void __init
efi_fake_memmap(void)
59 u64 start
, end
, m_start
, m_end
, m_attr
;
60 int new_nr_map
= efi
.memmap
.nr_map
;
61 efi_memory_desc_t
*md
;
62 phys_addr_t new_memmap_phy
;
67 if (!nr_fake_mem
|| !efi_enabled(EFI_MEMMAP
))
70 /* count up the number of EFI memory descriptor */
71 for_each_efi_memory_desc(md
) {
72 start
= md
->phys_addr
;
73 end
= start
+ (md
->num_pages
<< EFI_PAGE_SHIFT
) - 1;
75 for (i
= 0; i
< nr_fake_mem
; i
++) {
77 m_start
= fake_mems
[i
].range
.start
;
78 m_end
= fake_mems
[i
].range
.end
;
80 if (m_start
<= start
) {
81 /* split into 2 parts */
82 if (start
< m_end
&& m_end
< end
)
85 if (start
< m_start
&& m_start
< end
) {
86 /* split into 3 parts */
89 /* split into 2 parts */
96 /* allocate memory for new EFI memmap */
97 new_memmap_phy
= memblock_alloc(efi
.memmap
.desc_size
* new_nr_map
,
102 /* create new EFI memmap */
103 new_memmap
= early_memremap(new_memmap_phy
,
104 efi
.memmap
.desc_size
* new_nr_map
);
106 memblock_free(new_memmap_phy
, efi
.memmap
.desc_size
* new_nr_map
);
110 for (old
= efi
.memmap
.map
, new = new_memmap
;
111 old
< efi
.memmap
.map_end
;
112 old
+= efi
.memmap
.desc_size
, new += efi
.memmap
.desc_size
) {
114 /* copy original EFI memory descriptor */
115 memcpy(new, old
, efi
.memmap
.desc_size
);
117 start
= md
->phys_addr
;
118 end
= md
->phys_addr
+ (md
->num_pages
<< EFI_PAGE_SHIFT
) - 1;
120 for (i
= 0; i
< nr_fake_mem
; i
++) {
121 /* modifying range */
122 m_start
= fake_mems
[i
].range
.start
;
123 m_end
= fake_mems
[i
].range
.end
;
124 m_attr
= fake_mems
[i
].attribute
;
126 if (m_start
<= start
&& end
<= m_end
)
127 md
->attribute
|= m_attr
;
129 if (m_start
<= start
&&
130 (start
< m_end
&& m_end
< end
)) {
132 md
->attribute
|= m_attr
;
133 md
->num_pages
= (m_end
- md
->phys_addr
+ 1) >>
136 new += efi
.memmap
.desc_size
;
137 memcpy(new, old
, efi
.memmap
.desc_size
);
139 md
->phys_addr
= m_end
+ 1;
140 md
->num_pages
= (end
- md
->phys_addr
+ 1) >>
144 if ((start
< m_start
&& m_start
< end
) && m_end
< end
) {
146 md
->num_pages
= (m_start
- md
->phys_addr
) >>
149 new += efi
.memmap
.desc_size
;
150 memcpy(new, old
, efi
.memmap
.desc_size
);
152 md
->attribute
|= m_attr
;
153 md
->phys_addr
= m_start
;
154 md
->num_pages
= (m_end
- m_start
+ 1) >>
157 new += efi
.memmap
.desc_size
;
158 memcpy(new, old
, efi
.memmap
.desc_size
);
160 md
->phys_addr
= m_end
+ 1;
161 md
->num_pages
= (end
- m_end
) >>
165 if ((start
< m_start
&& m_start
< end
) &&
168 md
->num_pages
= (m_start
- md
->phys_addr
) >>
171 new += efi
.memmap
.desc_size
;
172 memcpy(new, old
, efi
.memmap
.desc_size
);
174 md
->phys_addr
= m_start
;
175 md
->num_pages
= (end
- md
->phys_addr
+ 1) >>
177 md
->attribute
|= m_attr
;
182 /* swap into new EFI memmap */
184 efi
.memmap
.map
= new_memmap
;
185 efi
.memmap
.phys_map
= new_memmap_phy
;
186 efi
.memmap
.nr_map
= new_nr_map
;
187 efi
.memmap
.map_end
= efi
.memmap
.map
+ efi
.memmap
.nr_map
* efi
.memmap
.desc_size
;
188 set_bit(EFI_MEMMAP
, &efi
.flags
);
190 /* print new EFI memmap */
194 static int __init
setup_fake_mem(char *p
)
196 u64 start
= 0, mem_size
= 0, attribute
= 0;
203 mem_size
= memparse(p
, &p
);
205 start
= memparse(p
+1, &p
);
210 attribute
= simple_strtoull(p
+1, &p
, 0);
214 if (nr_fake_mem
>= EFI_MAX_FAKEMEM
)
217 fake_mems
[nr_fake_mem
].range
.start
= start
;
218 fake_mems
[nr_fake_mem
].range
.end
= start
+ mem_size
- 1;
219 fake_mems
[nr_fake_mem
].attribute
= attribute
;
226 sort(fake_mems
, nr_fake_mem
, sizeof(struct fake_mem
),
229 for (i
= 0; i
< nr_fake_mem
; i
++)
230 pr_info("efi_fake_mem: add attr=0x%016llx to [mem 0x%016llx-0x%016llx]",
231 fake_mems
[i
].attribute
, fake_mems
[i
].range
.start
,
232 fake_mems
[i
].range
.end
);
234 return *p
== '\0' ? 0 : -EINVAL
;
237 early_param("efi_fake_mem", setup_fake_mem
);