xref: /freebsd/usr.sbin/bhyve/mem.c (revision ba9b7bf73aa2c67604b2cd52e37ca5130f45fd2c)
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