diff --git a/boards/arm64/xenvm/xenvm_defconfig b/boards/arm64/xenvm/xenvm_defconfig index 61c99421c8..9bd694fff1 100644 --- a/boards/arm64/xenvm/xenvm_defconfig +++ b/boards/arm64/xenvm/xenvm_defconfig @@ -5,6 +5,7 @@ CONFIG_BOARD_XENVM=y CONFIG_SERIAL=y CONFIG_MAX_XLAT_TABLES=10 +CONFIG_HEAP_MEM_POOL_SIZE=16384 # Enable console CONFIG_CONSOLE=y diff --git a/drivers/Kconfig b/drivers/Kconfig index d2ce721b17..1001e2ba71 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -143,4 +143,6 @@ source "drivers/mipi_dsi/Kconfig" source "drivers/coredump/Kconfig" +source "drivers/xen/Kconfig" + endmenu diff --git a/drivers/xen/CMakeLists.txt b/drivers/xen/CMakeLists.txt index 2060ee25e1..fe5a679fd9 100644 --- a/drivers/xen/CMakeLists.txt +++ b/drivers/xen/CMakeLists.txt @@ -1,5 +1,6 @@ # SPDX-License-Identifier: Apache-2.0 -# Copyright (c) 2021 EPAM Systems +# Copyright (c) 2021-2022 EPAM Systems zephyr_sources(hvm.c) zephyr_sources(events.c) +zephyr_sources_ifdef(CONFIG_XEN_GRANT_TABLE gnttab.c) diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig new file mode 100644 index 0000000000..547b3a7f9e --- /dev/null +++ b/drivers/xen/Kconfig @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) 2022 EPAM Systems + +if SOC_XENVM + +menu "Xen drivers" + +config XEN_GRANT_TABLE + bool "Xen grant table driver" + depends on HEAP_MEM_POOL_SIZE > 0 + default y + help + Xen grant table driver. Please note that driver uses dynamic memory + allocation with k_malloc(), so CONFIG_HEAP_MEM_POOL_SIZE should be + >= number of pages, that you want to alloc and grant or foreign frames + that you want to map. + +config XEN_GRANT_TABLE_INIT_PRIORITY + int "Grant table driver init priority" + depends on XEN_GRANT_TABLE + default 50 + +endmenu + +endif # SOC_XENVM diff --git a/drivers/xen/gnttab.c b/drivers/xen/gnttab.c new file mode 100644 index 0000000000..ccdadf02c1 --- /dev/null +++ b/drivers/xen/gnttab.c @@ -0,0 +1,333 @@ +/* SPDX-License-Identifier: MIT */ +/* + **************************************************************************** + * (C) 2006 - Cambridge University + * (C) 2021-2022 - EPAM Systems + **************************************************************************** + * + * File: gnttab.c + * Author: Steven Smith (sos22@cam.ac.uk) + * Changes: Grzegorz Milos (gm281@cam.ac.uk) + * + * Date: July 2006 + * + * Environment: Xen Minimal OS + * Description: Simple grant tables implementation. About as stupid as it's + * possible to be and still work. + * + **************************************************************************** + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +LOG_MODULE_REGISTER(xen_gnttab); + +/* Timeout for grant table ops retrying */ +#define GOP_RETRY_DELAY 200 + +/* NR_GRANT_FRAMES must be less than or equal to that configured in Xen */ +#define NR_GRANT_FRAMES 1 +#define NR_GRANT_ENTRIES \ + (NR_GRANT_FRAMES * XEN_PAGE_SIZE / sizeof(grant_entry_v1_t)) + +static struct gnttab { + struct k_sem sem; + grant_entry_v1_t *table; + grant_ref_t gref_list[NR_GRANT_ENTRIES]; +} gnttab; + +static grant_ref_t get_free_entry(void) +{ + grant_ref_t gref; + unsigned int flags; + + k_sem_take(&gnttab.sem, K_FOREVER); + + flags = irq_lock(); + gref = gnttab.gref_list[0]; + __ASSERT((gref >= GNTTAB_NR_RESERVED_ENTRIES && + gref < NR_GRANT_ENTRIES), "Invalid gref = %d", gref); + gnttab.gref_list[0] = gnttab.gref_list[gref]; + irq_unlock(flags); + + return gref; +} + +static void put_free_entry(grant_ref_t gref) +{ + unsigned int flags; + + flags = irq_lock(); + gnttab.gref_list[gref] = gnttab.gref_list[0]; + gnttab.gref_list[0] = gref; + + irq_unlock(flags); + + k_sem_give(&gnttab.sem); +} + +static void gnttab_grant_permit_access(grant_ref_t gref, domid_t domid, + unsigned long gfn, bool readonly) +{ + uint16_t flags = GTF_permit_access; + + if (readonly) { + flags |= GTF_readonly; + } + + gnttab.table[gref].frame = gfn; + gnttab.table[gref].domid = domid; + /* Need to be sure that gfn and domid will be set before flags */ + __DMB(); + + gnttab.table[gref].flags = flags; +} + +grant_ref_t gnttab_grant_access(domid_t domid, unsigned long gfn, + bool readonly) +{ + grant_ref_t gref = get_free_entry(); + + gnttab_grant_permit_access(gref, domid, gfn, readonly); + + return gref; +} + +/* Reset flags to zero in order to stop using the grant */ +static int gnttab_reset_flags(grant_ref_t gref) +{ + uint16_t flags, nflags; + uint16_t *pflags; + + pflags = &gnttab.table[gref].flags; + nflags = *pflags; + + do { + flags = nflags; + if (flags & (GTF_reading | GTF_writing)) { + LOG_WRN("gref = %u still in use! (0x%x)\n", + gref, flags); + return 1; + } + nflags = synch_cmpxchg(pflags, flags, 0); + } while (nflags != flags); + + return 0; +} + +int gnttab_end_access(grant_ref_t gref) +{ + int rc; + + __ASSERT((gref >= GNTTAB_NR_RESERVED_ENTRIES && + gref < NR_GRANT_ENTRIES), "Invalid gref = %d", gref); + + rc = gnttab_reset_flags(gref); + if (!rc) { + return rc; + } + + put_free_entry(gref); + + return 0; +} + +int32_t gnttab_alloc_and_grant(void **map, bool readonly) +{ + void *page; + unsigned long gfn; + grant_ref_t gref; + + __ASSERT_NO_MSG(map != NULL); + + page = k_aligned_alloc(XEN_PAGE_SIZE, XEN_PAGE_SIZE); + if (page == NULL) { + return -ENOMEM; + } + + gfn = xen_virt_to_gfn(page); + gref = gnttab_grant_access(0, gfn, readonly); + + *map = page; + + return gref; +} + +static void gop_eagain_retry(int cmd, struct gnttab_map_grant_ref *gref) +{ + unsigned int step = 10, delay = step; + int16_t *status = &gref->status; + + do { + HYPERVISOR_grant_table_op(cmd, gref, 1); + if (*status == GNTST_eagain) + k_sleep(K_MSEC(delay)); + + delay += step; + } while ((*status == GNTST_eagain) && (delay < GOP_RETRY_DELAY)); + + if (delay >= GOP_RETRY_DELAY) { + LOG_ERR("Failed to map grant, timeout reached\n"); + *status = GNTST_bad_page; + } +} + +void *gnttab_get_page(void) +{ + int ret; + void *page_addr; + struct xen_remove_from_physmap rfpm; + + page_addr = k_aligned_alloc(XEN_PAGE_SIZE, XEN_PAGE_SIZE); + if (!page_addr) { + LOG_WRN("Failed to allocate memory for gnttab page!\n"); + return NULL; + } + + rfpm.domid = DOMID_SELF; + rfpm.gpfn = xen_virt_to_gfn(page_addr); + + /* + * GNTTABOP_map_grant_ref will simply replace the entry in the P2M + * and not release any RAM that may have been associated with + * page_addr, so we release this memory before mapping. + */ + ret = HYPERVISOR_memory_op(XENMEM_remove_from_physmap, &rfpm); + if (ret) { + LOG_WRN("Failed to remove gnttab page from physmap, ret = %d\n", ret); + return NULL; + } + + return page_addr; +} + +void gnttab_put_page(void *page_addr) +{ + int ret, nr_extents = 1; + struct xen_memory_reservation reservation; + xen_pfn_t page = xen_virt_to_gfn(page_addr); + + /* + * After unmapping there will be a 4Kb holes in address space + * at 'page_addr' positions. To keep it contiguous and be able + * to return such addresses to memory allocator we need to + * populate memory on unmapped positions here. + */ + memset(&reservation, 0, sizeof(reservation)); + reservation.domid = DOMID_SELF; + reservation.extent_order = 0; + reservation.nr_extents = nr_extents; + set_xen_guest_handle(reservation.extent_start, &page); + + ret = HYPERVISOR_memory_op(XENMEM_populate_physmap, &reservation); + if (ret != nr_extents) { + LOG_WRN("failed to populate physmap on gfn = 0x%llx, ret = %d\n", + page, ret); + return; + } + + k_free(page_addr); +} + +int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops, unsigned int count) +{ + int i, ret; + + ret = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, map_ops, count); + if (ret) { + return ret; + } + + for (i = 0; i < count; i++) { + switch (map_ops[i].status) { + case GNTST_no_device_space: + LOG_WRN("map_grant_ref failed, no device space for page #%d\n", i); + break; + + case GNTST_eagain: + /* Operation not done; need to try again */ + gop_eagain_retry(GNTTABOP_map_grant_ref, &map_ops[i]); + /* Need to re-check status for current page */ + i--; + + break; + + default: + break; + } + } + + return 0; +} + +int gnttab_unmap_refs(struct gnttab_map_grant_ref *unmap_ops, unsigned int count) +{ + return HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, unmap_ops, count); +} + + +static const char * const gnttab_error_msgs[] = GNTTABOP_error_msgs; + +const char *gnttabop_error(int16_t status) +{ + status = -status; + if (status < 0 || (uint16_t) status >= ARRAY_SIZE(gnttab_error_msgs)) { + return "bad status"; + } else { + return gnttab_error_msgs[status]; + } +} + +static int gnttab_init(const struct device *d) +{ + grant_ref_t gref; + struct xen_add_to_physmap xatp; + struct gnttab_setup_table setup; + xen_pfn_t frames[NR_GRANT_FRAMES]; + int rc = 0, i; + + /* Will be taken/given during gnt_refs allocation/release */ + k_sem_init(&gnttab.sem, 0, NR_GRANT_ENTRIES - GNTTAB_NR_RESERVED_ENTRIES); + + for ( + gref = GNTTAB_NR_RESERVED_ENTRIES; + gref < NR_GRANT_ENTRIES; + gref++ + ) { + put_free_entry(gref); + } + + gnttab.table = (grant_entry_v1_t *) + DT_REG_ADDR_BY_IDX(DT_INST(0, xen_xen), 0); + + for (i = 0; i < NR_GRANT_FRAMES; i++) { + xatp.domid = DOMID_SELF; + xatp.size = 0; + xatp.space = XENMAPSPACE_grant_table; + xatp.idx = i; + xatp.gpfn = xen_virt_to_gfn(gnttab.table) + i; + rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp); + __ASSERT(!rc, "add_to_physmap failed; status = %d\n", rc); + } + + setup.dom = DOMID_SELF; + setup.nr_frames = NR_GRANT_FRAMES; + set_xen_guest_handle(setup.frame_list, frames); + rc = HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1); + __ASSERT((!rc) && (!setup.status), "Table setup failed; status = %s\n", + gnttabop_error(setup.status)); + + LOG_DBG("%s: grant table mapped\n", __func__); + + return 0; +} + +SYS_INIT(gnttab_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE); diff --git a/include/zephyr/arch/arm64/hypercall.h b/include/zephyr/arch/arm64/hypercall.h index 6fefdff12b..6aac25a10a 100644 --- a/include/zephyr/arch/arm64/hypercall.h +++ b/include/zephyr/arch/arm64/hypercall.h @@ -1,5 +1,5 @@ /* - * Copyright (c() 2021 EPAM Systems + * Copyright (c) 2021-2022 EPAM Systems * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,3 +10,4 @@ int HYPERVISOR_sched_op(int op, void *param); int HYPERVISOR_event_channel_op(int op, void *param); int HYPERVISOR_hvm_op(int op, void *param); int HYPERVISOR_memory_op(int op, void *param); +int HYPERVISOR_grant_table_op(int op, void *uop, unsigned int count); diff --git a/include/zephyr/xen/generic.h b/include/zephyr/xen/generic.h index 483b7f15e3..756bf80d88 100644 --- a/include/zephyr/xen/generic.h +++ b/include/zephyr/xen/generic.h @@ -11,4 +11,25 @@ #define XEN_PAGE_SIZE 4096 #define XEN_PAGE_SHIFT 12 +#define XEN_PFN_UP(x) (unsigned long)(((x) + XEN_PAGE_SIZE-1) >> XEN_PAGE_SHIFT) +#define XEN_PFN_DOWN(x) (unsigned long)((x) >> XEN_PAGE_SHIFT) +#define XEN_PFN_PHYS(x) ((unsigned long)(x) << XEN_PAGE_SHIFT) +#define XEN_PHYS_PFN(x) (unsigned long)((x) >> XEN_PAGE_SHIFT) + +#define xen_to_phys(x) ((unsigned long) (x)) +#define xen_to_virt(x) ((void *) (x)) + +#define xen_virt_to_gfn(_virt) (XEN_PFN_DOWN(xen_to_phys(_virt))) +#define xen_gfn_to_virt(_gfn) (xen_to_virt(XEN_PFN_PHYS(_gfn))) + +/* + * Atomically exchange value on "ptr" position. If value on "ptr" contains + * "old", then store and return "new". Otherwise, return the "old" value. + */ +#define synch_cmpxchg(ptr, old, new) \ +({ __typeof__(*ptr) stored = old; \ + __atomic_compare_exchange_n(ptr, &stored, new, 0, __ATOMIC_SEQ_CST, \ + __ATOMIC_SEQ_CST) ? new : old; \ +}) + #endif /* __XEN_GENERIC_H__ */ diff --git a/include/zephyr/xen/gnttab.h b/include/zephyr/xen/gnttab.h new file mode 100644 index 0000000000..8616e69540 --- /dev/null +++ b/include/zephyr/xen/gnttab.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2021-2022 EPAM Systems + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef __XEN_GNTTAB_H__ +#define __XEN_GNTTAB_H__ + +#include + +/* + * Assigns gref and permits access to 4K page for specific domain. + * + * @param domid - id of the domain you sharing gref with + * @param gfn - guest frame number of page, where grant will be located + * @param readonly - permit readonly access to shared grant + * @return - gref assigned to shared grant + */ +grant_ref_t gnttab_grant_access(domid_t domid, unsigned long gfn, + bool readonly); + +/* + * Finished access for previously shared grant. Does NOT + * free memory, if it was previously allocated by + * gnttab_alloc_and_grant(). + * + * @param gref - grant reference that need to be closed + * @return - zero on success, non-zero on failure + */ +int gnttab_end_access(grant_ref_t gref); + +/* + * Allocates 4K page for grant and share it via returned + * gref. Need to k_free memory, which was allocated in + * @map parameter after grant releasing. + * + * @param map - double pointer to memory, where grant will be allocated + * @param readonly - permit readonly access to allocated grant + * @return - grant ref on success or negative errno on failure + */ +int32_t gnttab_alloc_and_grant(void **map, bool readonly); + +/* + * Provides interface to acquire free page, that can be used for + * mapping of foreign frames. Should be freed by gnttab_put_page() + * after usage. + * + * @return - pointer to page start address, that can be used as host_addr + * in struct gnttab_map_grant_ref, NULL on error. + */ +void *gnttab_get_page(void); + +/* + * Releases provided page, that was used for mapping foreign grant frame, + * should be called after unmaping. + * + * @param page_addr - pointer to start address of used page. + */ +void gnttab_put_page(void *page_addr); + +/* + * Maps foreign grant ref to Zephyr address space. + * + * @param map_ops - array of prepared gnttab_map_grant_ref's for mapping + * @param count - number of grefs in map_ops array + * @return - zero on success or negative errno on failure + * also per-page status will be set in map_ops[i].status (GNTST_*) + * + * To map foreign frame you need 4K-aligned 4K memory page, which will be + * used as host_addr for grant mapping - it should be acquired by gnttab_get_page() + * function. + */ +int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops, unsigned int count); + +/* + * Unmap foreign grant refs. The gnttab_put_page() should be used after this for + * each page, that was successfully unmapped. + * + * @param unmap_ops - array of prepared gnttab_map_grant_ref's for unmapping + * @param count - number of grefs in unmap_ops array + * @return - @count on success or negative errno on failure + * also per-page status will be set in unmap_ops[i].status (GNTST_*) + */ +int gnttab_unmap_refs(struct gnttab_map_grant_ref *unmap_ops, unsigned int count); + +/* + * Convert grant ref status codes (GNTST_*) to text messages. + * + * @param status - negative GNTST_* code, that needs to be converted + * @return - constant pointer to text message, associated with @status + */ +const char *gnttabop_error(int16_t status); + +#endif /* __XEN_GNTTAB_H__ */ diff --git a/include/zephyr/xen/public/grant_table.h b/include/zephyr/xen/public/grant_table.h new file mode 100644 index 0000000000..bbac2f4552 --- /dev/null +++ b/include/zephyr/xen/public/grant_table.h @@ -0,0 +1,399 @@ +/* SPDX-License-Identifier: MIT */ + +/****************************************************************************** + * grant_table.h + * + * Interface for granting foreign access to page frames, and receiving + * page-ownership transfers. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2004, K A Fraser + */ + +#ifndef __XEN_PUBLIC_GRANT_TABLE_H__ +#define __XEN_PUBLIC_GRANT_TABLE_H__ + +#include "xen.h" + +/* + * `incontents 150 gnttab Grant Tables + * + * Xen's grant tables provide a generic mechanism to memory sharing + * between domains. This shared memory interface underpins the split + * device drivers for block and network IO. + * + * Each domain has its own grant table. This is a data structure that + * is shared with Xen; it allows the domain to tell Xen what kind of + * permissions other domains have on its pages. Entries in the grant + * table are identified by grant references. A grant reference is an + * integer, which indexes into the grant table. It acts as a + * capability which the grantee can use to perform operations on the + * granter's memory. + * + * This capability-based system allows shared-memory communications + * between unprivileged domains. A grant reference also encapsulates + * the details of a shared page, removing the need for a domain to + * know the real machine address of a page it is sharing. This makes + * it possible to share memory correctly with domains running in + * fully virtualised memory. + */ + +/*********************************** + * GRANT TABLE REPRESENTATION + */ + +/* Some rough guidelines on accessing and updating grant-table entries + * in a concurrency-safe manner. For more information, Linux contains a + * reference implementation for guest OSes (drivers/xen/grant_table.c, see + * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=blob;f=drivers/xen/grant-table.c;hb=HEAD + * + * NB. WMB is a no-op on current-generation x86 processors. However, a + * compiler barrier will still be required. + * + * Introducing a valid entry into the grant table: + * 1. Write ent->domid. + * 2. Write ent->frame: + * GTF_permit_access: Frame to which access is permitted. + * GTF_accept_transfer: Pseudo-phys frame slot being filled by new + * frame, or zero if none. + * 3. Write memory barrier (WMB). + * 4. Write ent->flags, inc. valid type. + * + * Invalidating an unused GTF_permit_access entry: + * 1. flags = ent->flags. + * 2. Observe that !(flags & (GTF_reading|GTF_writing)). + * 3. Check result of SMP-safe CMPXCHG(&ent->flags, flags, 0). + * NB. No need for WMB as reuse of entry is control-dependent on success of + * step 3, and all architectures guarantee ordering of ctrl-dep writes. + * + * Invalidating an in-use GTF_permit_access entry: + * This cannot be done directly. Request assistance from the domain controller + * which can set a timeout on the use of a grant entry and take necessary + * action. (NB. This is not yet implemented!). + * + * Invalidating an unused GTF_accept_transfer entry: + * 1. flags = ent->flags. + * 2. Observe that !(flags & GTF_transfer_committed). [*] + * 3. Check result of SMP-safe CMPXCHG(&ent->flags, flags, 0). + * NB. No need for WMB as reuse of entry is control-dependent on success of + * step 3, and all architectures guarantee ordering of ctrl-dep writes. + * [*] If GTF_transfer_committed is set then the grant entry is 'committed'. + * The guest must /not/ modify the grant entry until the address of the + * transferred frame is written. It is safe for the guest to spin waiting + * for this to occur (detect by observing GTF_transfer_completed in + * ent->flags). + * + * Invalidating a committed GTF_accept_transfer entry: + * 1. Wait for (ent->flags & GTF_transfer_completed). + * + * Changing a GTF_permit_access from writable to read-only: + * Use SMP-safe CMPXCHG to set GTF_readonly, while checking !GTF_writing. + * + * Changing a GTF_permit_access from read-only to writable: + * Use SMP-safe bit-setting instruction. + */ + +/* + * Reference to a grant entry in a specified domain's grant table. + */ +typedef uint32_t grant_ref_t; + +/* + * A grant table comprises a packed array of grant entries in one or more + * page frames shared between Xen and a guest. + * [XEN]: This field is written by Xen and read by the sharing guest. + * [GST]: This field is written by the guest and read by Xen. + */ + +/* + * Version 1 of the grant table entry structure is maintained purely + * for backwards compatibility. New guests should use version 2. + */ +#if __XEN_INTERFACE_VERSION__ < 0x0003020a +#define grant_entry_v1 grant_entry +#define grant_entry_v1_t grant_entry_t +#endif +struct grant_entry_v1 { + /* GTF_xxx: various type and flag information. [XEN,GST] */ + uint16_t flags; + /* The domain being granted foreign privileges. [GST] */ + domid_t domid; + /* + * GTF_permit_access: GFN that @domid is allowed to map and access. [GST] + * GTF_accept_transfer: GFN that @domid is allowed to transfer into. [GST] + * GTF_transfer_completed: MFN whose ownership transferred by @domid + * (non-translated guests only). [XEN] + */ + uint32_t frame; +}; +typedef struct grant_entry_v1 grant_entry_v1_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 +#define GNTTAB_RESERVED_CONSOLE 0 +#define GNTTAB_RESERVED_XENSTORE 1 + +/* + * 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 (0U << 0) +#define GTF_permit_access (1U << 0) +#define GTF_accept_transfer (2U << 0) +#define GTF_transitive (3U << 0) +#define GTF_type_mask (3U << 0) + +/* + * Subflags for GTF_permit_access and GTF_transitive. + * 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] + * Further subflags for GTF_permit_access only. + * GTF_PAT, GTF_PWT, GTF_PCD: (x86) cache attribute flags to be used for + * mappings of 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 (2) +#define GTF_readonly (1U << _GTF_readonly) +#define _GTF_reading (3) +#define GTF_reading (1U << _GTF_reading) +#define _GTF_writing (4) +#define GTF_writing (1U << _GTF_writing) +#define _GTF_PWT (5) +#define GTF_PWT (1U << _GTF_PWT) +#define _GTF_PCD (6) +#define GTF_PCD (1U << _GTF_PCD) +#define _GTF_PAT (7) +#define GTF_PAT (1U << _GTF_PAT) +#define _GTF_sub_page (8) +#define GTF_sub_page (1U << _GTF_sub_page) + +/* + * Subflags for GTF_accept_transfer: + * GTF_transfer_committed: Xen sets this flag to indicate that it is committed + * to transferring ownership of a page frame. When a guest sees this flag + * it must /not/ modify the grant entry until GTF_transfer_completed is + * set by Xen. + * GTF_transfer_completed: It is safe for the guest to spin-wait on this flag + * after reading GTF_transfer_committed. Xen will always write the frame + * address, followed by ORing this flag, in a timely manner. + */ +#define _GTF_transfer_committed (2) +#define GTF_transfer_committed (1U << _GTF_transfer_committed) +#define _GTF_transfer_completed (3) +#define GTF_transfer_completed (1U << _GTF_transfer_completed) + +/*********************************** + * GRANT TABLE QUERIES AND USES + */ + +/* ` enum neg_errnoval + * ` HYPERVISOR_grant_table_op(enum grant_table_op cmd, + * ` void *args, + * ` unsigned int count) + * ` + * + * @args points to an array of a per-command data structure. The array + * has @count members + */ + +/* ` enum grant_table_op { // GNTTABOP_* => struct gnttab_* */ +#define GNTTABOP_map_grant_ref 0 +#define GNTTABOP_unmap_grant_ref 1 +#define GNTTABOP_setup_table 2 +#define GNTTABOP_dump_table 3 +#define GNTTABOP_transfer 4 +#define GNTTABOP_copy 5 +#define GNTTABOP_query_size 6 +#define GNTTABOP_unmap_and_replace 7 +#if __XEN_INTERFACE_VERSION__ >= 0x0003020a +#define GNTTABOP_set_version 8 +#define GNTTABOP_get_status_frames 9 +#define GNTTABOP_get_version 10 +#define GNTTABOP_swap_grant_ref 11 +#define GNTTABOP_cache_flush 12 +#endif /* __XEN_INTERFACE_VERSION__ */ +/* ` } */ + +/* + * Handle to track a mapping created via a grant reference. + */ +typedef uint32_t grant_handle_t; + +/* + * GNTTABOP_map_grant_ref: Map the grant entry (,) for access + * by devices and/or host CPUs. If successful, is a tracking number + * that must be presented later to destroy the mapping(s). On error, + * is a negative status code. + * NOTES: + * 1. If GNTMAP_device_map is specified then is the address + * via which I/O devices may access the granted frame. + * 2. If GNTMAP_host_map is specified then a mapping will be added at + * either a host virtual address in the current address space, or at + * a PTE at the specified machine address. The type of mapping to + * perform is selected through the GNTMAP_contains_pte flag, and the + * address is specified in . + * 3. Mappings should only be destroyed via GNTTABOP_unmap_grant_ref. If a + * host mapping is destroyed by other means then it is *NOT* guaranteed + * to be accounted to the correct grant reference! + */ +struct gnttab_map_grant_ref { + /* IN parameters. */ + uint64_t host_addr; + uint32_t flags; /* GNTMAP_* */ + grant_ref_t ref; + domid_t dom; + /* OUT parameters. */ + int16_t status; /* => enum grant_status */ + grant_handle_t handle; + uint64_t dev_bus_addr; +}; +typedef struct gnttab_map_grant_ref gnttab_map_grant_ref_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_map_grant_ref_t); + +/* + * GNTTABOP_unmap_grant_ref: Destroy one or more grant-reference mappings + * tracked by . If or is zero, that + * field is ignored. If non-zero, they must refer to a device/host mapping + * that is tracked by + * NOTES: + * 1. The call may fail in an undefined manner if either mapping is not + * tracked by . + * 3. After executing a batch of unmaps, it is guaranteed that no stale + * mappings will remain in the device or host TLBs. + */ +struct gnttab_unmap_grant_ref { + /* IN parameters. */ + uint64_t host_addr; + uint64_t dev_bus_addr; + grant_handle_t handle; + /* OUT parameters. */ + int16_t status; /* => enum grant_status */ +}; +typedef struct gnttab_unmap_grant_ref gnttab_unmap_grant_ref_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_unmap_grant_ref_t); + +/* + * GNTTABOP_setup_table: Set up a grant table for comprising at least + * pages. The frame addresses are written to the . + * Only addresses are written, even if the table is larger. + * NOTES: + * 1. may be specified as DOMID_SELF. + * 2. Only a sufficiently-privileged domain may specify != DOMID_SELF. + * 3. Xen may not support more than a single grant-table page per domain. + */ +struct gnttab_setup_table { + /* IN parameters. */ + domid_t dom; + uint32_t nr_frames; + + /* OUT parameters. */ + int16_t status; /* => enum grant_status */ +#if __XEN_INTERFACE_VERSION__ < 0x00040300 + XEN_GUEST_HANDLE(ulong) frame_list; +#else + XEN_GUEST_HANDLE(xen_pfn_t) frame_list; +#endif +}; +typedef struct gnttab_setup_table gnttab_setup_table_t; +DEFINE_XEN_GUEST_HANDLE(gnttab_setup_table_t); + + + +/* + * Bitfield values for gnttab_map_grant_ref.flags. + */ + /* Map the grant entry for access by I/O devices. */ +#define _GNTMAP_device_map (0) +#define GNTMAP_device_map (1<<_GNTMAP_device_map) + /* Map the grant entry for access by host CPUs. */ +#define _GNTMAP_host_map (1) +#define GNTMAP_host_map (1<<_GNTMAP_host_map) + /* Accesses to the granted frame will be restricted to read-only access. */ +#define _GNTMAP_readonly (2) +#define GNTMAP_readonly (1<<_GNTMAP_readonly) + /* + * GNTMAP_host_map subflag: + * 0 => The host mapping is usable only by the guest OS. + * 1 => The host mapping is usable by guest OS + current application. + */ +#define _GNTMAP_application_map (3) +#define GNTMAP_application_map (1<<_GNTMAP_application_map) + + /* + * GNTMAP_contains_pte subflag: + * 0 => This map request contains a host virtual address. + * 1 => This map request contains the machine addess of the PTE to update. + */ +#define _GNTMAP_contains_pte (4) +#define GNTMAP_contains_pte (1<<_GNTMAP_contains_pte) + +/* + * Bits to be placed in guest kernel available PTE bits (architecture + * dependent; only supported when XENFEAT_gnttab_map_avail_bits is set). + */ +#define _GNTMAP_guest_avail0 (16) +#define GNTMAP_guest_avail_mask ((uint32_t)~0 << _GNTMAP_guest_avail0) + +/* + * Values for error status returns. All errors are -ve. + */ +/* ` enum grant_status { */ +#define GNTST_okay (0) /* Normal return */ +#define GNTST_general_error (-1) /* General undefined error */ +#define GNTST_bad_domain (-2) /* Unrecognsed domain id */ +#define GNTST_bad_gntref (-3) /* Unrecognised or inappropriate gntref */ +#define GNTST_bad_handle (-4) /* Unrecognised or inappropriate handle */ +#define GNTST_bad_virt_addr (-5) /* Inappropriate virtual address to map */ +#define GNTST_bad_dev_addr (-6) /* Inappropriate device address to unmap */ +#define GNTST_no_device_space (-7) /* Out of space in I/O MMU */ +#define GNTST_permission_denied (-8) /* Not enough privilege for operation */ +#define GNTST_bad_page (-9) /* Specified page was invalid for op */ +#define GNTST_bad_copy_arg (-10) /* copy arguments cross page boundary */ +#define GNTST_address_too_big (-11) /* transfer page address too large */ +#define GNTST_eagain (-12) /* Operation not done; try agains */ +/* ` } */ + +#define GNTTABOP_error_msgs { \ + "okay", \ + "undefined error", \ + "unrecognised domain id", \ + "invalid grant reference", \ + "invalid mapping handle", \ + "invalid virtual address", \ + "invalid device address", \ + "no spare translation slot in the I/O MMU", \ + "permission denied", \ + "bad page", \ + "copy arguments cross page boundary", \ + "page address size too large", \ + "operation not done; try again" \ +} + +#endif /* __XEN_PUBLIC_GRANT_TABLE_H__ */ diff --git a/include/zephyr/xen/public/memory.h b/include/zephyr/xen/public/memory.h index c2a4639749..9496b5f527 100644 --- a/include/zephyr/xen/public/memory.h +++ b/include/zephyr/xen/public/memory.h @@ -31,6 +31,44 @@ #include "xen.h" +#define XENMEM_populate_physmap 6 + +struct xen_memory_reservation { + + /* + * XENMEM_increase_reservation: + * OUT: MFN (*not* GMFN) bases of extents that were allocated + * XENMEM_decrease_reservation: + * IN: GMFN bases of extents to free + * XENMEM_populate_physmap: + * IN: GPFN bases of extents to populate with memory + * OUT: GMFN bases of extents that were allocated + * (NB. This command also updates the mach_to_phys translation table) + * XENMEM_claim_pages: + * IN: must be zero + */ + XEN_GUEST_HANDLE(xen_pfn_t) extent_start; + + /* Number of extents, and size/alignment of each (2^extent_order pages). */ + xen_ulong_t nr_extents; + unsigned int extent_order; + +#if __XEN_INTERFACE_VERSION__ >= 0x00030209 + /* XENMEMF flags. */ + unsigned int mem_flags; +#else + unsigned int address_bits; +#endif + + /* + * Domain whose reservation is being changed. + * Unprivileged domains can specify only DOMID_SELF. + */ + domid_t domid; +}; +typedef struct xen_memory_reservation xen_memory_reservation_t; +DEFINE_XEN_GUEST_HANDLE(xen_memory_reservation_t); + #define XENMAPSPACE_shared_info 0 /* shared info page */ #define XENMAPSPACE_grant_table 1 /* grant table page */ #define XENMAPSPACE_gmfn 2 /* GMFN */ @@ -73,4 +111,20 @@ struct xen_add_to_physmap { typedef struct xen_add_to_physmap xen_add_to_physmap_t; DEFINE_XEN_GUEST_HANDLE(xen_add_to_physmap_t); +/* + * Unmaps the page appearing at a particular GPFN from the specified guest's + * physical address space (translated guests only). + * arg == addr of xen_remove_from_physmap_t. + */ +#define XENMEM_remove_from_physmap 15 +struct xen_remove_from_physmap { + /* Which domain to change the mapping for. */ + domid_t domid; + + /* GPFN of the current mapping of the page. */ + xen_pfn_t gpfn; +}; +typedef struct xen_remove_from_physmap xen_remove_from_physmap_t; +DEFINE_XEN_GUEST_HANDLE(xen_remove_from_physmap_t); + #endif /* __XEN_PUBLIC_MEMORY_H__ */ diff --git a/soc/arm64/xenvm/mmu_regions.c b/soc/arm64/xenvm/mmu_regions.c index 97e6e7cdf8..8f829a799c 100644 --- a/soc/arm64/xenvm/mmu_regions.c +++ b/soc/arm64/xenvm/mmu_regions.c @@ -1,5 +1,5 @@ /* - * Copyright 2020 EPAM Systems + * Copyright (c) 2020-2022 EPAM Systems * * SPDX-License-Identifier: Apache-2.0 */ @@ -18,6 +18,11 @@ static const struct arm_mmu_region mmu_regions[] = { DT_REG_ADDR_BY_IDX(DT_INST(0, arm_gic), 1), DT_REG_SIZE_BY_IDX(DT_INST(0, arm_gic), 1), MT_DEVICE_nGnRnE | MT_P_RW_U_RW | MT_NS), + + MMU_REGION_FLAT_ENTRY("HYPERVISOR", + DT_REG_ADDR_BY_IDX(DT_INST(0, xen_xen), 0), + DT_REG_SIZE_BY_IDX(DT_INST(0, xen_xen), 0), + MT_NORMAL | MT_P_RW_U_RW | MT_NS), }; const struct arm_mmu_config mmu_config = {