2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2015 Neel Natu <neel@freebsd.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 #include <sys/param.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/types.h>
36 #include <machine/vmm.h>
54 #define BOOTROM_SIZE (16 * 1024 * 1024) /* 16 MB */
57 * ROM region is 16 MB at the top of 4GB ("low") memory.
59 * The size is limited so it doesn't encroach into reserved MMIO space (e.g.,
62 * It is allocated in page-multiple blocks on a first-come first-serve basis,
63 * from high to low, during initialization, and does not change at runtime.
65 static char *romptr
; /* Pointer to userspace-mapped bootrom region. */
66 static vm_paddr_t gpa_base
; /* GPA of low end of region. */
67 static vm_paddr_t gpa_allocbot
; /* Low GPA of free region. */
68 static vm_paddr_t gpa_alloctop
; /* High GPA, minus 1, of free region. */
70 #define CFI_BCS_WRITE_BYTE 0x10
71 #define CFI_BCS_CLEAR_STATUS 0x50
72 #define CFI_BCS_READ_STATUS 0x70
73 #define CFI_BCS_READ_ARRAY 0xff
75 static struct bootrom_var_state
{
80 } var
= { NULL
, 0, 0, CFI_BCS_READ_ARRAY
};
83 * Emulate just those CFI basic commands that will convince EDK II
84 * that the Firmware Volume area is writable and persistent.
87 bootrom_var_mem_handler(struct vmctx
*ctx __unused
, int vcpu __unused
, int dir
,
88 uint64_t addr
, int size
, uint64_t *val
, void *arg1 __unused
,
93 offset
= addr
- var
.gpa
;
94 if (offset
+ size
> var
.size
|| offset
< 0 || offset
+ size
<= offset
)
97 if (dir
== MEM_F_WRITE
) {
99 case CFI_BCS_WRITE_BYTE
:
100 memcpy(var
.mmap
+ offset
, val
, size
);
101 var
.cmd
= CFI_BCS_READ_ARRAY
;
104 var
.cmd
= *(uint8_t *)val
;
108 case CFI_BCS_CLEAR_STATUS
:
109 case CFI_BCS_READ_STATUS
:
110 memset(val
, 0, size
);
111 var
.cmd
= CFI_BCS_READ_ARRAY
;
114 memcpy(val
, var
.mmap
+ offset
, size
);
122 init_bootrom(struct vmctx
*ctx
)
124 romptr
= vm_create_devmem(ctx
, VM_BOOTROM
, "bootrom", BOOTROM_SIZE
);
125 if (romptr
== MAP_FAILED
)
126 err(4, "%s: vm_create_devmem", __func__
);
127 gpa_base
= (1ULL << 32) - BOOTROM_SIZE
;
128 gpa_allocbot
= gpa_base
;
129 gpa_alloctop
= (1ULL << 32) - 1;
133 bootrom_alloc(struct vmctx
*ctx
, size_t len
, int prot
, int flags
,
134 char **region_out
, uint64_t *gpa_out
)
136 static const int bootrom_valid_flags
= BOOTROM_ALLOC_TOP
;
141 if (flags
& ~bootrom_valid_flags
) {
142 warnx("%s: Invalid flags: %x", __func__
,
143 flags
& ~bootrom_valid_flags
);
146 if (prot
& ~_PROT_ALL
) {
147 warnx("%s: Invalid protection: %x", __func__
,
152 if (len
== 0 || len
> BOOTROM_SIZE
) {
153 warnx("ROM size %zu is invalid", len
);
156 if (len
& PAGE_MASK
) {
157 warnx("ROM size %zu is not a multiple of the page size",
162 if (flags
& BOOTROM_ALLOC_TOP
) {
163 gpa
= (gpa_alloctop
- len
) + 1;
164 if (gpa
< gpa_allocbot
) {
165 warnx("No room for %zu ROM in bootrom region", len
);
170 if (gpa
> (gpa_alloctop
- len
) + 1) {
171 warnx("No room for %zu ROM in bootrom region", len
);
176 segoff
= gpa
- gpa_base
;
177 if (vm_mmap_memseg(ctx
, gpa
, VM_BOOTROM
, segoff
, len
, prot
) != 0) {
179 warn("%s: vm_mmap_mapseg", __func__
);
183 if (flags
& BOOTROM_ALLOC_TOP
)
184 gpa_alloctop
= gpa
- 1;
186 gpa_allocbot
= gpa
+ len
;
188 *region_out
= romptr
+ segoff
;
195 bootrom_loadrom(struct vmctx
*ctx
, const nvlist_t
*nvl
)
199 off_t rom_size
, var_size
, total_size
;
201 int fd
, varfd
, i
, rv
;
202 const char *bootrom
, *varfile
;
207 bootrom
= get_config_value_node(nvl
, "bootrom");
208 if (bootrom
== NULL
) {
213 * get_config_value_node may use a thread local buffer to return
214 * variables. So, when we query the second variable, the first variable
215 * might get overwritten. For that reason, the bootrom should be
218 romfile
= strdup(bootrom
);
219 if (romfile
== NULL
) {
223 fd
= open(romfile
, O_RDONLY
);
225 EPRINTLN("Error opening bootrom \"%s\": %s",
226 romfile
, strerror(errno
));
230 if (fstat(fd
, &sbuf
) < 0) {
231 EPRINTLN("Could not fstat bootrom file \"%s\": %s", romfile
,
236 rom_size
= sbuf
.st_size
;
238 varfile
= get_config_value_node(nvl
, "bootvars");
240 if (varfile
!= NULL
) {
241 varfd
= open(varfile
, O_RDWR
);
243 fprintf(stderr
, "Error opening bootrom variable file "
244 "\"%s\": %s\n", varfile
, strerror(errno
));
248 if (fstat(varfd
, &sbuf
) < 0) {
250 "Could not fstat bootrom variable file \"%s\": %s\n",
251 varfile
, strerror(errno
));
255 var_size
= sbuf
.st_size
;
258 if (var_size
> BOOTROM_SIZE
||
259 (var_size
!= 0 && var_size
< PAGE_SIZE
)) {
260 fprintf(stderr
, "Invalid bootrom variable size %ld\n",
265 total_size
= rom_size
+ var_size
;
267 if (total_size
> BOOTROM_SIZE
) {
268 fprintf(stderr
, "Invalid bootrom and variable aggregate size "
269 "%ld\n", total_size
);
273 /* Map the bootrom into the guest address space */
274 if (bootrom_alloc(ctx
, rom_size
, PROT_READ
| PROT_EXEC
,
275 BOOTROM_ALLOC_TOP
, &ptr
, NULL
) != 0) {
279 /* Read 'romfile' into the guest address space */
280 for (i
= 0; i
< rom_size
/ PAGE_SIZE
; i
++) {
281 rlen
= read(fd
, ptr
+ i
* PAGE_SIZE
, PAGE_SIZE
);
282 if (rlen
!= PAGE_SIZE
) {
283 EPRINTLN("Incomplete read of page %d of bootrom "
284 "file %s: %ld bytes", i
, romfile
, rlen
);
291 var
.mmap
= mmap(NULL
, var_size
, PROT_READ
| PROT_WRITE
,
292 MAP_SHARED
, varfd
, 0);
294 var
.mmap
= (uint8_t *)mmap(NULL
, var_size
,
295 PROT_READ
| PROT_WRITE
, MAP_SHARED
, varfd
, 0);
297 if (var
.mmap
== MAP_FAILED
)
300 var
.gpa
= (gpa_alloctop
- var_size
) + 1;
301 gpa_alloctop
= var
.gpa
- 1;
302 rv
= register_mem(&(struct mem_range
){
303 .name
= "bootrom variable",
305 .handler
= bootrom_var_mem_handler
,