1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2015 Neel Natu <neel@freebsd.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 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. 15 * 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 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include <sys/types.h> 33 #include <sys/mman.h> 34 #include <sys/stat.h> 35 36 #include <machine/vmm.h> 37 38 #include <err.h> 39 #include <errno.h> 40 #include <fcntl.h> 41 #include <stdio.h> 42 #include <string.h> 43 #include <unistd.h> 44 #include <stdbool.h> 45 46 #include <vmmapi.h> 47 #include "bhyverun.h" 48 #include "bootrom.h" 49 #include "debug.h" 50 51 #define BOOTROM_SIZE (16 * 1024 * 1024) /* 16 MB */ 52 53 /* 54 * ROM region is 16 MB at the top of 4GB ("low") memory. 55 * 56 * The size is limited so it doesn't encroach into reserved MMIO space (e.g., 57 * APIC, HPET, MSI). 58 * 59 * It is allocated in page-multiple blocks on a first-come first-serve basis, 60 * from high to low, during initialization, and does not change at runtime. 61 */ 62 static char *romptr; /* Pointer to userspace-mapped bootrom region. */ 63 static vm_paddr_t gpa_base; /* GPA of low end of region. */ 64 static vm_paddr_t gpa_allocbot; /* Low GPA of free region. */ 65 static vm_paddr_t gpa_alloctop; /* High GPA, minus 1, of free region. */ 66 67 void 68 init_bootrom(struct vmctx *ctx) 69 { 70 romptr = vm_create_devmem(ctx, VM_BOOTROM, "bootrom", BOOTROM_SIZE); 71 if (romptr == MAP_FAILED) 72 err(4, "%s: vm_create_devmem", __func__); 73 gpa_base = (1ULL << 32) - BOOTROM_SIZE; 74 gpa_allocbot = gpa_base; 75 gpa_alloctop = (1ULL << 32) - 1; 76 } 77 78 int 79 bootrom_alloc(struct vmctx *ctx, size_t len, int prot, int flags, 80 char **region_out, uint64_t *gpa_out) 81 { 82 static const int bootrom_valid_flags = BOOTROM_ALLOC_TOP; 83 84 vm_paddr_t gpa; 85 vm_ooffset_t segoff; 86 87 if (flags & ~bootrom_valid_flags) { 88 warnx("%s: Invalid flags: %x", __func__, 89 flags & ~bootrom_valid_flags); 90 return (EINVAL); 91 } 92 if (prot & ~_PROT_ALL) { 93 warnx("%s: Invalid protection: %x", __func__, 94 prot & ~_PROT_ALL); 95 return (EINVAL); 96 } 97 98 if (len == 0 || len > BOOTROM_SIZE) { 99 warnx("ROM size %zu is invalid", len); 100 return (EINVAL); 101 } 102 if (len & PAGE_MASK) { 103 warnx("ROM size %zu is not a multiple of the page size", 104 len); 105 return (EINVAL); 106 } 107 108 if (flags & BOOTROM_ALLOC_TOP) { 109 gpa = (gpa_alloctop - len) + 1; 110 if (gpa < gpa_allocbot) { 111 warnx("No room for %zu ROM in bootrom region", len); 112 return (ENOMEM); 113 } 114 } else { 115 gpa = gpa_allocbot; 116 if (gpa > (gpa_alloctop - len) + 1) { 117 warnx("No room for %zu ROM in bootrom region", len); 118 return (ENOMEM); 119 } 120 } 121 122 segoff = gpa - gpa_base; 123 if (vm_mmap_memseg(ctx, gpa, VM_BOOTROM, segoff, len, prot) != 0) { 124 int serrno = errno; 125 warn("%s: vm_mmap_mapseg", __func__); 126 return (serrno); 127 } 128 129 if (flags & BOOTROM_ALLOC_TOP) 130 gpa_alloctop = gpa - 1; 131 else 132 gpa_allocbot = gpa + len; 133 134 *region_out = romptr + segoff; 135 if (gpa_out != NULL) 136 *gpa_out = gpa; 137 return (0); 138 } 139 140 int 141 bootrom_loadrom(struct vmctx *ctx, const char *romfile) 142 { 143 struct stat sbuf; 144 ssize_t rlen; 145 char *ptr; 146 int fd, i, rv; 147 148 rv = -1; 149 fd = open(romfile, O_RDONLY); 150 if (fd < 0) { 151 EPRINTLN("Error opening bootrom \"%s\": %s", 152 romfile, strerror(errno)); 153 goto done; 154 } 155 156 if (fstat(fd, &sbuf) < 0) { 157 EPRINTLN("Could not fstat bootrom file \"%s\": %s", 158 romfile, strerror(errno)); 159 goto done; 160 } 161 162 /* Map the bootrom into the guest address space */ 163 if (bootrom_alloc(ctx, sbuf.st_size, PROT_READ | PROT_EXEC, 164 BOOTROM_ALLOC_TOP, &ptr, NULL) != 0) 165 goto done; 166 167 /* Read 'romfile' into the guest address space */ 168 for (i = 0; i < sbuf.st_size / PAGE_SIZE; i++) { 169 rlen = read(fd, ptr + i * PAGE_SIZE, PAGE_SIZE); 170 if (rlen != PAGE_SIZE) { 171 EPRINTLN("Incomplete read of page %d of bootrom " 172 "file %s: %ld bytes", i, romfile, rlen); 173 goto done; 174 } 175 } 176 rv = 0; 177 done: 178 if (fd >= 0) 179 close(fd); 180 return (rv); 181 } 182