diff options
author | Mike Belopuhov <mikeb@cvs.openbsd.org> | 2015-12-22 22:16:54 +0000 |
---|---|---|
committer | Mike Belopuhov <mikeb@cvs.openbsd.org> | 2015-12-22 22:16:54 +0000 |
commit | baec026e1d85d3d6a77263ad1491a627a2ccd8f7 (patch) | |
tree | 28a55d443ceaa4a49b7161d45a7ea1caa4896af1 | |
parent | 52b52cce5d8bdf6b0288e6f304933bba62299408 (diff) |
Implement a bus_dma(9) abstraction on top of Grant Table API
-rw-r--r-- | sys/dev/pv/xen.c | 379 | ||||
-rw-r--r-- | sys/dev/pv/xenreg.h | 126 | ||||
-rw-r--r-- | sys/dev/pv/xenstore.c | 4 | ||||
-rw-r--r-- | sys/dev/pv/xenvar.h | 26 |
4 files changed, 529 insertions, 6 deletions
diff --git a/sys/dev/pv/xen.c b/sys/dev/pv/xen.c index d2e7072ff09..751bc2faa6e 100644 --- a/sys/dev/pv/xen.c +++ b/sys/dev/pv/xen.c @@ -1,4 +1,4 @@ -/* $OpenBSD: xen.c,v 1.18 2015/12/21 19:43:16 mikeb Exp $ */ +/* $OpenBSD: xen.c,v 1.19 2015/12/22 22:16:53 mikeb Exp $ */ /* * Copyright (c) 2015 Mike Belopuhov @@ -44,6 +44,13 @@ int xen_getfeatures(struct xen_softc *); int xen_init_info_page(struct xen_softc *); int xen_init_cbvec(struct xen_softc *); int xen_init_interrupts(struct xen_softc *); +int xen_init_grant_tables(struct xen_softc *); +struct xen_gntent * + xen_grant_table_grow(struct xen_softc *); +int xen_grant_table_alloc(struct xen_softc *, grant_ref_t *); +void xen_grant_table_free(struct xen_softc *, grant_ref_t); +int xen_grant_table_enter(struct xen_softc *, grant_ref_t, paddr_t, int); +void xen_grant_table_remove(struct xen_softc *, grant_ref_t); void xen_disable_emulated_devices(struct xen_softc *); int xen_match(struct device *, void *, void *); @@ -53,6 +60,16 @@ void xen_resume(struct device *); int xen_activate(struct device *, int); int xen_probe_devices(struct xen_softc *); +void xen_bus_dma_init(struct xen_softc *); +int xen_bus_dmamap_create(bus_dma_tag_t, bus_size_t, int, bus_size_t, + bus_size_t, int, bus_dmamap_t *); +void xen_bus_dmamap_destroy(bus_dma_tag_t, bus_dmamap_t); +int xen_bus_dmamap_load(bus_dma_tag_t, bus_dmamap_t, void *, bus_size_t, + struct proc *, int); +int xen_bus_dmamap_load_mbuf(bus_dma_tag_t, bus_dmamap_t, struct mbuf *, + int); +void xen_bus_dmamap_unload(bus_dma_tag_t, bus_dmamap_t); + int xs_attach(struct xen_softc *); struct cfdriver xen_cd = { @@ -63,6 +80,24 @@ const struct cfattach xen_ca = { sizeof(struct xen_softc), xen_match, xen_attach, NULL, xen_activate }; +struct bus_dma_tag xen_bus_dma_tag = { + NULL, + xen_bus_dmamap_create, + xen_bus_dmamap_destroy, + xen_bus_dmamap_load, + xen_bus_dmamap_load_mbuf, + NULL, + NULL, + xen_bus_dmamap_unload, + _bus_dmamap_sync, + _bus_dmamem_alloc, + NULL, + _bus_dmamem_free, + _bus_dmamem_map, + _bus_dmamem_unmap, + NULL, +}; + int xen_match(struct device *parent, void *match, void *aux) { @@ -105,6 +140,9 @@ xen_attach(struct device *parent, struct device *self, void *aux) if (xen_init_interrupts(sc)) return; + if (xen_init_grant_tables(sc)) + return; + if (xs_attach(sc)) return; @@ -732,6 +770,343 @@ xen_intr_unmask(xen_intr_handle_t xih) return (0); } +int +xen_init_grant_tables(struct xen_softc *sc) +{ + struct gnttab_query_size gqs; + struct gnttab_get_version ggv; + struct gnttab_set_version gsv; + int i; + + gqs.dom = DOMID_SELF; + if (xen_hypercall(sc, XC_GNTTAB, 3, GNTTABOP_query_size, &gqs, 1)) { + printf("%s: failed the query for grant table pages\n", + sc->sc_dev.dv_xname); + return (-1); + } + if (gqs.nr_frames == 0 || gqs.nr_frames > gqs.max_nr_frames) { + printf("%s: invalid number of grant table pages: %u/%u\n", + sc->sc_dev.dv_xname, gqs.nr_frames, gqs.max_nr_frames); + return (-1); + } + + DPRINTF("%s: grant table frames allocated %u, max %u\n", + sc->sc_dev.dv_xname, gqs.nr_frames, gqs.max_nr_frames); + + gsv.version = 2; + ggv.dom = DOMID_SELF; + if (xen_hypercall(sc, XC_GNTTAB, 3, GNTTABOP_set_version, &gsv, 1) || + xen_hypercall(sc, XC_GNTTAB, 3, GNTTABOP_get_version, &ggv, 1) || + ggv.version != 2) { + printf("%s: failed to set grant tables API version\n", + sc->sc_dev.dv_xname); + return (-1); + } + + SLIST_INIT(&sc->sc_gnts); + + for (i = 0; i < gqs.nr_frames; i++) + if (xen_grant_table_grow(sc) == NULL) + break; + + sc->sc_gntmax = gqs.max_nr_frames; + + xen_bus_dma_init(sc); + + return (0); +} + +struct xen_gntent * +xen_grant_table_grow(struct xen_softc *sc) +{ + struct xen_add_to_physmap xatp; + struct xen_gntent *ge; + paddr_t pa; + + ge = malloc(sizeof(*ge), M_DEVBUF, M_ZERO | M_NOWAIT); + if (ge == NULL) { + printf("%s: failed to allocate a grant table entry\n", + sc->sc_dev.dv_xname); + return (NULL); + } + ge->ge_table = km_alloc(PAGE_SIZE, &kv_any, &kp_zero, + &kd_nowait); + if (ge->ge_table == NULL) { + free(ge, M_DEVBUF, sizeof(*ge)); + return (NULL); + } + if (!pmap_extract(pmap_kernel(), (vaddr_t)ge->ge_table, &pa)) { + printf("%s: grant table page PA extraction failed\n", + sc->sc_dev.dv_xname); + km_free(ge->ge_table, PAGE_SIZE, &kv_any, &kp_zero); + free(ge, M_DEVBUF, sizeof(*ge)); + return (NULL); + } + xatp.domid = DOMID_SELF; + xatp.idx = sc->sc_gntcnt; + xatp.space = XENMAPSPACE_grant_table; + xatp.gpfn = atop(pa); + if (xen_hypercall(sc, XC_MEMORY, 2, XENMEM_add_to_physmap, &xatp)) { + printf("%s: failed to add a grant table page\n", + sc->sc_dev.dv_xname); + km_free(ge->ge_table, PAGE_SIZE, &kv_any, &kp_zero); + free(ge, M_DEVBUF, sizeof(*ge)); + return (NULL); + } + ge->ge_start = sc->sc_gntcnt * GNTTAB_NEPG; + /* First page has 8 reserved entries */ + ge->ge_reserved = ge->ge_start == 0 ? GNTTAB_NR_RESERVED_ENTRIES : 0; + ge->ge_free = GNTTAB_NEPG - ge->ge_reserved; + ge->ge_next = ge->ge_reserved ? ge->ge_reserved + 1 : 0; + mtx_init(&ge->ge_mtx, IPL_NET); + SLIST_INSERT_HEAD(&sc->sc_gnts, ge, ge_entry); + + DPRINTF("%s: grant table frame %d start %d pa %#lx\n", + sc->sc_dev.dv_xname, sc->sc_gntcnt, ge->ge_start, pa); + + sc->sc_gntcnt++; + + return (ge); +} + +int +xen_grant_table_alloc(struct xen_softc *sc, grant_ref_t *ref) +{ + struct xen_gntent *ge; + int i; + + retry: + SLIST_FOREACH(ge, &sc->sc_gnts, ge_entry) { + if (!ge->ge_free) + continue; + mtx_enter(&ge->ge_mtx); + for (i = ge->ge_next; + /* Math works here because GNTTAB_NEPG is a power of 2 */ + i != ((ge->ge_next + GNTTAB_NEPG - 1) & (GNTTAB_NEPG - 1)); + i++) { + if (i == GNTTAB_NEPG) + i = 0; + if (ge->ge_reserved && i < ge->ge_reserved) + continue; + if (ge->ge_table[i].hdr.flags != GTF_invalid && + ge->ge_table[i].full_page.frame != 0) + continue; + *ref = ge->ge_start + i; + /* XXX Mark as taken */ + ge->ge_table[i].full_page.frame = 0xffffffff; + if ((ge->ge_next = i + 1) == GNTTAB_NEPG) + ge->ge_next = ge->ge_reserved + 1; + ge->ge_free--; + mtx_leave(&ge->ge_mtx); + return (0); + } + mtx_leave(&ge->ge_mtx); + } + + /* We're out of entries, try growing the table */ + if (sc->sc_gntcnt >= sc->sc_gntmax) + return (-1); + + if (xen_grant_table_grow(sc) != NULL) + goto retry; + + return (-1); +} + +void +xen_grant_table_free(struct xen_softc *sc, grant_ref_t ref) +{ + struct xen_gntent *ge; + + SLIST_FOREACH(ge, &sc->sc_gnts, ge_entry) { + if (ref < ge->ge_start || ref > ge->ge_start + GNTTAB_NEPG) + continue; + ref -= ge->ge_start; + mtx_enter(&ge->ge_mtx); + if (ge->ge_table[ref].hdr.flags != GTF_invalid) { + mtx_leave(&ge->ge_mtx); + return; + } + ge->ge_table[ref].full_page.frame = 0; + ge->ge_next = ref; + ge->ge_free++; + mtx_leave(&ge->ge_mtx); + } +} + +int +xen_grant_table_enter(struct xen_softc *sc, grant_ref_t ref, paddr_t pa, + int flags) +{ + struct xen_gntent *ge; + + SLIST_FOREACH(ge, &sc->sc_gnts, ge_entry) { + if (ref < ge->ge_start || ref > ge->ge_start + GNTTAB_NEPG) + continue; + ref -= ge->ge_start; + mtx_enter(&ge->ge_mtx); + ge->ge_table[ref].full_page.frame = atop(pa); + ge->ge_table[ref].full_page.hdr.domid = 0; + membar_producer(); + ge->ge_table[ref].full_page.hdr.flags = + GTF_permit_access | flags; + mtx_leave(&ge->ge_mtx); + return (0); + } + return (ENOBUFS); +} + +void +xen_grant_table_remove(struct xen_softc *sc, grant_ref_t ref) +{ + struct xen_gntent *ge; + uint32_t flags, *ptr; + + SLIST_FOREACH(ge, &sc->sc_gnts, ge_entry) { + if (ref < ge->ge_start || ref > ge->ge_start + GNTTAB_NEPG) + continue; + ref -= ge->ge_start; + + mtx_enter(&ge->ge_mtx); + /* Invalidate the grant reference */ + ptr = (uint32_t *)&ge->ge_table[ref].hdr; + flags = (ge->ge_table[ref].hdr.flags & + ~(GTF_reading | GTF_writing)); + while (atomic_cas_uint(ptr, flags, 0) != flags) + CPU_BUSY_CYCLE(); + ge->ge_table[ref].full_page.frame = 0xffffffff; + mtx_leave(&ge->ge_mtx); + break; + } +} + +void +xen_bus_dma_init(struct xen_softc *sc) +{ + xen_bus_dma_tag._cookie = sc; +} + +int +xen_bus_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments, + bus_size_t maxsegsz, bus_size_t boundary, int flags, bus_dmamap_t *dmamp) +{ + struct xen_softc *sc = t->_cookie; + struct xen_gntmap *gm; + int i, error; + + if (maxsegsz < PAGE_SIZE) + return (EINVAL); + + /* Allocate a dma map structure */ + error = _bus_dmamap_create(t, size, nsegments, maxsegsz, boundary, + flags, dmamp); + if (error) + return (error); + /* ALlocate an array of grant table pa<->ref maps */ + gm = mallocarray(nsegments, sizeof(struct xen_gntmap), M_DEVBUF, + M_ZERO | ((flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK)); + if (gm == NULL) { + _bus_dmamap_destroy(t, *dmamp); + *dmamp = NULL; + return (ENOMEM); + } + /* Wire it to the dma map */ + (*dmamp)->_dm_cookie = gm; + /* Claim references from the grant table */ + for (i = 0; i < (*dmamp)->_dm_segcnt; i++) { + if (xen_grant_table_alloc(sc, &gm[i].gm_ref)) { + xen_bus_dmamap_destroy(t, *dmamp); + *dmamp = NULL; + return (ENOBUFS); + } + } + return (0); +} + +void +xen_bus_dmamap_destroy(bus_dma_tag_t t, bus_dmamap_t map) +{ + struct xen_softc *sc = t->_cookie; + struct xen_gntmap *gm; + int i; + + gm = map->_dm_cookie; + for (i = 0; i < map->_dm_segcnt; i++) { + if (gm[i].gm_ref == 0) + continue; + xen_grant_table_free(sc, gm[i].gm_ref); + } + free(gm, M_DEVBUF, map->_dm_segcnt * sizeof(struct xen_gntmap)); + _bus_dmamap_destroy(t, map); +} + +int +xen_bus_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf, + bus_size_t buflen, struct proc *p, int flags) +{ + struct xen_softc *sc = t->_cookie; + struct xen_gntmap *gm = map->_dm_cookie; + int i, error; + + error = _bus_dmamap_load(t, map, buf, buflen, p, flags); + if (error) + return (error); + for (i = 0; i < map->dm_nsegs; i++) { + error = xen_grant_table_enter(sc, gm[i].gm_ref, + map->dm_segs[i].ds_addr, flags & BUS_DMA_WRITE ? + GTF_readonly : 0); + if (error) { + xen_bus_dmamap_unload(t, map); + return (error); + } + gm[i].gm_paddr = map->dm_segs[i].ds_addr; + map->dm_segs[i].ds_addr = gm[i].gm_ref; + } + return (0); +} + +int +xen_bus_dmamap_load_mbuf(bus_dma_tag_t t, bus_dmamap_t map, struct mbuf *m0, + int flags) +{ + struct xen_softc *sc = t->_cookie; + struct xen_gntmap *gm = map->_dm_cookie; + int i, error; + + error = _bus_dmamap_load_mbuf(t, map, m0, flags); + if (error) + return (error); + for (i = 0; i < map->dm_nsegs; i++) { + error = xen_grant_table_enter(sc, gm[i].gm_ref, + map->dm_segs[i].ds_addr, flags & BUS_DMA_WRITE ? + GTF_readonly : 0); + if (error) { + xen_bus_dmamap_unload(t, map); + return (error); + } + gm[i].gm_paddr = map->dm_segs[i].ds_addr; + map->dm_segs[i].ds_addr = gm[i].gm_ref; + } + return (0); +} + +void +xen_bus_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map) +{ + struct xen_softc *sc = t->_cookie; + struct xen_gntmap *gm = map->_dm_cookie; + int i; + + for (i = 0; i < map->dm_nsegs; i++) { + if (gm[i].gm_paddr == 0) + continue; + xen_grant_table_remove(sc, gm[i].gm_ref); + map->dm_segs[i].ds_addr = gm[i].gm_paddr; + gm[i].gm_paddr = 0; + } + _bus_dmamap_unload(t, map); +} + static int xen_attach_print(void *aux, const char *name) { @@ -765,6 +1140,7 @@ xen_probe_devices(struct xen_softc *sc) /* Special handling */ if (!strcmp("suspend", (char *)iovp1[i].iov_base)) { xa.xa_parent = sc; + xa.xa_dmat = &xen_bus_dma_tag; strlcpy(xa.xa_name, (char *)iovp1[i].iov_base, sizeof(xa.xa_name)); snprintf(xa.xa_node, sizeof(xa.xa_node), "device/%s", @@ -782,6 +1158,7 @@ xen_probe_devices(struct xen_softc *sc) } for (j = 0; j < iov2_cnt; j++) { xa.xa_parent = sc; + xa.xa_dmat = &xen_bus_dma_tag; strlcpy(xa.xa_name, (char *)iovp1[i].iov_base, sizeof(xa.xa_name)); snprintf(xa.xa_node, sizeof(xa.xa_node), "device/%s/%s", diff --git a/sys/dev/pv/xenreg.h b/sys/dev/pv/xenreg.h index d3a694f3aa8..5bdbbd709dd 100644 --- a/sys/dev/pv/xenreg.h +++ b/sys/dev/pv/xenreg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: xenreg.h,v 1.5 2015/12/12 21:07:45 reyk Exp $ */ +/* $OpenBSD: xenreg.h,v 1.6 2015/12/22 22:16:53 mikeb Exp $ */ /* * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -578,6 +578,130 @@ struct evtchn_unmask { /* operation as Dom0 is supported */ #define XENFEAT_dom0 11 + +/* + * interface/grant_table.h + */ + +/* + * Reference to a grant entry in a specified domain's grant table. + */ +typedef uint32_t grant_ref_t; + +/* + * The first few grant table entries will be preserved across grant table + * version changes and may be pre-populated at domain creation by tools. + */ +#define GNTTAB_NR_RESERVED_ENTRIES 8 + +/* + * Type of grant entry. + * GTF_invalid: This grant entry grants no privileges. + * GTF_permit_access: Allow @domid to map/access @frame. + * GTF_accept_transfer: Allow @domid to transfer ownership of one page frame + * to this guest. Xen writes the page number to @frame. + * GTF_transitive: Allow @domid to transitively access a subrange of + * @trans_grant in @trans_domid. No mappings are allowed. + */ +#define GTF_invalid (0<<0) +#define GTF_permit_access (1<<0) +#define GTF_accept_transfer (2<<0) +#define GTF_transitive (3<<0) +#define GTF_type_mask (3<<0) + +/* + * Subflags for GTF_permit_access. + * GTF_readonly: Restrict @domid to read-only mappings and accesses. [GST] + * GTF_reading: Grant entry is currently mapped for reading by @domid. [XEN] + * GTF_writing: Grant entry is currently mapped for writing by @domid. [XEN] + * GTF_PAT, GTF_PWT, GTF_PCD: (x86) cache attribute flags for the grant [GST] + * GTF_sub_page: Grant access to only a subrange of the page. @domid + * will only be allowed to copy from the grant, and not + * map it. [GST] + */ +#define GTF_readonly (1<<2) +#define GTF_reading (1<<3) +#define GTF_writing (1<<4) +#define GTF_PWT (1<<5) +#define GTF_PCD (1<<6) +#define GTF_PAT (1<<7) +#define GTF_sub_page (1<<8) + +struct grant_entry_header { + uint16_t flags; + domid_t domid; +}; + +typedef union grant_entry { + struct grant_entry_header hdr; + + /* + * This member is used for V1-style full page grants, where either: + * + * -- hdr.type is GTF_accept_transfer, or + * -- hdr.type is GTF_permit_access and GTF_sub_page is not set. + * + * In that case, the frame field has the same semantics as the + * field of the same name in the V1 entry structure. + */ + struct { + struct grant_entry_header hdr; + uint32_t pad0; + uint64_t frame; + } full_page; + + uint32_t __spacer[4]; /* Pad to a power of two */ +} grant_entry_t; + +/* Number of grant table entries per memory page */ +#define GNTTAB_NEPG (PAGE_SIZE / sizeof(grant_entry_t)) + +#define GNTTABOP_query_size 6 +#define GNTTABOP_unmap_and_replace 7 +#define GNTTABOP_set_version 8 +#define GNTTABOP_get_version 10 + +/* + * GNTTABOP_query_size: Query the current and maximum sizes of the shared + * grant table. + * NOTES: + * 1. <dom> may be specified as DOMID_SELF. + * 2. Only a sufficiently-privileged domain may specify <dom> != DOMID_SELF. + */ +struct gnttab_query_size { + /* IN parameters. */ + domid_t dom; + /* OUT parameters. */ + uint32_t nr_frames; + uint32_t max_nr_frames; + int16_t status; /* => enum grant_status */ +}; + +/* + * GNTTABOP_set_version: Request a particular version of the grant + * table shared table structure. This operation can only be performed + * once in any given domain. It must be performed before any grants + * are activated; otherwise, the domain will be stuck with version 1. + * The only defined versions are 1 and 2. + */ +struct gnttab_set_version { + /* IN/OUT parameters */ + uint32_t version; +}; + +/* + * GNTTABOP_get_version: Get the grant table version which is in + * effect for domain <dom>. + */ +struct gnttab_get_version { + /* IN parameters */ + domid_t dom; + uint16_t pad; + /* OUT parameters */ + uint32_t version; +}; + + /* * interface/memory.h * diff --git a/sys/dev/pv/xenstore.c b/sys/dev/pv/xenstore.c index 53e67283cc4..45d1664be9d 100644 --- a/sys/dev/pv/xenstore.c +++ b/sys/dev/pv/xenstore.c @@ -1,4 +1,4 @@ -/* $OpenBSD: xenstore.c,v 1.9 2015/12/21 18:17:36 mikeb Exp $ */ +/* $OpenBSD: xenstore.c,v 1.10 2015/12/22 22:16:53 mikeb Exp $ */ /* * Copyright (c) 2015 Mike Belopuhov @@ -24,6 +24,8 @@ #include <sys/device.h> #include <sys/mutex.h> +#include <machine/bus.h> + #include <uvm/uvm_extern.h> #include <dev/pv/xenreg.h> diff --git a/sys/dev/pv/xenvar.h b/sys/dev/pv/xenvar.h index 6b0c136c58f..2d693d5eb07 100644 --- a/sys/dev/pv/xenvar.h +++ b/sys/dev/pv/xenvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: xenvar.h,v 1.15 2015/12/21 19:43:16 mikeb Exp $ */ +/* $OpenBSD: xenvar.h,v 1.16 2015/12/22 22:16:53 mikeb Exp $ */ /* * Copyright (c) 2015 Mike Belopuhov @@ -33,8 +33,23 @@ struct xen_intsrc { void *xi_arg; struct evcount xi_evcnt; evtchn_port_t xi_port; - int xi_noclose; - int xi_masked; + short xi_noclose; + short xi_masked; +}; + +struct xen_gntent { + SLIST_ENTRY(xen_gntent) ge_entry; + grant_entry_t *ge_table; + grant_ref_t ge_start; + short ge_reserved; + short ge_next; + short ge_free; + struct mutex ge_mtx; +}; + +struct xen_gntmap { + grant_ref_t gm_ref; + paddr_t gm_paddr; }; struct xen_softc { @@ -51,6 +66,10 @@ struct xen_softc { struct evcount sc_evcnt; /* upcall counter */ SLIST_HEAD(, xen_intsrc) sc_intrs; + SLIST_HEAD(, xen_gntent) sc_gnts; /* grant table entries */ + int sc_gntcnt; /* current number of pages */ + int sc_gntmax; /* maximum number of pages */ + /* * Xenstore */ @@ -64,6 +83,7 @@ struct xen_attach_args { char xa_name[16]; char xa_node[64]; char xa_backend[128]; + bus_dma_tag_t xa_dmat; }; /* |