14c87aefeSPatrick Mooney /*- 24c87aefeSPatrick Mooney * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 34c87aefeSPatrick Mooney * 44c87aefeSPatrick Mooney * Copyright (c) 2015 Neel Natu <neel@freebsd.org> 54c87aefeSPatrick Mooney * All rights reserved. 64c87aefeSPatrick Mooney * 74c87aefeSPatrick Mooney * Redistribution and use in source and binary forms, with or without 84c87aefeSPatrick Mooney * modification, are permitted provided that the following conditions 94c87aefeSPatrick Mooney * are met: 104c87aefeSPatrick Mooney * 1. Redistributions of source code must retain the above copyright 114c87aefeSPatrick Mooney * notice, this list of conditions and the following disclaimer. 124c87aefeSPatrick Mooney * 2. Redistributions in binary form must reproduce the above copyright 134c87aefeSPatrick Mooney * notice, this list of conditions and the following disclaimer in the 144c87aefeSPatrick Mooney * documentation and/or other materials provided with the distribution. 154c87aefeSPatrick Mooney * 164c87aefeSPatrick Mooney * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND 174c87aefeSPatrick Mooney * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 184c87aefeSPatrick Mooney * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 194c87aefeSPatrick Mooney * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 204c87aefeSPatrick Mooney * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 214c87aefeSPatrick Mooney * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 224c87aefeSPatrick Mooney * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 234c87aefeSPatrick Mooney * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 244c87aefeSPatrick Mooney * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 254c87aefeSPatrick Mooney * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 264c87aefeSPatrick Mooney * SUCH DAMAGE. 274c87aefeSPatrick Mooney */ 284c87aefeSPatrick Mooney 294c87aefeSPatrick Mooney #include <sys/param.h> 304c87aefeSPatrick Mooney __FBSDID("$FreeBSD$"); 314c87aefeSPatrick Mooney 324c87aefeSPatrick Mooney #include <sys/types.h> 334c87aefeSPatrick Mooney #include <sys/mman.h> 344c87aefeSPatrick Mooney #include <sys/stat.h> 354c87aefeSPatrick Mooney 364c87aefeSPatrick Mooney #include <machine/vmm.h> 374c87aefeSPatrick Mooney 38154972afSPatrick Mooney #include <err.h> 394c87aefeSPatrick Mooney #include <errno.h> 404c87aefeSPatrick Mooney #include <fcntl.h> 414c87aefeSPatrick Mooney #include <stdio.h> 42*6dc98349SAndy Fiddaman #include <stdlib.h> 434c87aefeSPatrick Mooney #include <string.h> 444c87aefeSPatrick Mooney #include <unistd.h> 454c87aefeSPatrick Mooney #include <stdbool.h> 464c87aefeSPatrick Mooney 474c87aefeSPatrick Mooney #include <vmmapi.h> 48*6dc98349SAndy Fiddaman 494c87aefeSPatrick Mooney #include "bhyverun.h" 504c87aefeSPatrick Mooney #include "bootrom.h" 51154972afSPatrick Mooney #include "debug.h" 52*6dc98349SAndy Fiddaman #include "mem.h" 534c87aefeSPatrick Mooney 54154972afSPatrick Mooney #define BOOTROM_SIZE (16 * 1024 * 1024) /* 16 MB */ 55154972afSPatrick Mooney 56154972afSPatrick Mooney /* 57154972afSPatrick Mooney * ROM region is 16 MB at the top of 4GB ("low") memory. 58154972afSPatrick Mooney * 59154972afSPatrick Mooney * The size is limited so it doesn't encroach into reserved MMIO space (e.g., 60154972afSPatrick Mooney * APIC, HPET, MSI). 61154972afSPatrick Mooney * 62154972afSPatrick Mooney * It is allocated in page-multiple blocks on a first-come first-serve basis, 63154972afSPatrick Mooney * from high to low, during initialization, and does not change at runtime. 64154972afSPatrick Mooney */ 65154972afSPatrick Mooney static char *romptr; /* Pointer to userspace-mapped bootrom region. */ 66154972afSPatrick Mooney static vm_paddr_t gpa_base; /* GPA of low end of region. */ 67154972afSPatrick Mooney static vm_paddr_t gpa_allocbot; /* Low GPA of free region. */ 68154972afSPatrick Mooney static vm_paddr_t gpa_alloctop; /* High GPA, minus 1, of free region. */ 69154972afSPatrick Mooney 70*6dc98349SAndy Fiddaman #define CFI_BCS_WRITE_BYTE 0x10 71*6dc98349SAndy Fiddaman #define CFI_BCS_CLEAR_STATUS 0x50 72*6dc98349SAndy Fiddaman #define CFI_BCS_READ_STATUS 0x70 73*6dc98349SAndy Fiddaman #define CFI_BCS_READ_ARRAY 0xff 74*6dc98349SAndy Fiddaman 75*6dc98349SAndy Fiddaman static struct bootrom_var_state { 76*6dc98349SAndy Fiddaman uint8_t *mmap; 77*6dc98349SAndy Fiddaman uint64_t gpa; 78*6dc98349SAndy Fiddaman off_t size; 79*6dc98349SAndy Fiddaman uint8_t cmd; 80*6dc98349SAndy Fiddaman } var = { NULL, 0, 0, CFI_BCS_READ_ARRAY }; 81*6dc98349SAndy Fiddaman 82*6dc98349SAndy Fiddaman /* 83*6dc98349SAndy Fiddaman * Emulate just those CFI basic commands that will convince EDK II 84*6dc98349SAndy Fiddaman * that the Firmware Volume area is writable and persistent. 85*6dc98349SAndy Fiddaman */ 86*6dc98349SAndy Fiddaman static int 87*6dc98349SAndy Fiddaman bootrom_var_mem_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr, 88*6dc98349SAndy Fiddaman int size, uint64_t *val, void *arg1, long arg2) 89*6dc98349SAndy Fiddaman { 90*6dc98349SAndy Fiddaman off_t offset; 91*6dc98349SAndy Fiddaman 92*6dc98349SAndy Fiddaman offset = addr - var.gpa; 93*6dc98349SAndy Fiddaman if (offset + size > var.size || offset < 0 || offset + size <= offset) 94*6dc98349SAndy Fiddaman return (EINVAL); 95*6dc98349SAndy Fiddaman 96*6dc98349SAndy Fiddaman if (dir == MEM_F_WRITE) { 97*6dc98349SAndy Fiddaman switch (var.cmd) { 98*6dc98349SAndy Fiddaman case CFI_BCS_WRITE_BYTE: 99*6dc98349SAndy Fiddaman memcpy(var.mmap + offset, val, size); 100*6dc98349SAndy Fiddaman var.cmd = CFI_BCS_READ_ARRAY; 101*6dc98349SAndy Fiddaman break; 102*6dc98349SAndy Fiddaman default: 103*6dc98349SAndy Fiddaman var.cmd = *(uint8_t *)val; 104*6dc98349SAndy Fiddaman } 105*6dc98349SAndy Fiddaman } else { 106*6dc98349SAndy Fiddaman switch (var.cmd) { 107*6dc98349SAndy Fiddaman case CFI_BCS_CLEAR_STATUS: 108*6dc98349SAndy Fiddaman case CFI_BCS_READ_STATUS: 109*6dc98349SAndy Fiddaman memset(val, 0, size); 110*6dc98349SAndy Fiddaman var.cmd = CFI_BCS_READ_ARRAY; 111*6dc98349SAndy Fiddaman break; 112*6dc98349SAndy Fiddaman default: 113*6dc98349SAndy Fiddaman memcpy(val, var.mmap + offset, size); 114*6dc98349SAndy Fiddaman break; 115*6dc98349SAndy Fiddaman } 116*6dc98349SAndy Fiddaman } 117*6dc98349SAndy Fiddaman return (0); 118*6dc98349SAndy Fiddaman } 119*6dc98349SAndy Fiddaman 120154972afSPatrick Mooney void 121154972afSPatrick Mooney init_bootrom(struct vmctx *ctx) 122154972afSPatrick Mooney { 123154972afSPatrick Mooney romptr = vm_create_devmem(ctx, VM_BOOTROM, "bootrom", BOOTROM_SIZE); 124154972afSPatrick Mooney if (romptr == MAP_FAILED) 125154972afSPatrick Mooney err(4, "%s: vm_create_devmem", __func__); 126154972afSPatrick Mooney gpa_base = (1ULL << 32) - BOOTROM_SIZE; 127154972afSPatrick Mooney gpa_allocbot = gpa_base; 128154972afSPatrick Mooney gpa_alloctop = (1ULL << 32) - 1; 129154972afSPatrick Mooney } 1304c87aefeSPatrick Mooney 1314c87aefeSPatrick Mooney int 132154972afSPatrick Mooney bootrom_alloc(struct vmctx *ctx, size_t len, int prot, int flags, 133154972afSPatrick Mooney char **region_out, uint64_t *gpa_out) 134154972afSPatrick Mooney { 135154972afSPatrick Mooney static const int bootrom_valid_flags = BOOTROM_ALLOC_TOP; 136154972afSPatrick Mooney 137154972afSPatrick Mooney vm_paddr_t gpa; 138154972afSPatrick Mooney vm_ooffset_t segoff; 139154972afSPatrick Mooney 140154972afSPatrick Mooney if (flags & ~bootrom_valid_flags) { 141154972afSPatrick Mooney warnx("%s: Invalid flags: %x", __func__, 142154972afSPatrick Mooney flags & ~bootrom_valid_flags); 143154972afSPatrick Mooney return (EINVAL); 144154972afSPatrick Mooney } 145154972afSPatrick Mooney if (prot & ~_PROT_ALL) { 146154972afSPatrick Mooney warnx("%s: Invalid protection: %x", __func__, 147154972afSPatrick Mooney prot & ~_PROT_ALL); 148154972afSPatrick Mooney return (EINVAL); 149154972afSPatrick Mooney } 150154972afSPatrick Mooney 151154972afSPatrick Mooney if (len == 0 || len > BOOTROM_SIZE) { 152154972afSPatrick Mooney warnx("ROM size %zu is invalid", len); 153154972afSPatrick Mooney return (EINVAL); 154154972afSPatrick Mooney } 155154972afSPatrick Mooney if (len & PAGE_MASK) { 156154972afSPatrick Mooney warnx("ROM size %zu is not a multiple of the page size", 157154972afSPatrick Mooney len); 158154972afSPatrick Mooney return (EINVAL); 159154972afSPatrick Mooney } 160154972afSPatrick Mooney 161154972afSPatrick Mooney if (flags & BOOTROM_ALLOC_TOP) { 162154972afSPatrick Mooney gpa = (gpa_alloctop - len) + 1; 163154972afSPatrick Mooney if (gpa < gpa_allocbot) { 164154972afSPatrick Mooney warnx("No room for %zu ROM in bootrom region", len); 165154972afSPatrick Mooney return (ENOMEM); 166154972afSPatrick Mooney } 167154972afSPatrick Mooney } else { 168154972afSPatrick Mooney gpa = gpa_allocbot; 169154972afSPatrick Mooney if (gpa > (gpa_alloctop - len) + 1) { 170154972afSPatrick Mooney warnx("No room for %zu ROM in bootrom region", len); 171154972afSPatrick Mooney return (ENOMEM); 172154972afSPatrick Mooney } 173154972afSPatrick Mooney } 174154972afSPatrick Mooney 175154972afSPatrick Mooney segoff = gpa - gpa_base; 176154972afSPatrick Mooney if (vm_mmap_memseg(ctx, gpa, VM_BOOTROM, segoff, len, prot) != 0) { 177154972afSPatrick Mooney int serrno = errno; 178154972afSPatrick Mooney warn("%s: vm_mmap_mapseg", __func__); 179154972afSPatrick Mooney return (serrno); 180154972afSPatrick Mooney } 181154972afSPatrick Mooney 182154972afSPatrick Mooney if (flags & BOOTROM_ALLOC_TOP) 183154972afSPatrick Mooney gpa_alloctop = gpa - 1; 184154972afSPatrick Mooney else 185154972afSPatrick Mooney gpa_allocbot = gpa + len; 186154972afSPatrick Mooney 187154972afSPatrick Mooney *region_out = romptr + segoff; 188154972afSPatrick Mooney if (gpa_out != NULL) 189154972afSPatrick Mooney *gpa_out = gpa; 190154972afSPatrick Mooney return (0); 191154972afSPatrick Mooney } 192154972afSPatrick Mooney 193154972afSPatrick Mooney int 194154972afSPatrick Mooney bootrom_loadrom(struct vmctx *ctx, const char *romfile) 1954c87aefeSPatrick Mooney { 1964c87aefeSPatrick Mooney struct stat sbuf; 1974c87aefeSPatrick Mooney ssize_t rlen; 198*6dc98349SAndy Fiddaman off_t rom_size, var_size, total_size; 199*6dc98349SAndy Fiddaman char *ptr, *varfile; 200*6dc98349SAndy Fiddaman int fd, varfd, i, rv; 2014c87aefeSPatrick Mooney 2024c87aefeSPatrick Mooney rv = -1; 203*6dc98349SAndy Fiddaman varfd = -1; 204*6dc98349SAndy Fiddaman 205*6dc98349SAndy Fiddaman varfile = strdup(romfile); 206*6dc98349SAndy Fiddaman romfile = strsep(&varfile, ","); 207*6dc98349SAndy Fiddaman 2084c87aefeSPatrick Mooney fd = open(romfile, O_RDONLY); 2094c87aefeSPatrick Mooney if (fd < 0) { 210154972afSPatrick Mooney EPRINTLN("Error opening bootrom \"%s\": %s", 2114c87aefeSPatrick Mooney romfile, strerror(errno)); 2124c87aefeSPatrick Mooney goto done; 2134c87aefeSPatrick Mooney } 2144c87aefeSPatrick Mooney 215*6dc98349SAndy Fiddaman if (varfile != NULL) { 216*6dc98349SAndy Fiddaman varfd = open(varfile, O_RDWR); 217*6dc98349SAndy Fiddaman if (varfd < 0) { 218*6dc98349SAndy Fiddaman fprintf(stderr, "Error opening bootrom variable file " 219*6dc98349SAndy Fiddaman "\"%s\": %s\n", varfile, strerror(errno)); 220*6dc98349SAndy Fiddaman goto done; 221*6dc98349SAndy Fiddaman } 222*6dc98349SAndy Fiddaman } 223*6dc98349SAndy Fiddaman 2244c87aefeSPatrick Mooney if (fstat(fd, &sbuf) < 0) { 225154972afSPatrick Mooney EPRINTLN("Could not fstat bootrom file \"%s\": %s", 2264c87aefeSPatrick Mooney romfile, strerror(errno)); 2274c87aefeSPatrick Mooney goto done; 2284c87aefeSPatrick Mooney } 2294c87aefeSPatrick Mooney 230*6dc98349SAndy Fiddaman rom_size = sbuf.st_size; 231*6dc98349SAndy Fiddaman if (varfd < 0) { 232*6dc98349SAndy Fiddaman var_size = 0; 233*6dc98349SAndy Fiddaman } else { 234*6dc98349SAndy Fiddaman if (fstat(varfd, &sbuf) < 0) { 235*6dc98349SAndy Fiddaman fprintf(stderr, "Could not fstat bootrom variable file \"%s\": %s\n", 236*6dc98349SAndy Fiddaman varfile, strerror(errno)); 2374c87aefeSPatrick Mooney goto done; 238*6dc98349SAndy Fiddaman } 239*6dc98349SAndy Fiddaman var_size = sbuf.st_size; 240*6dc98349SAndy Fiddaman } 241*6dc98349SAndy Fiddaman 242*6dc98349SAndy Fiddaman if (var_size > BOOTROM_SIZE || 243*6dc98349SAndy Fiddaman (var_size != 0 && var_size < PAGE_SIZE)) { 244*6dc98349SAndy Fiddaman fprintf(stderr, "Invalid bootrom variable size %ld\n", 245*6dc98349SAndy Fiddaman var_size); 246*6dc98349SAndy Fiddaman goto done; 247*6dc98349SAndy Fiddaman } 248*6dc98349SAndy Fiddaman 249*6dc98349SAndy Fiddaman total_size = rom_size + var_size; 250*6dc98349SAndy Fiddaman 251*6dc98349SAndy Fiddaman if (total_size > BOOTROM_SIZE) { 252*6dc98349SAndy Fiddaman fprintf(stderr, "Invalid bootrom and variable aggregate size " 253*6dc98349SAndy Fiddaman "%ld\n", total_size); 254*6dc98349SAndy Fiddaman goto done; 255*6dc98349SAndy Fiddaman } 256*6dc98349SAndy Fiddaman 257*6dc98349SAndy Fiddaman /* Map the bootrom into the guest address space */ 258*6dc98349SAndy Fiddaman if (bootrom_alloc(ctx, rom_size, PROT_READ | PROT_EXEC, 259*6dc98349SAndy Fiddaman BOOTROM_ALLOC_TOP, &ptr, NULL) != 0) { 260*6dc98349SAndy Fiddaman goto done; 261*6dc98349SAndy Fiddaman } 2624c87aefeSPatrick Mooney 2634c87aefeSPatrick Mooney /* Read 'romfile' into the guest address space */ 264*6dc98349SAndy Fiddaman for (i = 0; i < rom_size / PAGE_SIZE; i++) { 2654c87aefeSPatrick Mooney rlen = read(fd, ptr + i * PAGE_SIZE, PAGE_SIZE); 2664c87aefeSPatrick Mooney if (rlen != PAGE_SIZE) { 267154972afSPatrick Mooney EPRINTLN("Incomplete read of page %d of bootrom " 268154972afSPatrick Mooney "file %s: %ld bytes", i, romfile, rlen); 2694c87aefeSPatrick Mooney goto done; 2704c87aefeSPatrick Mooney } 2714c87aefeSPatrick Mooney } 272*6dc98349SAndy Fiddaman 273*6dc98349SAndy Fiddaman if (varfd >= 0) { 274*6dc98349SAndy Fiddaman #ifdef __FreeBSD__ 275*6dc98349SAndy Fiddaman var.mmap = mmap(NULL, var_size, PROT_READ | PROT_WRITE, 276*6dc98349SAndy Fiddaman MAP_SHARED, varfd, 0); 277*6dc98349SAndy Fiddaman #else 278*6dc98349SAndy Fiddaman var.mmap = (uint8_t *)mmap(NULL, var_size, 279*6dc98349SAndy Fiddaman PROT_READ | PROT_WRITE, MAP_SHARED, varfd, 0); 280*6dc98349SAndy Fiddaman #endif 281*6dc98349SAndy Fiddaman if (var.mmap == MAP_FAILED) 282*6dc98349SAndy Fiddaman goto done; 283*6dc98349SAndy Fiddaman var.size = var_size; 284*6dc98349SAndy Fiddaman var.gpa = (gpa_alloctop - var_size) + 1; 285*6dc98349SAndy Fiddaman gpa_alloctop = var.gpa - 1; 286*6dc98349SAndy Fiddaman rv = register_mem(&(struct mem_range){ 287*6dc98349SAndy Fiddaman .name = "bootrom variable", 288*6dc98349SAndy Fiddaman .flags = MEM_F_RW, 289*6dc98349SAndy Fiddaman .handler = bootrom_var_mem_handler, 290*6dc98349SAndy Fiddaman .base = var.gpa, 291*6dc98349SAndy Fiddaman .size = var.size, 292*6dc98349SAndy Fiddaman }); 293*6dc98349SAndy Fiddaman if (rv != 0) 294*6dc98349SAndy Fiddaman goto done; 295*6dc98349SAndy Fiddaman } 296*6dc98349SAndy Fiddaman 2974c87aefeSPatrick Mooney rv = 0; 2984c87aefeSPatrick Mooney done: 2994c87aefeSPatrick Mooney if (fd >= 0) 3004c87aefeSPatrick Mooney close(fd); 3014c87aefeSPatrick Mooney return (rv); 3024c87aefeSPatrick Mooney } 303