14d1e669cSPeter Grehan /*- 24d1e669cSPeter Grehan * Copyright (c) 2012 NetApp, Inc. 34d1e669cSPeter Grehan * All rights reserved. 44d1e669cSPeter Grehan * 54d1e669cSPeter Grehan * Redistribution and use in source and binary forms, with or without 64d1e669cSPeter Grehan * modification, are permitted provided that the following conditions 74d1e669cSPeter Grehan * are met: 84d1e669cSPeter Grehan * 1. Redistributions of source code must retain the above copyright 94d1e669cSPeter Grehan * notice, this list of conditions and the following disclaimer. 104d1e669cSPeter Grehan * 2. Redistributions in binary form must reproduce the above copyright 114d1e669cSPeter Grehan * notice, this list of conditions and the following disclaimer in the 124d1e669cSPeter Grehan * documentation and/or other materials provided with the distribution. 134d1e669cSPeter Grehan * 144d1e669cSPeter Grehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 154d1e669cSPeter Grehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 164d1e669cSPeter Grehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 174d1e669cSPeter Grehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 184d1e669cSPeter Grehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 194d1e669cSPeter Grehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 204d1e669cSPeter Grehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 214d1e669cSPeter Grehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 224d1e669cSPeter Grehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 234d1e669cSPeter Grehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 244d1e669cSPeter Grehan * SUCH DAMAGE. 254d1e669cSPeter Grehan * 264d1e669cSPeter Grehan * $FreeBSD$ 274d1e669cSPeter Grehan */ 284d1e669cSPeter Grehan 294d1e669cSPeter Grehan /* 304d1e669cSPeter Grehan * Memory ranges are represented with an RB tree. On insertion, the range 314d1e669cSPeter Grehan * is checked for overlaps. On lookup, the key has the same base and limit 324d1e669cSPeter Grehan * so it can be searched within the range. 334d1e669cSPeter Grehan * 344d1e669cSPeter Grehan * It is assumed that all setup of ranges takes place in single-threaded 354d1e669cSPeter Grehan * mode before vCPUs have been started. As such, no locks are used on the 364d1e669cSPeter Grehan * RB tree. If this is no longer the case, then a r/w lock could be used, 374d1e669cSPeter Grehan * with readers on the lookup and a writer if the tree needs to be changed 384d1e669cSPeter Grehan * (and per vCPU caches flushed) 394d1e669cSPeter Grehan */ 404d1e669cSPeter Grehan 414d1e669cSPeter Grehan #include <sys/cdefs.h> 424d1e669cSPeter Grehan __FBSDID("$FreeBSD$"); 434d1e669cSPeter Grehan 444d1e669cSPeter Grehan #include <sys/types.h> 454d1e669cSPeter Grehan #include <sys/tree.h> 464d1e669cSPeter Grehan #include <sys/errno.h> 474d1e669cSPeter Grehan #include <machine/vmm.h> 484d1e669cSPeter Grehan 494d1e669cSPeter Grehan #include <stdio.h> 504d1e669cSPeter Grehan #include <stdlib.h> 514d1e669cSPeter Grehan #include <assert.h> 524d1e669cSPeter Grehan 534d1e669cSPeter Grehan #include "mem.h" 544d1e669cSPeter Grehan 554d1e669cSPeter Grehan struct mmio_rb_range { 564d1e669cSPeter Grehan RB_ENTRY(mmio_rb_range) mr_link; /* RB tree links */ 574d1e669cSPeter Grehan struct mem_range mr_param; 584d1e669cSPeter Grehan uint64_t mr_base; 594d1e669cSPeter Grehan uint64_t mr_end; 604d1e669cSPeter Grehan }; 614d1e669cSPeter Grehan 624d1e669cSPeter Grehan struct mmio_rb_tree; 634d1e669cSPeter Grehan RB_PROTOTYPE(mmio_rb_tree, mmio_rb_range, mr_link, mmio_rb_range_compare); 644d1e669cSPeter Grehan 654d1e669cSPeter Grehan RB_HEAD(mmio_rb_tree, mmio_rb_range) mmio_rbroot; 664d1e669cSPeter Grehan 674d1e669cSPeter Grehan /* 684d1e669cSPeter Grehan * Per-vCPU cache. Since most accesses from a vCPU will be to 694d1e669cSPeter Grehan * consecutive addresses in a range, it makes sense to cache the 704d1e669cSPeter Grehan * result of a lookup. 714d1e669cSPeter Grehan */ 724d1e669cSPeter Grehan static struct mmio_rb_range *mmio_hint[VM_MAXCPU]; 734d1e669cSPeter Grehan 744d1e669cSPeter Grehan static int 754d1e669cSPeter Grehan mmio_rb_range_compare(struct mmio_rb_range *a, struct mmio_rb_range *b) 764d1e669cSPeter Grehan { 774d1e669cSPeter Grehan if (a->mr_end < b->mr_base) 784d1e669cSPeter Grehan return (-1); 794d1e669cSPeter Grehan else if (a->mr_base > b->mr_end) 804d1e669cSPeter Grehan return (1); 814d1e669cSPeter Grehan return (0); 824d1e669cSPeter Grehan } 834d1e669cSPeter Grehan 844d1e669cSPeter Grehan static int 854d1e669cSPeter Grehan mmio_rb_lookup(uint64_t addr, struct mmio_rb_range **entry) 864d1e669cSPeter Grehan { 874d1e669cSPeter Grehan struct mmio_rb_range find, *res; 884d1e669cSPeter Grehan 894d1e669cSPeter Grehan find.mr_base = find.mr_end = addr; 904d1e669cSPeter Grehan 914d1e669cSPeter Grehan res = RB_FIND(mmio_rb_tree, &mmio_rbroot, &find); 924d1e669cSPeter Grehan 934d1e669cSPeter Grehan if (res != NULL) { 944d1e669cSPeter Grehan *entry = res; 954d1e669cSPeter Grehan return (0); 964d1e669cSPeter Grehan } 974d1e669cSPeter Grehan 984d1e669cSPeter Grehan return (ENOENT); 994d1e669cSPeter Grehan } 1004d1e669cSPeter Grehan 1014d1e669cSPeter Grehan static int 1024d1e669cSPeter Grehan mmio_rb_add(struct mmio_rb_range *new) 1034d1e669cSPeter Grehan { 1044d1e669cSPeter Grehan struct mmio_rb_range *overlap; 1054d1e669cSPeter Grehan 1064d1e669cSPeter Grehan overlap = RB_INSERT(mmio_rb_tree, &mmio_rbroot, new); 1074d1e669cSPeter Grehan 1084d1e669cSPeter Grehan if (overlap != NULL) { 1094d1e669cSPeter Grehan #ifdef RB_DEBUG 1104d1e669cSPeter Grehan printf("overlap detected: new %lx:%lx, tree %lx:%lx\n", 1114d1e669cSPeter Grehan new->mr_base, new->mr_end, 1124d1e669cSPeter Grehan overlap->mr_base, overlap->mr_end); 1134d1e669cSPeter Grehan #endif 1144d1e669cSPeter Grehan 1154d1e669cSPeter Grehan return (EEXIST); 1164d1e669cSPeter Grehan } 1174d1e669cSPeter Grehan 1184d1e669cSPeter Grehan return (0); 1194d1e669cSPeter Grehan } 1204d1e669cSPeter Grehan 1214d1e669cSPeter Grehan #if 0 1224d1e669cSPeter Grehan static void 1234d1e669cSPeter Grehan mmio_rb_dump(void) 1244d1e669cSPeter Grehan { 1254d1e669cSPeter Grehan struct mmio_rb_range *np; 1264d1e669cSPeter Grehan 1274d1e669cSPeter Grehan RB_FOREACH(np, mmio_rb_tree, &mmio_rbroot) { 1284d1e669cSPeter Grehan printf(" %lx:%lx, %s\n", np->mr_base, np->mr_end, 1294d1e669cSPeter Grehan np->mr_param.name); 1304d1e669cSPeter Grehan } 1314d1e669cSPeter Grehan } 1324d1e669cSPeter Grehan #endif 1334d1e669cSPeter Grehan 1344d1e669cSPeter Grehan RB_GENERATE(mmio_rb_tree, mmio_rb_range, mr_link, mmio_rb_range_compare); 1354d1e669cSPeter Grehan 136*ba9b7bf7SNeel Natu static int 137*ba9b7bf7SNeel Natu mem_read(void *ctx, int vcpu, uint64_t gpa, uint64_t *rval, int size, void *arg) 138*ba9b7bf7SNeel Natu { 139*ba9b7bf7SNeel Natu int error; 140*ba9b7bf7SNeel Natu struct mem_range *mr = arg; 141*ba9b7bf7SNeel Natu 142*ba9b7bf7SNeel Natu error = (*mr->handler)(ctx, vcpu, MEM_F_READ, gpa, size, 143*ba9b7bf7SNeel Natu rval, mr->arg1, mr->arg2); 144*ba9b7bf7SNeel Natu return (error); 145*ba9b7bf7SNeel Natu } 146*ba9b7bf7SNeel Natu 147*ba9b7bf7SNeel Natu static int 148*ba9b7bf7SNeel Natu mem_write(void *ctx, int vcpu, uint64_t gpa, uint64_t wval, int size, void *arg) 149*ba9b7bf7SNeel Natu { 150*ba9b7bf7SNeel Natu int error; 151*ba9b7bf7SNeel Natu struct mem_range *mr = arg; 152*ba9b7bf7SNeel Natu 153*ba9b7bf7SNeel Natu error = (*mr->handler)(ctx, vcpu, MEM_F_WRITE, gpa, size, 154*ba9b7bf7SNeel Natu &wval, mr->arg1, mr->arg2); 155*ba9b7bf7SNeel Natu return (error); 156*ba9b7bf7SNeel Natu } 157*ba9b7bf7SNeel Natu 1584d1e669cSPeter Grehan int 1594d1e669cSPeter Grehan emulate_mem(struct vmctx *ctx, int vcpu, uint64_t paddr, uint64_t rip, 160*ba9b7bf7SNeel Natu uint64_t cr3, int mode, struct vie *vie) 1614d1e669cSPeter Grehan { 1624d1e669cSPeter Grehan struct mmio_rb_range *entry; 1634d1e669cSPeter Grehan int err; 1644d1e669cSPeter Grehan 1654d1e669cSPeter Grehan /* 1664d1e669cSPeter Grehan * First check the per-vCPU cache 1674d1e669cSPeter Grehan */ 1684d1e669cSPeter Grehan if (mmio_hint[vcpu] && 1694d1e669cSPeter Grehan paddr >= mmio_hint[vcpu]->mr_base && 1704d1e669cSPeter Grehan paddr <= mmio_hint[vcpu]->mr_end) { 171*ba9b7bf7SNeel Natu entry = mmio_hint[vcpu]; 172*ba9b7bf7SNeel Natu } else 173*ba9b7bf7SNeel Natu entry = NULL; 174*ba9b7bf7SNeel Natu 175*ba9b7bf7SNeel Natu if (entry == NULL) { 176*ba9b7bf7SNeel Natu if (mmio_rb_lookup(paddr, &entry)) 177*ba9b7bf7SNeel Natu return (ESRCH); 178*ba9b7bf7SNeel Natu 179*ba9b7bf7SNeel Natu /* Update the per-vCPU cache */ 1804d1e669cSPeter Grehan mmio_hint[vcpu] = entry; 1814d1e669cSPeter Grehan } 1824d1e669cSPeter Grehan 183*ba9b7bf7SNeel Natu assert(entry != NULL && entry == mmio_hint[vcpu]); 184*ba9b7bf7SNeel Natu 185*ba9b7bf7SNeel Natu err = vmm_emulate_instruction(ctx, vcpu, paddr, vie, 186*ba9b7bf7SNeel Natu mem_read, mem_write, &entry->mr_param); 1874d1e669cSPeter Grehan return (err); 1884d1e669cSPeter Grehan } 1894d1e669cSPeter Grehan 1904d1e669cSPeter Grehan int 1914d1e669cSPeter Grehan register_mem(struct mem_range *memp) 1924d1e669cSPeter Grehan { 1934d1e669cSPeter Grehan struct mmio_rb_range *mrp; 1944d1e669cSPeter Grehan int err; 1954d1e669cSPeter Grehan 1964d1e669cSPeter Grehan err = 0; 1974d1e669cSPeter Grehan 1984d1e669cSPeter Grehan mrp = malloc(sizeof(struct mmio_rb_range)); 1994d1e669cSPeter Grehan 2004d1e669cSPeter Grehan if (mrp != NULL) { 2014d1e669cSPeter Grehan mrp->mr_param = *memp; 2024d1e669cSPeter Grehan mrp->mr_base = memp->base; 2034d1e669cSPeter Grehan mrp->mr_end = memp->base + memp->size - 1; 2044d1e669cSPeter Grehan 2054d1e669cSPeter Grehan err = mmio_rb_add(mrp); 2064d1e669cSPeter Grehan if (err) 2074d1e669cSPeter Grehan free(mrp); 2084d1e669cSPeter Grehan } else 2094d1e669cSPeter Grehan err = ENOMEM; 2104d1e669cSPeter Grehan 2114d1e669cSPeter Grehan return (err); 2124d1e669cSPeter Grehan } 2134d1e669cSPeter Grehan 2144d1e669cSPeter Grehan void 2154d1e669cSPeter Grehan init_mem(void) 2164d1e669cSPeter Grehan { 2174d1e669cSPeter Grehan 2184d1e669cSPeter Grehan RB_INIT(&mmio_rbroot); 2194d1e669cSPeter Grehan } 220