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
= 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 (old
= memmap
.map
; old
< memmap
.map_end
; old
+= memmap
.desc_size
) {
73 start
= md
->phys_addr
;
74 end
= start
+ (md
->num_pages
<< EFI_PAGE_SHIFT
) - 1;
76 for (i
= 0; i
< nr_fake_mem
; i
++) {
78 m_start
= fake_mems
[i
].range
.start
;
79 m_end
= fake_mems
[i
].range
.end
;
81 if (m_start
<= start
) {
82 /* split into 2 parts */
83 if (start
< m_end
&& m_end
< end
)
86 if (start
< m_start
&& m_start
< end
) {
87 /* split into 3 parts */
90 /* split into 2 parts */
97 /* allocate memory for new EFI memmap */
98 new_memmap_phy
= memblock_alloc(memmap
.desc_size
* new_nr_map
,
103 /* create new EFI memmap */
104 new_memmap
= early_memremap(new_memmap_phy
,
105 memmap
.desc_size
* new_nr_map
);
107 memblock_free(new_memmap_phy
, memmap
.desc_size
* new_nr_map
);
111 for (old
= memmap
.map
, new = new_memmap
;
112 old
< memmap
.map_end
;
113 old
+= memmap
.desc_size
, new += memmap
.desc_size
) {
115 /* copy original EFI memory descriptor */
116 memcpy(new, old
, memmap
.desc_size
);
118 start
= md
->phys_addr
;
119 end
= md
->phys_addr
+ (md
->num_pages
<< EFI_PAGE_SHIFT
) - 1;
121 for (i
= 0; i
< nr_fake_mem
; i
++) {
122 /* modifying range */
123 m_start
= fake_mems
[i
].range
.start
;
124 m_end
= fake_mems
[i
].range
.end
;
125 m_attr
= fake_mems
[i
].attribute
;
127 if (m_start
<= start
&& end
<= m_end
)
128 md
->attribute
|= m_attr
;
130 if (m_start
<= start
&&
131 (start
< m_end
&& m_end
< end
)) {
133 md
->attribute
|= m_attr
;
134 md
->num_pages
= (m_end
- md
->phys_addr
+ 1) >>
137 new += memmap
.desc_size
;
138 memcpy(new, old
, memmap
.desc_size
);
140 md
->phys_addr
= m_end
+ 1;
141 md
->num_pages
= (end
- md
->phys_addr
+ 1) >>
145 if ((start
< m_start
&& m_start
< end
) && m_end
< end
) {
147 md
->num_pages
= (m_start
- md
->phys_addr
) >>
150 new += memmap
.desc_size
;
151 memcpy(new, old
, memmap
.desc_size
);
153 md
->attribute
|= m_attr
;
154 md
->phys_addr
= m_start
;
155 md
->num_pages
= (m_end
- m_start
+ 1) >>
158 new += memmap
.desc_size
;
159 memcpy(new, old
, memmap
.desc_size
);
161 md
->phys_addr
= m_end
+ 1;
162 md
->num_pages
= (end
- m_end
) >>
166 if ((start
< m_start
&& m_start
< end
) &&
169 md
->num_pages
= (m_start
- md
->phys_addr
) >>
172 new += memmap
.desc_size
;
173 memcpy(new, old
, memmap
.desc_size
);
175 md
->phys_addr
= m_start
;
176 md
->num_pages
= (end
- md
->phys_addr
+ 1) >>
178 md
->attribute
|= m_attr
;
183 /* swap into new EFI memmap */
185 memmap
.map
= new_memmap
;
186 memmap
.phys_map
= new_memmap_phy
;
187 memmap
.nr_map
= new_nr_map
;
188 memmap
.map_end
= memmap
.map
+ memmap
.nr_map
* memmap
.desc_size
;
189 set_bit(EFI_MEMMAP
, &efi
.flags
);
191 /* print new EFI memmap */
195 static int __init
setup_fake_mem(char *p
)
197 u64 start
= 0, mem_size
= 0, attribute
= 0;
204 mem_size
= memparse(p
, &p
);
206 start
= memparse(p
+1, &p
);
211 attribute
= simple_strtoull(p
+1, &p
, 0);
215 if (nr_fake_mem
>= EFI_MAX_FAKEMEM
)
218 fake_mems
[nr_fake_mem
].range
.start
= start
;
219 fake_mems
[nr_fake_mem
].range
.end
= start
+ mem_size
- 1;
220 fake_mems
[nr_fake_mem
].attribute
= attribute
;
227 sort(fake_mems
, nr_fake_mem
, sizeof(struct fake_mem
),
230 for (i
= 0; i
< nr_fake_mem
; i
++)
231 pr_info("efi_fake_mem: add attr=0x%016llx to [mem 0x%016llx-0x%016llx]",
232 fake_mems
[i
].attribute
, fake_mems
[i
].range
.start
,
233 fake_mems
[i
].range
.end
);
235 return *p
== '\0' ? 0 : -EINVAL
;
238 early_param("efi_fake_mem", setup_fake_mem
);