14d1e669cSPeter Grehan /*- 21de7b4b8SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 31de7b4b8SPedro F. Giffuni * 44d1e669cSPeter Grehan * Copyright (c) 2012 NetApp, Inc. 54d1e669cSPeter Grehan * All rights reserved. 64d1e669cSPeter Grehan * 74d1e669cSPeter Grehan * Redistribution and use in source and binary forms, with or without 84d1e669cSPeter Grehan * modification, are permitted provided that the following conditions 94d1e669cSPeter Grehan * are met: 104d1e669cSPeter Grehan * 1. Redistributions of source code must retain the above copyright 114d1e669cSPeter Grehan * notice, this list of conditions and the following disclaimer. 124d1e669cSPeter Grehan * 2. Redistributions in binary form must reproduce the above copyright 134d1e669cSPeter Grehan * notice, this list of conditions and the following disclaimer in the 144d1e669cSPeter Grehan * documentation and/or other materials provided with the distribution. 154d1e669cSPeter Grehan * 164d1e669cSPeter Grehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 174d1e669cSPeter Grehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 184d1e669cSPeter Grehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 194d1e669cSPeter Grehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 204d1e669cSPeter Grehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 214d1e669cSPeter Grehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 224d1e669cSPeter Grehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 234d1e669cSPeter Grehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 244d1e669cSPeter Grehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 254d1e669cSPeter Grehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 264d1e669cSPeter Grehan * SUCH DAMAGE. 274d1e669cSPeter Grehan * 284d1e669cSPeter Grehan * $FreeBSD$ 294d1e669cSPeter Grehan */ 304d1e669cSPeter Grehan 314d1e669cSPeter Grehan /* 324d1e669cSPeter Grehan * Memory ranges are represented with an RB tree. On insertion, the range 334d1e669cSPeter Grehan * is checked for overlaps. On lookup, the key has the same base and limit 344d1e669cSPeter Grehan * so it can be searched within the range. 354d1e669cSPeter Grehan */ 364d1e669cSPeter Grehan 374d1e669cSPeter Grehan #include <sys/cdefs.h> 384d1e669cSPeter Grehan __FBSDID("$FreeBSD$"); 394d1e669cSPeter Grehan 404d1e669cSPeter Grehan #include <sys/types.h> 414d1e669cSPeter Grehan #include <sys/errno.h> 42*f2b5dc3aSMarcelo Araujo #include <sys/tree.h> 434d1e669cSPeter Grehan #include <machine/vmm.h> 44e813a873SNeel Natu #include <machine/vmm_instruction_emul.h> 454d1e669cSPeter Grehan 46*f2b5dc3aSMarcelo Araujo #include <assert.h> 47*f2b5dc3aSMarcelo Araujo #include <err.h> 48*f2b5dc3aSMarcelo Araujo #include <pthread.h> 494d1e669cSPeter Grehan #include <stdio.h> 504d1e669cSPeter Grehan #include <stdlib.h> 514d1e669cSPeter Grehan 524d1e669cSPeter Grehan #include "mem.h" 534d1e669cSPeter Grehan 544d1e669cSPeter Grehan struct mmio_rb_range { 554d1e669cSPeter Grehan RB_ENTRY(mmio_rb_range) mr_link; /* RB tree links */ 564d1e669cSPeter Grehan struct mem_range mr_param; 574d1e669cSPeter Grehan uint64_t mr_base; 584d1e669cSPeter Grehan uint64_t mr_end; 594d1e669cSPeter Grehan }; 604d1e669cSPeter Grehan 614d1e669cSPeter Grehan struct mmio_rb_tree; 624d1e669cSPeter Grehan RB_PROTOTYPE(mmio_rb_tree, mmio_rb_range, mr_link, mmio_rb_range_compare); 634d1e669cSPeter Grehan 640ab13648SPeter Grehan RB_HEAD(mmio_rb_tree, mmio_rb_range) mmio_rb_root, mmio_rb_fallback; 654d1e669cSPeter Grehan 664d1e669cSPeter Grehan /* 674d1e669cSPeter Grehan * Per-vCPU cache. Since most accesses from a vCPU will be to 684d1e669cSPeter Grehan * consecutive addresses in a range, it makes sense to cache the 694d1e669cSPeter Grehan * result of a lookup. 704d1e669cSPeter Grehan */ 714d1e669cSPeter Grehan static struct mmio_rb_range *mmio_hint[VM_MAXCPU]; 724d1e669cSPeter Grehan 73ae551da6SNeel Natu static pthread_rwlock_t mmio_rwlock; 74028d9311SNeel Natu 754d1e669cSPeter Grehan static int 764d1e669cSPeter Grehan mmio_rb_range_compare(struct mmio_rb_range *a, struct mmio_rb_range *b) 774d1e669cSPeter Grehan { 784d1e669cSPeter Grehan if (a->mr_end < b->mr_base) 794d1e669cSPeter Grehan return (-1); 804d1e669cSPeter Grehan else if (a->mr_base > b->mr_end) 814d1e669cSPeter Grehan return (1); 824d1e669cSPeter Grehan return (0); 834d1e669cSPeter Grehan } 844d1e669cSPeter Grehan 854d1e669cSPeter Grehan static int 860ab13648SPeter Grehan mmio_rb_lookup(struct mmio_rb_tree *rbt, uint64_t addr, 870ab13648SPeter Grehan struct mmio_rb_range **entry) 884d1e669cSPeter Grehan { 894d1e669cSPeter Grehan struct mmio_rb_range find, *res; 904d1e669cSPeter Grehan 914d1e669cSPeter Grehan find.mr_base = find.mr_end = addr; 924d1e669cSPeter Grehan 930ab13648SPeter Grehan res = RB_FIND(mmio_rb_tree, rbt, &find); 944d1e669cSPeter Grehan 954d1e669cSPeter Grehan if (res != NULL) { 964d1e669cSPeter Grehan *entry = res; 974d1e669cSPeter Grehan return (0); 984d1e669cSPeter Grehan } 994d1e669cSPeter Grehan 1004d1e669cSPeter Grehan return (ENOENT); 1014d1e669cSPeter Grehan } 1024d1e669cSPeter Grehan 1034d1e669cSPeter Grehan static int 1040ab13648SPeter Grehan mmio_rb_add(struct mmio_rb_tree *rbt, struct mmio_rb_range *new) 1054d1e669cSPeter Grehan { 1064d1e669cSPeter Grehan struct mmio_rb_range *overlap; 1074d1e669cSPeter Grehan 1080ab13648SPeter Grehan overlap = RB_INSERT(mmio_rb_tree, rbt, new); 1094d1e669cSPeter Grehan 1104d1e669cSPeter Grehan if (overlap != NULL) { 1114d1e669cSPeter Grehan #ifdef RB_DEBUG 1124d1e669cSPeter Grehan printf("overlap detected: new %lx:%lx, tree %lx:%lx\n", 1134d1e669cSPeter Grehan new->mr_base, new->mr_end, 1144d1e669cSPeter Grehan overlap->mr_base, overlap->mr_end); 1154d1e669cSPeter Grehan #endif 1164d1e669cSPeter Grehan 1174d1e669cSPeter Grehan return (EEXIST); 1184d1e669cSPeter Grehan } 1194d1e669cSPeter Grehan 1204d1e669cSPeter Grehan return (0); 1214d1e669cSPeter Grehan } 1224d1e669cSPeter Grehan 1234d1e669cSPeter Grehan #if 0 1244d1e669cSPeter Grehan static void 1250ab13648SPeter Grehan mmio_rb_dump(struct mmio_rb_tree *rbt) 1264d1e669cSPeter Grehan { 1275f4c83abSMarcelo Araujo int perror; 1284d1e669cSPeter Grehan struct mmio_rb_range *np; 1294d1e669cSPeter Grehan 130ae551da6SNeel Natu pthread_rwlock_rdlock(&mmio_rwlock); 1310ab13648SPeter Grehan RB_FOREACH(np, mmio_rb_tree, rbt) { 1324d1e669cSPeter Grehan printf(" %lx:%lx, %s\n", np->mr_base, np->mr_end, 1334d1e669cSPeter Grehan np->mr_param.name); 1344d1e669cSPeter Grehan } 1355f4c83abSMarcelo Araujo perror = pthread_rwlock_unlock(&mmio_rwlock); 1365f4c83abSMarcelo Araujo assert(perror == 0); 1374d1e669cSPeter Grehan } 1384d1e669cSPeter Grehan #endif 1394d1e669cSPeter Grehan 1404d1e669cSPeter Grehan RB_GENERATE(mmio_rb_tree, mmio_rb_range, mr_link, mmio_rb_range_compare); 1414d1e669cSPeter Grehan 142cd377eb3SJohn Baldwin typedef int (mem_cb_t)(struct vmctx *ctx, int vcpu, uint64_t gpa, 143cd377eb3SJohn Baldwin struct mem_range *mr, void *arg); 144cd377eb3SJohn Baldwin 145ba9b7bf7SNeel Natu static int 146ba9b7bf7SNeel Natu mem_read(void *ctx, int vcpu, uint64_t gpa, uint64_t *rval, int size, void *arg) 147ba9b7bf7SNeel Natu { 148ba9b7bf7SNeel Natu int error; 149ba9b7bf7SNeel Natu struct mem_range *mr = arg; 150ba9b7bf7SNeel Natu 151ba9b7bf7SNeel Natu error = (*mr->handler)(ctx, vcpu, MEM_F_READ, gpa, size, 152ba9b7bf7SNeel Natu rval, mr->arg1, mr->arg2); 153ba9b7bf7SNeel Natu return (error); 154ba9b7bf7SNeel Natu } 155ba9b7bf7SNeel Natu 156ba9b7bf7SNeel Natu static int 157ba9b7bf7SNeel Natu mem_write(void *ctx, int vcpu, uint64_t gpa, uint64_t wval, int size, void *arg) 158ba9b7bf7SNeel Natu { 159ba9b7bf7SNeel Natu int error; 160ba9b7bf7SNeel Natu struct mem_range *mr = arg; 161ba9b7bf7SNeel Natu 162ba9b7bf7SNeel Natu error = (*mr->handler)(ctx, vcpu, MEM_F_WRITE, gpa, size, 163ba9b7bf7SNeel Natu &wval, mr->arg1, mr->arg2); 164ba9b7bf7SNeel Natu return (error); 165ba9b7bf7SNeel Natu } 166ba9b7bf7SNeel Natu 167cd377eb3SJohn Baldwin static int 168cd377eb3SJohn Baldwin access_memory(struct vmctx *ctx, int vcpu, uint64_t paddr, mem_cb_t *cb, 169cd377eb3SJohn Baldwin void *arg) 1704d1e669cSPeter Grehan { 1714d1e669cSPeter Grehan struct mmio_rb_range *entry; 1725f4c83abSMarcelo Araujo int err, perror, immutable; 1734d1e669cSPeter Grehan 174ae551da6SNeel Natu pthread_rwlock_rdlock(&mmio_rwlock); 1754d1e669cSPeter Grehan /* 1764d1e669cSPeter Grehan * First check the per-vCPU cache 1774d1e669cSPeter Grehan */ 1784d1e669cSPeter Grehan if (mmio_hint[vcpu] && 1794d1e669cSPeter Grehan paddr >= mmio_hint[vcpu]->mr_base && 1804d1e669cSPeter Grehan paddr <= mmio_hint[vcpu]->mr_end) { 181ba9b7bf7SNeel Natu entry = mmio_hint[vcpu]; 182ba9b7bf7SNeel Natu } else 183ba9b7bf7SNeel Natu entry = NULL; 184ba9b7bf7SNeel Natu 185ba9b7bf7SNeel Natu if (entry == NULL) { 186028d9311SNeel Natu if (mmio_rb_lookup(&mmio_rb_root, paddr, &entry) == 0) { 187ba9b7bf7SNeel Natu /* Update the per-vCPU cache */ 1884d1e669cSPeter Grehan mmio_hint[vcpu] = entry; 1890ab13648SPeter Grehan } else if (mmio_rb_lookup(&mmio_rb_fallback, paddr, &entry)) { 1905f4c83abSMarcelo Araujo perror = pthread_rwlock_unlock(&mmio_rwlock); 1915f4c83abSMarcelo Araujo assert(perror == 0); 1920ab13648SPeter Grehan return (ESRCH); 1930ab13648SPeter Grehan } 1944d1e669cSPeter Grehan } 1954d1e669cSPeter Grehan 1960ab13648SPeter Grehan assert(entry != NULL); 19712a6eb99SNeel Natu 19812a6eb99SNeel Natu /* 19912a6eb99SNeel Natu * An 'immutable' memory range is guaranteed to be never removed 20012a6eb99SNeel Natu * so there is no need to hold 'mmio_rwlock' while calling the 20112a6eb99SNeel Natu * handler. 20212a6eb99SNeel Natu * 20312a6eb99SNeel Natu * XXX writes to the PCIR_COMMAND register can cause register_mem() 20412a6eb99SNeel Natu * to be called. If the guest is using PCI extended config space 20512a6eb99SNeel Natu * to modify the PCIR_COMMAND register then register_mem() can 20612a6eb99SNeel Natu * deadlock on 'mmio_rwlock'. However by registering the extended 20712a6eb99SNeel Natu * config space window as 'immutable' the deadlock can be avoided. 20812a6eb99SNeel Natu */ 20912a6eb99SNeel Natu immutable = (entry->mr_param.flags & MEM_F_IMMUTABLE); 2105f4c83abSMarcelo Araujo if (immutable) { 2115f4c83abSMarcelo Araujo perror = pthread_rwlock_unlock(&mmio_rwlock); 2125f4c83abSMarcelo Araujo assert(perror == 0); 2135f4c83abSMarcelo Araujo } 21412a6eb99SNeel Natu 215cd377eb3SJohn Baldwin err = cb(ctx, vcpu, paddr, &entry->mr_param, arg); 21612a6eb99SNeel Natu 2175f4c83abSMarcelo Araujo if (!immutable) { 2185f4c83abSMarcelo Araujo perror = pthread_rwlock_unlock(&mmio_rwlock); 2195f4c83abSMarcelo Araujo assert(perror == 0); 2205f4c83abSMarcelo Araujo } 2215f4c83abSMarcelo Araujo 222028d9311SNeel Natu 2234d1e669cSPeter Grehan return (err); 2244d1e669cSPeter Grehan } 2254d1e669cSPeter Grehan 226cd377eb3SJohn Baldwin struct emulate_mem_args { 227cd377eb3SJohn Baldwin struct vie *vie; 228cd377eb3SJohn Baldwin struct vm_guest_paging *paging; 229cd377eb3SJohn Baldwin }; 230cd377eb3SJohn Baldwin 231cd377eb3SJohn Baldwin static int 232cd377eb3SJohn Baldwin emulate_mem_cb(struct vmctx *ctx, int vcpu, uint64_t paddr, struct mem_range *mr, 233cd377eb3SJohn Baldwin void *arg) 234cd377eb3SJohn Baldwin { 235cd377eb3SJohn Baldwin struct emulate_mem_args *ema; 236cd377eb3SJohn Baldwin 237cd377eb3SJohn Baldwin ema = arg; 238cd377eb3SJohn Baldwin return (vmm_emulate_instruction(ctx, vcpu, paddr, ema->vie, ema->paging, 239cd377eb3SJohn Baldwin mem_read, mem_write, mr)); 240cd377eb3SJohn Baldwin } 241cd377eb3SJohn Baldwin 242cd377eb3SJohn Baldwin int 243cd377eb3SJohn Baldwin emulate_mem(struct vmctx *ctx, int vcpu, uint64_t paddr, struct vie *vie, 244cd377eb3SJohn Baldwin struct vm_guest_paging *paging) 245cd377eb3SJohn Baldwin 246cd377eb3SJohn Baldwin { 247cd377eb3SJohn Baldwin struct emulate_mem_args ema; 248cd377eb3SJohn Baldwin 249cd377eb3SJohn Baldwin ema.vie = vie; 250cd377eb3SJohn Baldwin ema.paging = paging; 251cd377eb3SJohn Baldwin return (access_memory(ctx, vcpu, paddr, emulate_mem_cb, &ema)); 252cd377eb3SJohn Baldwin } 253cd377eb3SJohn Baldwin 254cd377eb3SJohn Baldwin struct read_mem_args { 255cd377eb3SJohn Baldwin uint64_t *rval; 256cd377eb3SJohn Baldwin int size; 257cd377eb3SJohn Baldwin }; 258cd377eb3SJohn Baldwin 259cd377eb3SJohn Baldwin static int 260cd377eb3SJohn Baldwin read_mem_cb(struct vmctx *ctx, int vcpu, uint64_t paddr, struct mem_range *mr, 261cd377eb3SJohn Baldwin void *arg) 262cd377eb3SJohn Baldwin { 263cd377eb3SJohn Baldwin struct read_mem_args *rma; 264cd377eb3SJohn Baldwin 265cd377eb3SJohn Baldwin rma = arg; 266cd377eb3SJohn Baldwin return (mr->handler(ctx, vcpu, MEM_F_READ, paddr, rma->size, 267cd377eb3SJohn Baldwin rma->rval, mr->arg1, mr->arg2)); 268cd377eb3SJohn Baldwin } 269cd377eb3SJohn Baldwin 270cd377eb3SJohn Baldwin int 271cd377eb3SJohn Baldwin read_mem(struct vmctx *ctx, int vcpu, uint64_t gpa, uint64_t *rval, int size) 272cd377eb3SJohn Baldwin { 273cd377eb3SJohn Baldwin struct read_mem_args rma; 274cd377eb3SJohn Baldwin 275cd377eb3SJohn Baldwin rma.rval = rval; 276cd377eb3SJohn Baldwin rma.size = size; 277cd377eb3SJohn Baldwin return (access_memory(ctx, vcpu, gpa, read_mem_cb, &rma)); 278cd377eb3SJohn Baldwin } 279cd377eb3SJohn Baldwin 2800ab13648SPeter Grehan static int 2810ab13648SPeter Grehan register_mem_int(struct mmio_rb_tree *rbt, struct mem_range *memp) 2824d1e669cSPeter Grehan { 283028d9311SNeel Natu struct mmio_rb_range *entry, *mrp; 2845f4c83abSMarcelo Araujo int err, perror; 2854d1e669cSPeter Grehan 2864d1e669cSPeter Grehan err = 0; 2874d1e669cSPeter Grehan 2884d1e669cSPeter Grehan mrp = malloc(sizeof(struct mmio_rb_range)); 289*f2b5dc3aSMarcelo Araujo if (mrp == NULL) { 290*f2b5dc3aSMarcelo Araujo warn("%s: couldn't allocate memory for mrp\n", 291*f2b5dc3aSMarcelo Araujo __func__); 292*f2b5dc3aSMarcelo Araujo err = ENOMEM; 293*f2b5dc3aSMarcelo Araujo } else { 2944d1e669cSPeter Grehan mrp->mr_param = *memp; 2954d1e669cSPeter Grehan mrp->mr_base = memp->base; 2964d1e669cSPeter Grehan mrp->mr_end = memp->base + memp->size - 1; 297ae551da6SNeel Natu pthread_rwlock_wrlock(&mmio_rwlock); 298028d9311SNeel Natu if (mmio_rb_lookup(rbt, memp->base, &entry) != 0) 2990ab13648SPeter Grehan err = mmio_rb_add(rbt, mrp); 3005f4c83abSMarcelo Araujo perror = pthread_rwlock_unlock(&mmio_rwlock); 3015f4c83abSMarcelo Araujo assert(perror == 0); 3024d1e669cSPeter Grehan if (err) 3034d1e669cSPeter Grehan free(mrp); 304*f2b5dc3aSMarcelo Araujo } 3054d1e669cSPeter Grehan 3064d1e669cSPeter Grehan return (err); 3074d1e669cSPeter Grehan } 3084d1e669cSPeter Grehan 3090ab13648SPeter Grehan int 3100ab13648SPeter Grehan register_mem(struct mem_range *memp) 3110ab13648SPeter Grehan { 3120ab13648SPeter Grehan 3130ab13648SPeter Grehan return (register_mem_int(&mmio_rb_root, memp)); 3140ab13648SPeter Grehan } 3150ab13648SPeter Grehan 3160ab13648SPeter Grehan int 3170ab13648SPeter Grehan register_mem_fallback(struct mem_range *memp) 3180ab13648SPeter Grehan { 3190ab13648SPeter Grehan 3200ab13648SPeter Grehan return (register_mem_int(&mmio_rb_fallback, memp)); 3210ab13648SPeter Grehan } 3220ab13648SPeter Grehan 323028d9311SNeel Natu int 324028d9311SNeel Natu unregister_mem(struct mem_range *memp) 325028d9311SNeel Natu { 326028d9311SNeel Natu struct mem_range *mr; 327028d9311SNeel Natu struct mmio_rb_range *entry = NULL; 3285f4c83abSMarcelo Araujo int err, perror, i; 329028d9311SNeel Natu 330ae551da6SNeel Natu pthread_rwlock_wrlock(&mmio_rwlock); 331028d9311SNeel Natu err = mmio_rb_lookup(&mmio_rb_root, memp->base, &entry); 332028d9311SNeel Natu if (err == 0) { 333028d9311SNeel Natu mr = &entry->mr_param; 334028d9311SNeel Natu assert(mr->name == memp->name); 335028d9311SNeel Natu assert(mr->base == memp->base && mr->size == memp->size); 33612a6eb99SNeel Natu assert((mr->flags & MEM_F_IMMUTABLE) == 0); 337028d9311SNeel Natu RB_REMOVE(mmio_rb_tree, &mmio_rb_root, entry); 338028d9311SNeel Natu 339028d9311SNeel Natu /* flush Per-vCPU cache */ 340028d9311SNeel Natu for (i=0; i < VM_MAXCPU; i++) { 341028d9311SNeel Natu if (mmio_hint[i] == entry) 342028d9311SNeel Natu mmio_hint[i] = NULL; 343028d9311SNeel Natu } 344028d9311SNeel Natu } 3455f4c83abSMarcelo Araujo perror = pthread_rwlock_unlock(&mmio_rwlock); 3465f4c83abSMarcelo Araujo assert(perror == 0); 347028d9311SNeel Natu 348028d9311SNeel Natu if (entry) 349028d9311SNeel Natu free(entry); 350028d9311SNeel Natu 351028d9311SNeel Natu return (err); 352028d9311SNeel Natu } 353028d9311SNeel Natu 3544d1e669cSPeter Grehan void 3554d1e669cSPeter Grehan init_mem(void) 3564d1e669cSPeter Grehan { 3574d1e669cSPeter Grehan 3580ab13648SPeter Grehan RB_INIT(&mmio_rb_root); 3590ab13648SPeter Grehan RB_INIT(&mmio_rb_fallback); 360ae551da6SNeel Natu pthread_rwlock_init(&mmio_rwlock, NULL); 3614d1e669cSPeter Grehan } 362