Messages in this thread Patch in this message |  | | From | Hans de Goede <> | Subject | [RFC v2 1/2] virt: Add vboxguest driver for Virtual Box Guest integration | Date | Fri, 25 Aug 2017 16:37:31 +0200 |
| |
This commit adds a driver for the Virtual Box Guest PCI device used in Virtual Box virtual machines. Enabling this driver will add support for Virtual Box Guest integration features such as copy-and-paste, seamless mode and OpenGL pass-through.
This driver also offers vboxguest IPC functionality which is needed for the vboxfs driver which offers folder sharing support.
Signed-off-by: Hans de Goede <hdegoede@redhat.com> --- drivers/virt/Kconfig | 1 + drivers/virt/Makefile | 1 + drivers/virt/vboxguest/Kconfig | 16 + drivers/virt/vboxguest/Makefile | 3 + drivers/virt/vboxguest/vboxguest_core.c | 1580 +++++++++++++++++++++++++ drivers/virt/vboxguest/vboxguest_core.h | 194 ++++ drivers/virt/vboxguest/vboxguest_linux.c | 478 ++++++++ drivers/virt/vboxguest/vboxguest_utils.c | 1094 +++++++++++++++++ drivers/virt/vboxguest/vboxguest_version.h | 18 + include/linux/vbox_utils.h | 107 ++ include/linux/vbox_vmmdev.h | 123 ++ include/uapi/linux/vbox_err.h | 178 +++ include/uapi/linux/vbox_ostypes.h | 153 +++ include/uapi/linux/vbox_vmmdev.h | 1745 ++++++++++++++++++++++++++++ include/uapi/linux/vboxguest.h | 359 ++++++ 15 files changed, 6050 insertions(+) create mode 100644 drivers/virt/vboxguest/Kconfig create mode 100644 drivers/virt/vboxguest/Makefile create mode 100644 drivers/virt/vboxguest/vboxguest_core.c create mode 100644 drivers/virt/vboxguest/vboxguest_core.h create mode 100644 drivers/virt/vboxguest/vboxguest_linux.c create mode 100644 drivers/virt/vboxguest/vboxguest_utils.c create mode 100644 drivers/virt/vboxguest/vboxguest_version.h create mode 100644 include/linux/vbox_utils.h create mode 100644 include/linux/vbox_vmmdev.h create mode 100644 include/uapi/linux/vbox_err.h create mode 100644 include/uapi/linux/vbox_ostypes.h create mode 100644 include/uapi/linux/vbox_vmmdev.h create mode 100644 include/uapi/linux/vboxguest.h
diff --git a/drivers/virt/Kconfig b/drivers/virt/Kconfig index 99ebdde590f8..8d9cdfbd6bcc 100644 --- a/drivers/virt/Kconfig +++ b/drivers/virt/Kconfig @@ -30,4 +30,5 @@ config FSL_HV_MANAGER 4) A kernel interface for receiving callbacks when a managed partition shuts down. +source "drivers/virt/vboxguest/Kconfig" endif diff --git a/drivers/virt/Makefile b/drivers/virt/Makefile index c47f04dd343b..d3f7b2540890 100644 --- a/drivers/virt/Makefile +++ b/drivers/virt/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_FSL_HV_MANAGER) += fsl_hypervisor.o +obj-y += vboxguest/ diff --git a/drivers/virt/vboxguest/Kconfig b/drivers/virt/vboxguest/Kconfig new file mode 100644 index 000000000000..e88ee46c31d4 --- /dev/null +++ b/drivers/virt/vboxguest/Kconfig @@ -0,0 +1,16 @@ +config VBOXGUEST + tristate "Virtual Box Guest integration support" + depends on X86 && PCI && INPUT + help + This is a driver for the Virtual Box Guest PCI device used in + Virtual Box virtual machines. Enabling this driver will add + support for Virtual Box Guest integration features such as + copy-and-paste, seamless mode and OpenGL pass-through. + + This driver also offers vboxguest IPC functionality which is needed + for the vboxfs driver which offers folder sharing support. + + Although it is possible to build this module in, it is advised + to build this driver as a module, so that it can be updated + independently of the kernel. Select M to build this driver as a + module. diff --git a/drivers/virt/vboxguest/Makefile b/drivers/virt/vboxguest/Makefile new file mode 100644 index 000000000000..203b8f465817 --- /dev/null +++ b/drivers/virt/vboxguest/Makefile @@ -0,0 +1,3 @@ +vboxguest-y := vboxguest_linux.o vboxguest_core.o vboxguest_utils.o + +obj-$(CONFIG_VBOXGUEST) += vboxguest.o diff --git a/drivers/virt/vboxguest/vboxguest_core.c b/drivers/virt/vboxguest/vboxguest_core.c new file mode 100644 index 000000000000..6310386a2ab9 --- /dev/null +++ b/drivers/virt/vboxguest/vboxguest_core.c @@ -0,0 +1,1580 @@ +/* + * vboxguest core guest-device handling code, VBoxGuest.cpp in upstream svn. + * + * Copyright (C) 2007-2016 Oracle Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, in which case the provisions of the CDDL are applicable + * instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + +#include <linux/device.h> +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/sizes.h> +#include <linux/slab.h> +#include <linux/vbox_err.h> +#include <linux/vbox_utils.h> +#include <linux/vmalloc.h> +#include "vboxguest_core.h" +#include "vboxguest_version.h" + +#define GUEST_MAPPINGS_TRIES 5 + +/** + * Reserves memory in which the VMM can relocate any guest mappings + * that are floating around. + * + * This operation is a little bit tricky since the VMM might not accept + * just any address because of address clashes between the three contexts + * it operates in, so we try several times. + * + * Failure to reserve the guest mappings is ignored. + * + * @param gdev The Guest extension device. + */ +static void vbg_guest_mappings_init(struct vbg_dev *gdev) +{ + VMMDevReqHypervisorInfo *req; + void *guest_mappings[GUEST_MAPPINGS_TRIES]; + struct page **pages = NULL; + u32 size, hypervisor_size; + int i, rc; + + /* Query the required space. */ + req = vbg_req_alloc(sizeof(*req), VMMDevReq_GetHypervisorInfo); + if (!req) + return; + + req->hypervisorStart = 0; + req->hypervisorSize = 0; + rc = vbg_req_perform(gdev, req); + if (rc < 0) + goto out; + + /* + * The VMM will report back if there is nothing it wants to map, like + * for instance in VT-x and AMD-V mode. + */ + if (req->hypervisorSize == 0) + goto out; + + hypervisor_size = req->hypervisorSize; + /* Add 4M so that we can align the vmap to 4MiB as the host requires. */ + size = PAGE_ALIGN(req->hypervisorSize) + SZ_4M; + + pages = kmalloc(sizeof(*pages) * (size >> PAGE_SHIFT), GFP_KERNEL); + if (!pages) + goto out; + + gdev->guest_mappings_dummy_page = alloc_page(GFP_HIGHUSER); + if (!gdev->guest_mappings_dummy_page) + goto out; + + for (i = 0; i < (size >> PAGE_SHIFT); i++) + pages[i] = gdev->guest_mappings_dummy_page; + + /* Try several times, the host can be picky about certain addresses. */ + for (i = 0; i < GUEST_MAPPINGS_TRIES; i++) { + guest_mappings[i] = vmap(pages, (size >> PAGE_SHIFT), + VM_MAP, PAGE_KERNEL_RO); + if (!guest_mappings[i]) + break; + + req->header.requestType = VMMDevReq_SetHypervisorInfo; + req->header.rc = VERR_INTERNAL_ERROR; + req->hypervisorSize = hypervisor_size; + req->hypervisorStart = + (unsigned long)PTR_ALIGN(guest_mappings[i], SZ_4M); + + rc = vbg_req_perform(gdev, req); + if (rc >= 0) { + gdev->guest_mappings = guest_mappings[i]; + break; + } + } + + /* Free vmap's from failed attempts. */ + while (--i >= 0) + vunmap(guest_mappings[i]); + + /* On failure free the dummy-page backing the vmap */ + if (!gdev->guest_mappings) { + __free_page(gdev->guest_mappings_dummy_page); + gdev->guest_mappings_dummy_page = NULL; + } + +out: + kfree(req); + kfree(pages); +} + +/** + * Undo what vbg_guest_mappings_init did. + * + * @param gdev The Guest extension device. + */ +static void vbg_guest_mappings_exit(struct vbg_dev *gdev) +{ + VMMDevReqHypervisorInfo *req; + int rc; + + if (!gdev->guest_mappings) + return; + + /* + * Tell the host that we're going to free the memory we reserved for + * it, the free it up. (Leak the memory if anything goes wrong here.) + */ + req = vbg_req_alloc(sizeof(*req), VMMDevReq_SetHypervisorInfo); + if (!req) + return; + + req->hypervisorStart = 0; + req->hypervisorSize = 0; + + rc = vbg_req_perform(gdev, req); + + kfree(req); + + if (rc < 0) { + vbg_err("%s error: %d\n", __func__, rc); + return; + } + + vunmap(gdev->guest_mappings); + gdev->guest_mappings = NULL; + + __free_page(gdev->guest_mappings_dummy_page); + gdev->guest_mappings_dummy_page = NULL; +} + +/** + * Report the guest information to the host. + * + * @returns 0 or negative errno value. + * @param gdev The Guest extension device. + */ +static int vbg_report_guest_info(struct vbg_dev *gdev) +{ + /* + * Allocate and fill in the two guest info reports. + */ + VMMDevReportGuestInfo *req1 = NULL; + VMMDevReportGuestInfo2 *req2 = NULL; + int rc, ret = -ENOMEM; + + req1 = vbg_req_alloc(sizeof(*req1), VMMDevReq_ReportGuestInfo); + req2 = vbg_req_alloc(sizeof(*req2), VMMDevReq_ReportGuestInfo2); + if (!req1 || !req2) + goto out_free; + + req1->guestInfo.interfaceVersion = VMMDEV_VERSION; +#ifdef CONFIG_X86_64 + req1->guestInfo.osType = VBOXOSTYPE_Linux26_x64; +#else + req1->guestInfo.osType = VBOXOSTYPE_Linux26; +#endif + + req2->guestInfo.additionsMajor = VBOX_VERSION_MAJOR; + req2->guestInfo.additionsMinor = VBOX_VERSION_MINOR; + req2->guestInfo.additionsBuild = VBOX_VERSION_BUILD; + req2->guestInfo.additionsRevision = VBOX_SVN_REV; + /* (no features defined yet) */ + req2->guestInfo.additionsFeatures = 0; + strlcpy(req2->guestInfo.szName, VBOX_VERSION_STRING, + sizeof(req2->guestInfo.szName)); + + /* + * There are two protocols here: + * 1. Info2 + Info1. Supported by >=3.2.51. + * 2. Info1 and optionally Info2. The old protocol. + * + * We try protocol 2 first. It will fail with VERR_NOT_SUPPORTED + * if not supported by the VMMDev (message ordering requirement). + */ + rc = vbg_req_perform(gdev, req2); + if (rc >= 0) { + rc = vbg_req_perform(gdev, req1); + } else if (rc == VERR_NOT_SUPPORTED || rc == VERR_NOT_IMPLEMENTED) { + rc = vbg_req_perform(gdev, req1); + if (rc >= 0) { + rc = vbg_req_perform(gdev, req2); + if (rc == VERR_NOT_IMPLEMENTED) + rc = VINF_SUCCESS; + } + } + ret = vbg_status_code_to_errno(rc); + +out_free: + kfree(req2); + kfree(req1); + return ret; +} + +/** + * Report the guest driver status to the host. + * + * @returns 0 or negative errno value. + * @param gdev The Guest extension device. + * @param active Flag whether the driver is now active or not. + */ +static int vbg_report_driver_status(struct vbg_dev *gdev, bool active) +{ + VMMDevReportGuestStatus *req; + int rc; + + req = vbg_req_alloc(sizeof(*req), VMMDevReq_ReportGuestStatus); + if (!req) + return -ENOMEM; + + req->guestStatus.facility = VBoxGuestFacilityType_VBoxGuestDriver; + req->guestStatus.status = active ? VBoxGuestFacilityStatus_Active : + VBoxGuestFacilityStatus_Inactive; + req->guestStatus.flags = 0; + + rc = vbg_req_perform(gdev, req); + if (rc == VERR_NOT_IMPLEMENTED) /* Compatibility with older hosts. */ + rc = VINF_SUCCESS; + + kfree(req); + + return vbg_status_code_to_errno(rc); +} + +/** @name Memory Ballooning + * @{ + */ + +/** + * Inflate the balloon by one chunk. + * + * The caller owns the balloon mutex. + * + * @returns VBox status code + * @param gdev The Guest extension device. + * @param chunk_idx Index of the chunk. + */ +static int vbg_balloon_inflate(struct vbg_dev *gdev, u32 chunk_idx) +{ + VMMDevChangeMemBalloon *req = gdev->mem_balloon.change_req; + struct page **pages; + int i, rc; + + pages = kmalloc(sizeof(*pages) * VMMDEV_MEMORY_BALLOON_CHUNK_PAGES, + GFP_KERNEL | __GFP_NOWARN); + if (!pages) + return VERR_NO_MEMORY; + + req->header.size = sizeof(*req); + req->inflate = true; + req->pages = VMMDEV_MEMORY_BALLOON_CHUNK_PAGES; + + for (i = 0; i < VMMDEV_MEMORY_BALLOON_CHUNK_PAGES; i++) { + pages[i] = alloc_page(GFP_KERNEL | __GFP_NOWARN); + if (!pages[i]) { + rc = VERR_NO_MEMORY; + goto out_error; + } + + req->phys_page[i] = page_to_phys(pages[i]); + } + + rc = vbg_req_perform(gdev, req); + if (rc < 0) { + vbg_err("%s error: %d\n", __func__, rc); + goto out_error; + } + + gdev->mem_balloon.pages[chunk_idx] = pages; + + return VINF_SUCCESS; + +out_error: + while (--i >= 0) + __free_page(pages[i]); + kfree(pages); + + return rc; +} + +/** + * Deflate the balloon by one chunk. + * + * The caller owns the balloon mutex. + * + * @returns VBox status code + * @param gdev The Guest extension device. + * @param chunk_idx Index of the chunk. + */ +static int vbg_balloon_deflate(struct vbg_dev *gdev, u32 chunk_idx) +{ + VMMDevChangeMemBalloon *req = gdev->mem_balloon.change_req; + struct page **pages = gdev->mem_balloon.pages[chunk_idx]; + int i, rc; + + req->header.size = sizeof(*req); + req->inflate = false; + req->pages = VMMDEV_MEMORY_BALLOON_CHUNK_PAGES; + + for (i = 0; i < VMMDEV_MEMORY_BALLOON_CHUNK_PAGES; i++) + req->phys_page[i] = page_to_phys(pages[i]); + + rc = vbg_req_perform(gdev, req); + if (rc < 0) { + vbg_err("%s error: %d\n", __func__, rc); + return rc; + } + + for (i = 0; i < VMMDEV_MEMORY_BALLOON_CHUNK_PAGES; i++) + __free_page(pages[i]); + kfree(pages); + gdev->mem_balloon.pages[chunk_idx] = NULL; + + return VINF_SUCCESS; +} + +/** + * Respond to VMMDEV_EVENT_BALLOON_CHANGE_REQUEST events, query the size + * the host wants the balloon to be and adjust accordingly. + */ +static void vbg_balloon_work(struct work_struct *work) +{ + struct vbg_dev *gdev = + container_of(work, struct vbg_dev, mem_balloon.work); + VMMDevGetMemBalloonChangeRequest *req = gdev->mem_balloon.get_req; + u32 i, chunks; + int rc; + + /* + * Setting this bit means that we request the value from the host and + * change the guest memory balloon according to the returned value. + */ + req->eventAck = VMMDEV_EVENT_BALLOON_CHANGE_REQUEST; + rc = vbg_req_perform(gdev, req); + if (rc < 0) { + vbg_err("%s error: %d)\n", __func__, rc); + return; + } + + /* + * The host always returns the same maximum amount of chunks, so + * we do this once. + */ + if (!gdev->mem_balloon.max_chunks) { + gdev->mem_balloon.pages = + devm_kcalloc(gdev->dev, req->cPhysMemChunks, + sizeof(struct page **), GFP_KERNEL); + if (!gdev->mem_balloon.pages) + return; + + gdev->mem_balloon.max_chunks = req->cPhysMemChunks; + } + + chunks = req->cBalloonChunks; + if (chunks > gdev->mem_balloon.max_chunks) { + vbg_err("%s: illegal balloon size %u (max=%u)\n", + __func__, chunks, gdev->mem_balloon.max_chunks); + return; + } + + if (req->cBalloonChunks > gdev->mem_balloon.chunks) { + /* inflate */ + for (i = gdev->mem_balloon.chunks; i < chunks; i++) { + rc = vbg_balloon_inflate(gdev, i); + if (rc < 0) + return; + + gdev->mem_balloon.chunks++; + } + } else { + /* deflate */ + for (i = gdev->mem_balloon.chunks; i-- > chunks;) { + rc = vbg_balloon_deflate(gdev, i); + if (rc < 0) + return; + + gdev->mem_balloon.chunks--; + } + } +} + +/** @} */ + +/** @name Heartbeat + * @{ + */ + +/** + * Callback for heartbeat timer. + */ +static void vbg_heartbeat_timer(unsigned long data) +{ + struct vbg_dev *gdev = (struct vbg_dev *)data; + + vbg_req_perform(gdev, gdev->guest_heartbeat_req); + mod_timer(&gdev->heartbeat_timer, + msecs_to_jiffies(gdev->heartbeat_interval_ms)); +} + +/** + * Configure the host to check guest's heartbeat + * and get heartbeat interval from the host. + * + * @returns 0 or negative errno value. + * @param gdev The Guest extension device. + * @param enabled Set true to enable guest heartbeat checks on host. + */ +static int vbg_heartbeat_host_config(struct vbg_dev *gdev, bool enabled) +{ + VMMDevReqHeartbeat *req; + int rc; + + req = vbg_req_alloc(sizeof(*req), VMMDevReq_HeartbeatConfigure); + if (!req) + return -ENOMEM; + + req->fEnabled = enabled; + req->cNsInterval = 0; + rc = vbg_req_perform(gdev, req); + do_div(req->cNsInterval, 1000000); /* ns -> ms */ + gdev->heartbeat_interval_ms = req->cNsInterval; + kfree(req); + + return vbg_status_code_to_errno(rc); +} + +/** + * Initializes the heartbeat timer. + * + * This feature may be disabled by the host. + * + * @returns 0 or negative errno value (ignored). + * @param gdev The Guest extension device. + */ +static int vbg_heartbeat_init(struct vbg_dev *gdev) +{ + int ret; + + /* Make sure that heartbeat checking is disabled if we fail. */ + ret = vbg_heartbeat_host_config(gdev, false); + if (ret < 0) + return ret; + + ret = vbg_heartbeat_host_config(gdev, true); + if (ret < 0) + return ret; + + /* + * Preallocate the request to use it from the timer callback because: + * 1) on Windows vbg_req_alloc must be called at IRQL <= APC_LEVEL + * and the timer callback runs at DISPATCH_LEVEL; + * 2) avoid repeated allocations. + */ + gdev->guest_heartbeat_req = vbg_req_alloc( + sizeof(*gdev->guest_heartbeat_req), + VMMDevReq_GuestHeartbeat); + if (!gdev->guest_heartbeat_req) + return -ENOMEM; + + vbg_info("%s: Setting up heartbeat to trigger every %d milliseconds\n", + __func__, gdev->heartbeat_interval_ms); + mod_timer(&gdev->heartbeat_timer, 0); + + return 0; +} + +/** + * Cleanup hearbeat code, stop HB timer and disable host heartbeat checking. + * @param gdev The Guest extension device. + */ +static void vbg_heartbeat_exit(struct vbg_dev *gdev) +{ + del_timer_sync(&gdev->heartbeat_timer); + vbg_heartbeat_host_config(gdev, false); + kfree(gdev->guest_heartbeat_req); + +} + +/** @} */ + +/** @name Guest Capabilities and Event Filter + * @{ + */ + +/** + * Applies a change to the bit usage tracker. + * + * @returns true if the mask changed, false if not. + * @param tracker The bit usage tracker. + * @param changed The bits to change. + * @param previous The previous value of the bits. + */ +static bool vbg_track_bit_usage(struct vbg_bit_usage_tracker *tracker, + u32 changed, u32 previous) +{ + bool global_change = false; + + while (changed) { + u32 bit = ffs(changed) - 1; + u32 bitmask = BIT(bit); + + if (bitmask & previous) { + tracker->per_bit_usage[bit] -= 1; + if (tracker->per_bit_usage[bit] == 0) { + global_change = true; + tracker->mask &= ~bitmask; + } + } else { + tracker->per_bit_usage[bit] += 1; + if (tracker->per_bit_usage[bit] == 1) { + global_change = true; + tracker->mask |= bitmask; + } + } + + changed &= ~bitmask; + } + + return global_change; +} + +/** + * Init and termination worker for resetting the (host) event filter on the host + * + * @returns 0 or negative errno value. + * @param gdev The Guest extension device. + * @param fixed_events Fixed events (init time). + */ +static int vbg_reset_host_event_filter(struct vbg_dev *gdev, + u32 fixed_events) +{ + VMMDevCtlGuestFilterMask *req; + int rc; + + req = vbg_req_alloc(sizeof(*req), VMMDevReq_CtlGuestFilterMask); + if (!req) + return -ENOMEM; + + req->u32NotMask = U32_MAX & ~fixed_events; + req->u32OrMask = fixed_events; + rc = vbg_req_perform(gdev, req); + if (rc < 0) + vbg_err("%s error: %d\n", __func__, rc); + + kfree(req); + return vbg_status_code_to_errno(rc); +} + +/** + * Changes the event filter mask for the given session. + * + * This is called in response to VBOXGUEST_IOCTL_CTL_FILTER_MASK as well as to + * do session cleanup. + * + * @returns VBox status code + * @param gdev The Guest extension device. + * @param session The session. + * @param or_mask The events to add. + * @param not_mask The events to remove. + * @param session_termination Set if we're called by the session cleanup code. + * This tweaks the error handling so we perform + * proper session cleanup even if the host + * misbehaves. + * + * @remarks Takes the session spinlock. + */ +static int vbg_set_session_event_filter(struct vbg_dev *gdev, + struct vbg_session *session, + u32 or_mask, u32 not_mask, + bool session_termination) +{ + VMMDevCtlGuestFilterMask *req; + u32 changed, previous; + unsigned long flags; + int rc = VINF_SUCCESS; + + /* Allocate a request buffer before taking the spinlock */ + req = vbg_req_alloc(sizeof(*req), VMMDevReq_CtlGuestFilterMask); + if (!req) { + if (!session_termination) + return VERR_NO_MEMORY; + /* Ignore failure, we must do session cleanup. */ + } + + spin_lock_irqsave(&gdev->session_spinlock, flags); + + /* Apply the changes to the session mask. */ + previous = session->event_filter; + session->event_filter |= or_mask; + session->event_filter &= ~not_mask; + + /* If anything actually changed, update the global usage counters. */ + changed = previous ^ session->event_filter; + if (!changed) + goto out; + + vbg_track_bit_usage(&gdev->event_filter_tracker, changed, previous); + req->u32OrMask = gdev->fixed_events | gdev->event_filter_tracker.mask; + + if (gdev->event_filter_host == req->u32OrMask || !req) + goto out; + + gdev->event_filter_host = req->u32OrMask; + req->u32NotMask = ~req->u32OrMask; + rc = vbg_req_perform(gdev, req); + if (rc < 0) { + /* Failed, roll back (unless it's session termination time). */ + gdev->event_filter_host = U32_MAX; + if (session_termination) + goto out; + + vbg_track_bit_usage(&gdev->event_filter_tracker, changed, + session->event_filter); + session->event_filter = previous; + } + +out: + spin_unlock_irqrestore(&gdev->session_spinlock, flags); + kfree(req); + + return rc; +} + +/** + * Init and termination worker for set guest capabilities to zero on the host. + * + * @returns 0 or negative errno value. + * @param gdev The Guest extension device. + */ +static int vbg_reset_host_capabilities(struct vbg_dev *gdev) +{ + VMMDevReqGuestCapabilities2 *req; + int rc; + + req = vbg_req_alloc(sizeof(*req), VMMDevReq_SetGuestCapabilities); + if (!req) + return -ENOMEM; + + req->u32NotMask = U32_MAX; + req->u32OrMask = 0; + rc = vbg_req_perform(gdev, req); + if (rc < 0) + vbg_err("%s error: %d\n", __func__, rc); + + kfree(req); + return vbg_status_code_to_errno(rc); +} + +/** + * Sets the guest capabilities for a session. + * + * @returns VBox status code + * @param gdev The Guest extension device. + * @param session The session. + * @param or_mask The capabilities to add. + * @param not_mask The capabilities to remove. + * @param session_termination Set if we're called by the session cleanup code. + * This tweaks the error handling so we perform + * proper session cleanup even if the host + * misbehaves. + * + * @remarks Takes the session spinlock. + */ +static int vbg_set_session_capabilities(struct vbg_dev *gdev, + struct vbg_session *session, + u32 or_mask, u32 not_mask, + bool session_termination) +{ + VMMDevReqGuestCapabilities2 *req; + unsigned long flags; + int rc = VINF_SUCCESS; + u32 changed, previous; + + /* Allocate a request buffer before taking the spinlock */ + req = vbg_req_alloc(sizeof(*req), VMMDevReq_SetGuestCapabilities); + if (!req) { + if (!session_termination) + return VERR_NO_MEMORY; + /* Ignore failure, we must do session cleanup. */ + } + + spin_lock_irqsave(&gdev->session_spinlock, flags); + + /* Apply the changes to the session mask. */ + previous = session->guest_caps; + session->guest_caps |= or_mask; + session->guest_caps &= ~not_mask; + + /* If anything actually changed, update the global usage counters. */ + changed = previous ^ session->guest_caps; + if (!changed) + goto out; + + vbg_track_bit_usage(&gdev->guest_caps_tracker, changed, previous); + req->u32OrMask = gdev->guest_caps_tracker.mask; + + if (gdev->guest_caps_host == req->u32OrMask || !req) + goto out; + + gdev->guest_caps_host = req->u32OrMask; + req->u32NotMask = ~req->u32OrMask; + rc = vbg_req_perform(gdev, req); + if (rc < 0) { + /* Failed, roll back (unless it's session termination time). */ + gdev->guest_caps_host = U32_MAX; + if (session_termination) + goto out; + + vbg_track_bit_usage(&gdev->guest_caps_tracker, changed, + session->guest_caps); + session->guest_caps = previous; + } + +out: + spin_unlock_irqrestore(&gdev->session_spinlock, flags); + kfree(req); + + return rc; +} + +/** @} */ + +/** + * vbg_query_host_version try get the host feature mask and version information + * (vbg_host_version). + * + * @returns 0 or negative errno value (ignored). + * @param gdev The Guest extension device. + */ +static int vbg_query_host_version(struct vbg_dev *gdev) +{ + VMMDevReqHostVersion *req; + int rc, ret; + + req = vbg_req_alloc(sizeof(*req), VMMDevReq_GetHostVersion); + if (!req) + return -ENOMEM; + + rc = vbg_req_perform(gdev, req); + ret = vbg_status_code_to_errno(rc); + if (ret) + goto out; + + snprintf(gdev->host_version, sizeof(gdev->host_version), "%u.%u.%ur%u", + req->major, req->minor, req->build, req->revision); + gdev->host_features = req->features; + + vbg_info("vboxguest: host-version: %s %#x\n", gdev->host_version, + gdev->host_features); + + if (!(req->features & VMMDEV_HVF_HGCM_PHYS_PAGE_LIST)) { + vbg_err("vboxguest: Error host too old (does not support page-lists)\n"); + ret = -ENODEV; + } + +out: + kfree(req); + return ret; +} + +/** + * Initializes the VBoxGuest device extension when the + * device driver is loaded. + * + * The native code locates the VMMDev on the PCI bus and retrieve + * the MMIO and I/O port ranges, this function will take care of + * mapping the MMIO memory (if present). Upon successful return + * the native code should set up the interrupt handler. + * + * @returns 0 or negative errno value. + * + * @param gdev The Guest extension device. + * @param fixed_events Events that will be enabled upon init and no client + * will ever be allowed to mask. + */ +int vbg_core_init(struct vbg_dev *gdev, u32 fixed_events) +{ + int ret = -ENOMEM; + + gdev->fixed_events = fixed_events | VMMDEV_EVENT_HGCM; + gdev->event_filter_host = U32_MAX; /* forces a report */ + gdev->guest_caps_host = U32_MAX; /* forces a report */ + + init_waitqueue_head(&gdev->event_wq); + init_waitqueue_head(&gdev->hgcm_wq); + INIT_LIST_HEAD(&gdev->session_list); + spin_lock_init(&gdev->event_spinlock); + spin_lock_init(&gdev->session_spinlock); + mutex_init(&gdev->cancel_req_mutex); + setup_timer(&gdev->heartbeat_timer, vbg_heartbeat_timer, + (unsigned long)gdev); + INIT_WORK(&gdev->mem_balloon.work, vbg_balloon_work); + + gdev->mem_balloon.get_req = + vbg_req_alloc(sizeof(*gdev->mem_balloon.get_req), + VMMDevReq_GetMemBalloonChangeRequest); + gdev->mem_balloon.change_req = + vbg_req_alloc(sizeof(*gdev->mem_balloon.change_req), + VMMDevReq_ChangeMemBalloon); + gdev->cancel_req = + vbg_req_alloc(sizeof(*(gdev->cancel_req)), + VMMDevReq_HGCMCancel2); + gdev->ack_events_req = + vbg_req_alloc(sizeof(*gdev->ack_events_req), + VMMDevReq_AcknowledgeEvents); + gdev->mouse_status_req = + vbg_req_alloc(sizeof(*gdev->mouse_status_req), + VMMDevReq_GetMouseStatus); + + if (!gdev->mem_balloon.get_req || !gdev->mem_balloon.change_req || + !gdev->cancel_req || !gdev->ack_events_req || + !gdev->mouse_status_req) + goto err_free_reqs; + + ret = vbg_query_host_version(gdev); + if (ret) + goto err_free_reqs; + + ret = vbg_report_guest_info(gdev); + if (ret) { + vbg_err("vboxguest: VBoxReportGuestInfo error: %d\n", ret); + goto err_free_reqs; + } + + ret = vbg_reset_host_event_filter(gdev, gdev->fixed_events); + if (ret) { + vbg_err("vboxguest: Error setting fixed event filter: %d\n", + ret); + goto err_free_reqs; + } + + ret = vbg_reset_host_capabilities(gdev); + if (ret) { + vbg_err("vboxguest: Error clearing guest capabilities: %d\n", + ret); + goto err_free_reqs; + } + + ret = vbg_core_set_mouse_status(gdev, 0); + if (ret) { + vbg_err("vboxguest: Error clearing mouse status: %d\n", ret); + goto err_free_reqs; + } + + /* These may fail without requiring the driver init to fail. */ + vbg_guest_mappings_init(gdev); + vbg_heartbeat_init(gdev); + + /* All Done! */ + ret = vbg_report_driver_status(gdev, true); + if (ret < 0) + vbg_err("vboxguest: VBoxReportGuestDriverStatus error: %d\n", + ret); + + return 0; + +err_free_reqs: + kfree(gdev->mouse_status_req); + kfree(gdev->ack_events_req); + kfree(gdev->cancel_req); + kfree(gdev->mem_balloon.change_req); + kfree(gdev->mem_balloon.get_req); + return ret; +} + +/** + * Call this on exit to clean-up vboxguest-core managed resources. + * + * The native code should call this before the driver is loaded, + * but don't call this on shutdown. + * + * @param gdev The Guest extension device. + */ +void vbg_core_exit(struct vbg_dev *gdev) +{ + vbg_heartbeat_exit(gdev); + vbg_guest_mappings_exit(gdev); + + /* Clear the host flags (mouse status etc). */ + vbg_reset_host_event_filter(gdev, 0); + vbg_reset_host_capabilities(gdev); + vbg_core_set_mouse_status(gdev, 0); + + kfree(gdev->mouse_status_req); + kfree(gdev->ack_events_req); + kfree(gdev->cancel_req); + kfree(gdev->mem_balloon.change_req); + kfree(gdev->mem_balloon.get_req); +} + +/** + * Creates a VBoxGuest user session. + * + * vboxguest_linux.c calls this when userspace opens the char-device. + * + * @returns 0 or negative errno value. + * @param gdev The Guest extension device. + * @param session_ret Where to store the session on success. + * @param user_session Set if this is a session for the vboxuser device. + */ +int vbg_core_open_session(struct vbg_dev *gdev, + struct vbg_session **session_ret, bool user_session) +{ + struct vbg_session *session; + unsigned long flags; + + session = kzalloc(sizeof(*session), GFP_KERNEL); + if (!session) + return -ENOMEM; + + session->gdev = gdev; + session->user_session = user_session; + + spin_lock_irqsave(&gdev->session_spinlock, flags); + list_add(&session->list_node, &gdev->session_list); + spin_unlock_irqrestore(&gdev->session_spinlock, flags); + + *session_ret = session; + + return 0; +} + +/** + * Closes a VBoxGuest session. + * + * @param session The session to close (and free). + */ +void vbg_core_close_session(struct vbg_session *session) +{ + struct vbg_dev *gdev = session->gdev; + unsigned long flags; + int i; + + spin_lock_irqsave(&gdev->session_spinlock, flags); + list_del(&session->list_node); + spin_unlock_irqrestore(&gdev->session_spinlock, flags); + + vbg_set_session_capabilities(gdev, session, 0, U32_MAX, true); + vbg_set_session_event_filter(gdev, session, 0, U32_MAX, true); + + for (i = 0; i < ARRAY_SIZE(session->hgcm_client_ids); i++) { + if (session->hgcm_client_ids[i]) + vbg_hgcm_disconnect(gdev, session->hgcm_client_ids[i]); + } + + kfree(session); +} + +static bool vbg_wait_event_cond(struct vbg_dev *gdev, + struct vbg_session *session, + VBoxGuestWaitEventInfo *info) +{ + unsigned long flags; + bool wakeup; + u32 events; + + spin_lock_irqsave(&gdev->event_spinlock, flags); + + events = gdev->pending_events & info->u32EventMaskIn; + wakeup = events || session->cancel_waiters; + + spin_unlock_irqrestore(&gdev->event_spinlock, flags); + + return wakeup; +} + +/* Must be called with the event_lock held */ +static u32 vbg_consume_events_locked(struct vbg_dev *gdev, + struct vbg_session *session, + VBoxGuestWaitEventInfo *info) +{ + u32 events = gdev->pending_events & info->u32EventMaskIn; + + gdev->pending_events &= ~events; + return events; +} + +static int vbg_ioctl_wait_event(struct vbg_dev *gdev, + struct vbg_session *session, + VBoxGuestWaitEventInfo *info) +{ + unsigned long flags; + long timeout; + int rc = VINF_SUCCESS; + + if (info->u32TimeoutIn == U32_MAX) + timeout = MAX_SCHEDULE_TIMEOUT; + else + timeout = msecs_to_jiffies(info->u32TimeoutIn); + + info->u32Result = VBOXGUEST_WAITEVENT_OK; + info->u32EventFlagsOut = 0; + + do { + timeout = wait_event_interruptible_timeout( + gdev->event_wq, + vbg_wait_event_cond(gdev, session, info), + timeout); + + spin_lock_irqsave(&gdev->event_spinlock, flags); + + if (timeout < 0 || session->cancel_waiters) { + info->u32Result = VBOXGUEST_WAITEVENT_INTERRUPTED; + rc = VERR_INTERRUPTED; + } else if (timeout == 0) { + info->u32Result = VBOXGUEST_WAITEVENT_TIMEOUT; + rc = VERR_TIMEOUT; + } else { + info->u32EventFlagsOut = + vbg_consume_events_locked(gdev, session, info); + } + + spin_unlock_irqrestore(&gdev->event_spinlock, flags); + + /* + * Someone else may have consumed the event(s) first, in + * which case we go back to waiting. + */ + } while (rc == VINF_SUCCESS && info->u32EventFlagsOut == 0); + + return rc; +} + +static int vbg_ioctl_cancel_all_wait_events(struct vbg_dev *gdev, + struct vbg_session *session) +{ + unsigned long flags; + + spin_lock_irqsave(&gdev->event_spinlock, flags); + session->cancel_waiters = true; + spin_unlock_irqrestore(&gdev->event_spinlock, flags); + + wake_up(&gdev->event_wq); + + return VINF_SUCCESS; +} + +/** + * Checks if the VMM request is allowed in the context of the given session. + * + * @returns VBox status code + * @param gdev The Guest extension device. + * @param session The calling session. + * @param req The request. + */ +static int vbg_req_allowed(struct vbg_dev *gdev, struct vbg_session *session, + VMMDevRequestHeader const *req) +{ + const VMMDevReportGuestStatus *guest_status; + bool trusted_apps_only; + + switch (req->requestType) { + /* Trusted users apps only. */ + case VMMDevReq_QueryCredentials: + case VMMDevReq_ReportCredentialsJudgement: + case VMMDevReq_RegisterSharedModule: + case VMMDevReq_UnregisterSharedModule: + case VMMDevReq_WriteCoreDump: + case VMMDevReq_GetCpuHotPlugRequest: + case VMMDevReq_SetCpuHotPlugStatus: + case VMMDevReq_CheckSharedModules: + case VMMDevReq_GetPageSharingStatus: + case VMMDevReq_DebugIsPageShared: + case VMMDevReq_ReportGuestStats: + case VMMDevReq_ReportGuestUserState: + case VMMDevReq_GetStatisticsChangeRequest: + case VMMDevReq_ChangeMemBalloon: + trusted_apps_only = true; + break; + + /* Anyone. */ + case VMMDevReq_GetMouseStatus: + case VMMDevReq_SetMouseStatus: + case VMMDevReq_SetPointerShape: + case VMMDevReq_GetHostVersion: + case VMMDevReq_Idle: + case VMMDevReq_GetHostTime: + case VMMDevReq_SetPowerStatus: + case VMMDevReq_AcknowledgeEvents: + case VMMDevReq_CtlGuestFilterMask: + case VMMDevReq_ReportGuestStatus: + case VMMDevReq_GetDisplayChangeRequest: + case VMMDevReq_VideoModeSupported: + case VMMDevReq_GetHeightReduction: + case VMMDevReq_GetDisplayChangeRequest2: + case VMMDevReq_VideoModeSupported2: + case VMMDevReq_VideoAccelEnable: + case VMMDevReq_VideoAccelFlush: + case VMMDevReq_VideoSetVisibleRegion: + case VMMDevReq_GetDisplayChangeRequestEx: + case VMMDevReq_GetSeamlessChangeRequest: + case VMMDevReq_GetVRDPChangeRequest: + case VMMDevReq_LogString: + case VMMDevReq_GetSessionId: + trusted_apps_only = false; + break; + + /** + * @todo this have to be changed into an I/O control and the facilities + * tracked in the session so they can automatically be failed when + * the session terminates without reporting the new status. + * + * The information presented by IGuest is not reliable without this! + */ + /* Depends on the request parameters... */ + case VMMDevReq_ReportGuestCapabilities: + guest_status = (const VMMDevReportGuestStatus *)req; + switch (guest_status->guestStatus.facility) { + case VBoxGuestFacilityType_All: + case VBoxGuestFacilityType_VBoxGuestDriver: + vbg_err("Denying userspace vmm report guest cap. call facility %#08x\n", + guest_status->guestStatus.facility); + return VERR_PERMISSION_DENIED; + case VBoxGuestFacilityType_VBoxService: + trusted_apps_only = true; + break; + case VBoxGuestFacilityType_VBoxTrayClient: + case VBoxGuestFacilityType_Seamless: + case VBoxGuestFacilityType_Graphics: + default: + trusted_apps_only = false; + break; + } + break; + + /* Anything else is not allowed. */ + default: + vbg_err("Denying userspace vmm call type %#08x\n", + req->requestType); + return VERR_PERMISSION_DENIED; + } + + if (trusted_apps_only && session->user_session) { + vbg_err("Denying userspace vmm call type %#08x through vboxuser device node\n", + req->requestType); + return VERR_PERMISSION_DENIED; + } + + return VINF_SUCCESS; +} + +static int vbg_ioctl_vmmrequest(struct vbg_dev *gdev, + struct vbg_session *session, + VMMDevRequestHeader *req, size_t req_size, + size_t *req_size_ret) +{ + int rc; + + rc = vbg_req_verify(req, req_size); + if (rc < 0) + return rc; + + rc = vbg_req_allowed(gdev, session, req); + if (rc < 0) + return rc; + + rc = vbg_req_perform(gdev, req); + if (rc >= 0) { + WARN_ON(rc == VINF_HGCM_ASYNC_EXECUTE); + *req_size_ret = req->size; + } + + return rc; +} + +static int vbg_ioctl_hgcm_connect(struct vbg_dev *gdev, + struct vbg_session *session, + VBoxGuestHGCMConnectInfo *info) +{ + unsigned long flags; + u32 client_id; + int i, rc; + + /* Find a free place in the sessions clients array and claim it */ + spin_lock_irqsave(&gdev->session_spinlock, flags); + for (i = 0; i < ARRAY_SIZE(session->hgcm_client_ids); i++) { + if (!session->hgcm_client_ids[i]) { + session->hgcm_client_ids[i] = U32_MAX; + break; + } + } + spin_unlock_irqrestore(&gdev->session_spinlock, flags); + + if (i >= ARRAY_SIZE(session->hgcm_client_ids)) + return VERR_TOO_MANY_OPEN_FILES; + + rc = vbg_hgcm_connect(gdev, &info->Loc, &client_id); + + spin_lock_irqsave(&gdev->session_spinlock, flags); + if (rc >= 0) { + info->result = VINF_SUCCESS; + info->u32ClientID = client_id; + session->hgcm_client_ids[i] = client_id; + } else { + session->hgcm_client_ids[i] = 0; + } + spin_unlock_irqrestore(&gdev->session_spinlock, flags); + + return rc; +} + +static int vbg_ioctl_hgcm_disconnect(struct vbg_dev *gdev, + struct vbg_session *session, + VBoxGuestHGCMDisconnectInfo *info) +{ + unsigned long flags; + u32 client_id; + int i, rc; + + client_id = info->u32ClientID; + if (client_id == 0 || client_id == U32_MAX) + return VERR_INVALID_HANDLE; + + spin_lock_irqsave(&gdev->session_spinlock, flags); + for (i = 0; i < ARRAY_SIZE(session->hgcm_client_ids); i++) { + if (session->hgcm_client_ids[i] == client_id) { + session->hgcm_client_ids[i] = U32_MAX; + break; + } + } + spin_unlock_irqrestore(&gdev->session_spinlock, flags); + + if (i >= ARRAY_SIZE(session->hgcm_client_ids)) + return VERR_INVALID_HANDLE; + + rc = vbg_hgcm_disconnect(gdev, client_id); + + spin_lock_irqsave(&gdev->session_spinlock, flags); + if (rc >= 0) { + info->result = VINF_SUCCESS; + session->hgcm_client_ids[i] = 0; + } else { + session->hgcm_client_ids[i] = client_id; + } + spin_unlock_irqrestore(&gdev->session_spinlock, flags); + + return rc; +} + +static int vbg_ioctl_hgcm_call(struct vbg_dev *gdev, + struct vbg_session *session, + u32 timeout_ms, bool f32bit, + VBoxGuestHGCMCallInfo *info, size_t info_size, + size_t *info_size_ret) +{ + u32 client_id = info->u32ClientID; + unsigned long flags; + size_t actual_size; + int i, rc; + + if (info_size < sizeof(VBoxGuestHGCMCallInfo)) + return VERR_BUFFER_OVERFLOW; + + if (info->cParms > VBOX_HGCM_MAX_PARMS) + return VERR_INVALID_PARAMETER; + + if (client_id == 0 || client_id == U32_MAX) + return VERR_INVALID_HANDLE; + + actual_size = sizeof(*info); + if (f32bit) + actual_size += info->cParms * sizeof(HGCMFunctionParameter32); + else + actual_size += info->cParms * sizeof(HGCMFunctionParameter); + if (info_size < actual_size) { + vbg_debug("VBOXGUEST_IOCTL_HGCM_CALL: info_size=%#zx (%zu) required size is %#zx (%zu)\n", + info_size, info_size, actual_size, actual_size); + return VERR_INVALID_PARAMETER; + } + + /* + * Validate the client id. + */ + spin_lock_irqsave(&gdev->session_spinlock, flags); + for (i = 0; i < ARRAY_SIZE(session->hgcm_client_ids); i++) + if (session->hgcm_client_ids[i] == client_id) + break; + spin_unlock_irqrestore(&gdev->session_spinlock, flags); + if (i >= ARRAY_SIZE(session->hgcm_client_ids)) { + vbg_debug("VBOXGUEST_IOCTL_HGCM_CALL: Invalid handle. u32Client=%#08x\n", + client_id); + return VERR_INVALID_HANDLE; + } + + if (f32bit) + rc = vbg_hgcm_call32(gdev, info, info_size, timeout_ms, true); + else + rc = vbg_hgcm_call(gdev, info, info_size, timeout_ms, true); + + if (rc >= 0) { + *info_size_ret = actual_size; + } else if (rc == VERR_INTERRUPTED || rc == VERR_TIMEOUT || + rc == VERR_OUT_OF_RANGE) { + vbg_debug("VBOXGUEST_IOCTL_HGCM_CALL%s error: %d\n", + f32bit ? "32" : "64", rc); + } else { + vbg_err("VBOXGUEST_IOCTL_HGCM_CALL%s error: %d\n", + f32bit ? "32" : "64", rc); + } + return rc; +} + +/** + * Handle a request for writing a core dump of the guest on the host. + * + * @returns VBox status code + * + * @param gdev The Guest extension device. + * @param info The output buffer. + */ +static int vbg_ioctl_write_core_dump(struct vbg_dev *gdev, + VBoxGuestWriteCoreDump *info) +{ + VMMDevReqWriteCoreDump *req; + int rc; + + req = vbg_req_alloc(sizeof(*req), VMMDevReq_WriteCoreDump); + if (!req) + return VERR_NO_MEMORY; + + req->fFlags = info->fFlags; + rc = vbg_req_perform(gdev, req); + + kfree(req); + return rc; +} + +/** + * Handle VBOXGUEST_IOCTL_CTL_FILTER_MASK. + * + * @returns VBox status code + * @param gdev The Guest extension device. + * @param session The session. + * @param info The request. + */ +static int vbg_ioctl_ctl_filter_mask(struct vbg_dev *gdev, + struct vbg_session *session, + VBoxGuestFilterMaskInfo *info) +{ + if ((info->u32OrMask | info->u32NotMask) & + ~VMMDEV_EVENT_VALID_EVENT_MASK) + return VERR_INVALID_PARAMETER; + + return vbg_set_session_event_filter(gdev, session, info->u32OrMask, + info->u32NotMask, false); +} + +/** + * Report guest supported mouse-features to the host. + * + * @returns 0 or negative errno value. + * @returns VBox status code + * @param gdev The Guest extension device. + * @param features The set of features to report to the host. + */ +int vbg_core_set_mouse_status(struct vbg_dev *gdev, u32 features) +{ + VMMDevReqMouseStatus *req; + int rc; + + req = vbg_req_alloc(sizeof(*req), VMMDevReq_SetMouseStatus); + if (!req) + return -ENOMEM; + + req->mouseFeatures = features; + req->pointerXPos = 0; + req->pointerYPos = 0; + + rc = vbg_req_perform(gdev, req); + if (rc < 0) + vbg_err("%s error: %d\n", __func__, rc); + + kfree(req); + return vbg_status_code_to_errno(rc); +} + +/** + * Handle VBOXGUEST_IOCTL_SET_GUEST_CAPABILITIES. + * + * @returns VBox status code + * @param gdev The Guest extension device. + * @param session The session. + * @param info The request. + */ +static int vbg_ioctl_set_capabilities(struct vbg_dev *gdev, + struct vbg_session *session, + VBoxGuestSetCapabilitiesInfo *info) +{ + if ((info->u32OrMask | info->u32NotMask) & + ~VMMDEV_GUEST_CAPABILITIES_MASK) + return VERR_INVALID_PARAMETER; + + return vbg_set_session_capabilities(gdev, session, info->u32OrMask, + info->u32NotMask, false); +} + +/** + * Common IOCtl for user to kernel communication. + * + * This function only does the basic validation and then invokes + * worker functions that takes care of each specific function. + * + * @returns VBox status code + * @param session The client session. + * @param req The requested function. + * @param data The input/output data buffer. + * @param data_size The max size of the data buffer. + * @param data_size_ret Where to store the amount of returned data. + */ +int vbg_core_ioctl(struct vbg_session *session, unsigned int req, void *data, + size_t data_size, size_t *data_size_ret) +{ + VBoxGuestHGCMCallInfoTimed *timed = data; + struct vbg_dev *gdev = session->gdev; + bool f32bit = false; + size_t offset; + int rc; + + *data_size_ret = 0; + + /* All our ioctls are type 'V' dir RW */ + if (_IOC_TYPE(req) != 'V' || _IOC_DIR(req) != (_IOC_READ|_IOC_WRITE)) + return VERR_NOT_SUPPORTED; + + /* First check for variable sized ioctls by ioc number. */ + switch (_IOC_NR(req)) { + case _IOC_NR(VBOXGUEST_IOCTL_VMMREQUEST(0)): + return vbg_ioctl_vmmrequest(gdev, session, + data, data_size, data_size_ret); +#ifdef CONFIG_X86_64 + case _IOC_NR(VBOXGUEST_IOCTL_HGCM_CALL_32(0)): + f32bit = true; + /* Fall through */ +#endif + case _IOC_NR(VBOXGUEST_IOCTL_HGCM_CALL(0)): + return vbg_ioctl_hgcm_call(gdev, session, U32_MAX, f32bit, + data, data_size, data_size_ret); +#ifdef CONFIG_X86_64 + case _IOC_NR(VBOXGUEST_IOCTL_HGCM_CALL_TIMED_32(0)): + f32bit = true; + /* Fall through */ +#endif + case _IOC_NR(VBOXGUEST_IOCTL_HGCM_CALL_TIMED(0)): + offset = offsetof(VBoxGuestHGCMCallInfoTimed, info); + rc = vbg_ioctl_hgcm_call(gdev, session, + timed->u32Timeout, f32bit, + data + offset, data_size - offset, + data_size_ret); + *data_size_ret += offset; + return rc; + case _IOC_NR(VBOXGUEST_IOCTL_LOG(0)): + vbg_info("%.*s", (int)data_size, (char *)data); + return VINF_SUCCESS; + } + + /* Handle fixed size requests. */ + *data_size_ret = data_size; + + switch (req) { + case VBOXGUEST_IOCTL_WAITEVENT: + return vbg_ioctl_wait_event(gdev, session, data); + case VBOXGUEST_IOCTL_CANCEL_ALL_WAITEVENTS: + return vbg_ioctl_cancel_all_wait_events(gdev, session); + case VBOXGUEST_IOCTL_CTL_FILTER_MASK: + return vbg_ioctl_ctl_filter_mask(gdev, session, data); + case VBOXGUEST_IOCTL_HGCM_CONNECT: +#ifdef CONFIG_X86_64 /* Needed because these are identical on 32 bit builds */ + case VBOXGUEST_IOCTL_HGCM_CONNECT_32: +#endif + return vbg_ioctl_hgcm_connect(gdev, session, data); + case VBOXGUEST_IOCTL_HGCM_DISCONNECT: +#ifdef CONFIG_X86_64 + case VBOXGUEST_IOCTL_HGCM_DISCONNECT_32: +#endif + return vbg_ioctl_hgcm_disconnect(gdev, session, data); + case VBOXGUEST_IOCTL_CHECK_BALLOON: + /* + * Under Linux we handle VMMDEV_EVENT_BALLOON_CHANGE_REQUEST + * events entirely in the kernel, see vbg_core_isr(). + */ + return VINF_SUCCESS; + case VBOXGUEST_IOCTL_CHANGE_BALLOON: + /* Under Linux we always handle the balloon in R0. */ + return VERR_PERMISSION_DENIED; + case VBOXGUEST_IOCTL_WRITE_CORE_DUMP: + return vbg_ioctl_write_core_dump(gdev, data); + case VBOXGUEST_IOCTL_SET_MOUSE_STATUS: + vbg_err("VGDrvCommonIoCtl: VBOXGUEST_IOCTL_SET_MOUSE_STATUS should not be used under Linux\n"); + return VERR_NOT_SUPPORTED; + case VBOXGUEST_IOCTL_GUEST_CAPS_ACQUIRE: + vbg_err("VGDrvCommonIoCtl: VBOXGUEST_IOCTL_GUEST_CAPS_ACQUIRE should not be used under Linux\n"); + return VERR_NOT_SUPPORTED; + case VBOXGUEST_IOCTL_SET_GUEST_CAPABILITIES: + return vbg_ioctl_set_capabilities(gdev, session, data); + } + + vbg_debug("VGDrvCommonIoCtl: Unknown req %#08x\n", req); + return VERR_NOT_SUPPORTED; +} + +/** Core interrupt service routine. */ +irqreturn_t vbg_core_isr(int irq, void *dev_id) +{ + struct vbg_dev *gdev = dev_id; + VMMDevEvents *req = gdev->ack_events_req; + bool mouse_position_changed = false; + unsigned long flags; + u32 events = 0; + int rc; + + if (!gdev->mmio->V.V1_04.fHaveEvents) + return IRQ_NONE; + + /* Get and acknowlegde events. */ + req->header.rc = VERR_INTERNAL_ERROR; + req->events = 0; + rc = vbg_req_perform(gdev, req); + if (rc < 0) { + vbg_err("Error performing VMMDevEvents req: %d\n", rc); + return IRQ_NONE; + } + + events = req->events; + + if (events & VMMDEV_EVENT_MOUSE_POSITION_CHANGED) { + mouse_position_changed = true; + events &= ~VMMDEV_EVENT_MOUSE_POSITION_CHANGED; + } + + if (events & VMMDEV_EVENT_HGCM) { + wake_up(&gdev->hgcm_wq); + events &= ~VMMDEV_EVENT_HGCM; + } + + if (events & VMMDEV_EVENT_BALLOON_CHANGE_REQUEST) { + schedule_work(&gdev->mem_balloon.work); + events &= ~VMMDEV_EVENT_BALLOON_CHANGE_REQUEST; + } + + if (events) { + spin_lock_irqsave(&gdev->event_spinlock, flags); + gdev->pending_events |= events; + spin_unlock_irqrestore(&gdev->event_spinlock, flags); + + wake_up(&gdev->event_wq); + } + + if (mouse_position_changed) + vbg_linux_mouse_event(gdev); + + return IRQ_HANDLED; +} diff --git a/drivers/virt/vboxguest/vboxguest_core.h b/drivers/virt/vboxguest/vboxguest_core.h new file mode 100644 index 000000000000..5a84ffce6104 --- /dev/null +++ b/drivers/virt/vboxguest/vboxguest_core.h @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2010-2016 Oracle Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, in which case the provisions of the CDDL are applicable + * instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + +#ifndef __VBOXGUEST_CORE_H__ +#define __VBOXGUEST_CORE_H__ + +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/miscdevice.h> +#include <linux/spinlock.h> +#include <linux/wait.h> +#include <linux/workqueue.h> +#include <linux/vbox_vmmdev.h> +#include <linux/vboxguest.h> + +struct vbg_session; + +/** VBox guest memory balloon. */ +struct vbg_mem_balloon { + /** Work handling VMMDEV_EVENT_BALLOON_CHANGE_REQUEST events */ + struct work_struct work; + /** Pre-allocated VMMDevGetMemBalloonChangeRequest for query */ + VMMDevGetMemBalloonChangeRequest *get_req; + /** Pre-allocated VMMDevChangeMemBalloon req for inflate / deflate */ + VMMDevChangeMemBalloon *change_req; + /** The current number of chunks in the balloon. */ + u32 chunks; + /** The maximum number of chunks in the balloon. */ + u32 max_chunks; + /** + * Array of pointers to page arrays. A page * array is allocated for + * each chunk when inflating, and freed when the deflating. + */ + struct page ***pages; +}; + +/** + * Per bit usage tracker for a u32 mask. + * + * Used for optimal handling of guest properties and event filter. + */ +struct vbg_bit_usage_tracker { + /** Per bit usage counters. */ + u32 per_bit_usage[32]; + /** The current mask according to per_bit_usage. */ + u32 mask; +}; + +/** VBox guest device (data) extension. */ +struct vbg_dev { + struct device *dev; + /** The base of the adapter I/O ports. */ + u16 io_port; + /** Pointer to the mapping of the VMMDev adapter memory. */ + VMMDevMemory *mmio; + /** Host version */ + char host_version[64]; + /** Host features */ + unsigned int host_features; + /** + * Dummy page and vmap address for reserved kernel virtual-address + * space for the guest mappings, only used on hosts lacking vtx. + */ + struct page *guest_mappings_dummy_page; + void *guest_mappings; + /** Spinlock protecting pending_events. */ + spinlock_t event_spinlock; + /** Preallocated VMMDevEvents for the IRQ handler. */ + VMMDevEvents *ack_events_req; + /** Wait-for-event list for threads waiting for multiple events. */ + wait_queue_head_t event_wq; + /** Mask of pending events. */ + u32 pending_events; + /** Wait-for-event list for threads waiting on HGCM async completion. */ + wait_queue_head_t hgcm_wq; + /** Pre-allocated hgcm cancel2 req. for cancellation on timeout */ + VMMDevHGCMCancel2 *cancel_req; + /** Mutex protecting cancel_req accesses */ + struct mutex cancel_req_mutex; + /** Pre-allocated mouse-status request for the input-device handling. */ + VMMDevReqMouseStatus *mouse_status_req; + /** Input device for reporting abs mouse coordinates to the guest. */ + struct input_dev *input; + + /** Spinlock various items in vbg_session. */ + spinlock_t session_spinlock; + /** List of guest sessions, protected by session_spinlock. */ + struct list_head session_list; + /** Memory balloon information. */ + struct vbg_mem_balloon mem_balloon; + + /** + * @name Host Event Filtering + * @{ + */ + + /** Events we won't permit anyone to filter out. */ + u32 fixed_events; + /** Usage counters for the host events (excludes fixed events). */ + struct vbg_bit_usage_tracker event_filter_tracker; + /** The event filter last reported to the host (or UINT32_MAX). */ + u32 event_filter_host; + /** @} */ + + /** + * @name Guest Capabilities + * @{ + */ + + /** + * Usage counters for guest capabilities. Indexed by capability bit + * number, one count per session using a capability. + */ + struct vbg_bit_usage_tracker guest_caps_tracker; + /** The guest capabilities last reported to the host (or UINT32_MAX). */ + u32 guest_caps_host; + /** @} */ + + /** + * Heartbeat timer which fires with interval + * cNsHearbeatInterval and its handler sends + * VMMDevReq_GuestHeartbeat to VMMDev. + */ + struct timer_list heartbeat_timer; + /** Heartbeat timer interval in ms. */ + int heartbeat_interval_ms; + /** Preallocated VMMDevReq_GuestHeartbeat request. */ + VMMDevRequestHeader *guest_heartbeat_req; + + /** "vboxguest" char-device */ + struct miscdevice misc_device; + /** "vboxuser" char-device */ + struct miscdevice misc_device_user; +}; + +/** The VBoxGuest per session data. */ +struct vbg_session { + /** The list node. */ + struct list_head list_node; + /** Pointer to the device extension. */ + struct vbg_dev *gdev; + + /** + * Array containing HGCM client IDs associated with this session. + * These will be automatically disconnected when the session is closed. + */ + u32 hgcm_client_ids[64]; + /** + * Host events requested by the session. + * An event type requested in any guest session will be added to the + * host filter. Protected by struct vbg_dev.SessionSpinlock. + */ + u32 event_filter; + /** + * Guest capabilities for this session. + * A capability claimed by any guest session will be reported to the + * host. Protected by struct vbg_dev.SessionSpinlock. + */ + u32 guest_caps; + /** Does this session belong to a root process or a user one? */ + bool user_session; + /** Set on CANCEL_ALL_WAITEVENTS, protected by the event_spinlock. */ + bool cancel_waiters; +}; + +int vbg_core_init(struct vbg_dev *gdev, u32 fixed_events); +void vbg_core_exit(struct vbg_dev *gdev); +int vbg_core_open_session(struct vbg_dev *gdev, + struct vbg_session **session_ret, bool user_session); +void vbg_core_close_session(struct vbg_session *session); +int vbg_core_ioctl(struct vbg_session *session, unsigned int req, void *data, + size_t data_size, size_t *data_size_ret); +int vbg_core_set_mouse_status(struct vbg_dev *gdev, u32 features); + +irqreturn_t vbg_core_isr(int irq, void *dev_id); + +void vbg_linux_mouse_event(struct vbg_dev *gdev); + +#endif diff --git a/drivers/virt/vboxguest/vboxguest_linux.c b/drivers/virt/vboxguest/vboxguest_linux.c new file mode 100644 index 000000000000..bc4268b161cc --- /dev/null +++ b/drivers/virt/vboxguest/vboxguest_linux.c @@ -0,0 +1,478 @@ +/* + * vboxguest linux pci driver, char-dev and input-device code, + * + * Copyright (C) 2006-2016 Oracle Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/input.h> +#include <linux/kernel.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/poll.h> +#include <linux/vbox_utils.h> +#include "vboxguest_core.h" + +/** The device name. */ +#define DEVICE_NAME "vboxguest" +/** The device name for the device node open to everyone. */ +#define DEVICE_NAME_USER "vboxuser" +/** VirtualBox PCI vendor ID. */ +#define VBOX_VENDORID 0x80ee +/** VMMDev PCI card product ID. */ +#define VMMDEV_DEVICEID 0xcafe + +/** Mutex protecting the global vbg_gdev pointer used by vbg_get/put_gdev. */ +static DEFINE_MUTEX(vbg_gdev_mutex); +/** Global vbg_gdev pointer used by vbg_get/put_gdev. */ +static struct vbg_dev *vbg_gdev; + +static int vbg_misc_device_open(struct inode *inode, struct file *filp) +{ + struct vbg_session *session; + struct vbg_dev *gdev; + int ret; + + /* misc_open sets filp->private_data to our misc device */ + gdev = container_of(filp->private_data, struct vbg_dev, misc_device); + + ret = vbg_core_open_session(gdev, &session, false); + if (ret) + return -ENOMEM; + + filp->private_data = session; + return 0; +} + +static int vbg_misc_device_user_open(struct inode *inode, struct file *filp) +{ + struct vbg_session *session; + struct vbg_dev *gdev; + int ret; + + /* misc_open sets filp->private_data to our misc device */ + gdev = container_of(filp->private_data, struct vbg_dev, + misc_device_user); + + ret = vbg_core_open_session(gdev, &session, true); + if (ret) + return ret; + + filp->private_data = session; + return 0; +} + +/** + * Close device. + * + * @param inode Pointer to inode info structure. + * @param filp Associated file pointer. + */ +static int vbg_misc_device_close(struct inode *inode, struct file *filp) +{ + vbg_core_close_session(filp->private_data); + filp->private_data = NULL; + return 0; +} + +/** + * Device I/O Control entry point. + * + * @returns -ENOMEM or -EFAULT for errors inside the ioctl callback; 0 + * on success, or a positive VBox status code on vbox guest-device errors. + * + * @param filp Associated file pointer. + * @param req The request specified to ioctl(). + * @param arg The argument specified to ioctl(). + */ +static long vbg_misc_device_ioctl(struct file *filp, unsigned int req, + unsigned long arg) +{ + struct vbg_session *session = filp->private_data; + size_t returned_data_size, size = _IOC_SIZE(req); + void *buf, *buf_free = NULL; + u8 stack_buf[32]; + int rc, ret = 0; + + /* + * The largest ioctl req is a hgcm-call with 32 function arguments, + * which is 16 + 32 * 16 bytes. However the variable length buffer + * passed to VMMDevReq_LogString may be longer. Allow logging up to + * 1024 bytes (minus header size). + */ + if (size > 1024) + return -E2BIG; + + /* + * For small amounts of data being passed we use a stack based buffer + * except for VMMREQUESTs where the data must not be on the stack. + */ + if (size <= sizeof(stack_buf) && + _IOC_NR(req) != _IOC_NR(VBOXGUEST_IOCTL_VMMREQUEST(0))) { + buf = stack_buf; + } else { + /* __GFP_DMA32 for VBOXGUEST_IOCTL_VMMREQUEST */ + buf_free = buf = kmalloc(size, GFP_KERNEL | __GFP_DMA32); + if (!buf) + return -ENOMEM; + } + + if (copy_from_user(buf, (void *)arg, size)) { + ret = -EFAULT; + goto out; + } + + rc = vbg_core_ioctl(session, req, buf, size, &returned_data_size); + if (rc < 0) { + /* Negate the Vbox status code to make it positive. */ + ret = -rc; + goto out; + } + + if (returned_data_size > size) { + vbg_debug("%s: too much output data %zu > %zu\n", + __func__, returned_data_size, size); + returned_data_size = size; + } + if (returned_data_size > 0) { + if (copy_to_user((void *)arg, buf, returned_data_size) != 0) + ret = -EFAULT; + } + +out: + kfree(buf_free); + + return ret; +} + +/** The file_operations structures. */ +static const struct file_operations vbg_misc_device_fops = { + .owner = THIS_MODULE, + .open = vbg_misc_device_open, + .release = vbg_misc_device_close, + .unlocked_ioctl = vbg_misc_device_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = vbg_misc_device_ioctl, +#endif +}; +static const struct file_operations vbg_misc_device_user_fops = { + .owner = THIS_MODULE, + .open = vbg_misc_device_user_open, + .release = vbg_misc_device_close, + .unlocked_ioctl = vbg_misc_device_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = vbg_misc_device_ioctl, +#endif +}; + +/** + * Called when the input device is first opened. + * + * Sets up absolute mouse reporting. + */ +static int vbg_input_open(struct input_dev *input) +{ + struct vbg_dev *gdev = input_get_drvdata(input); + u32 feat = VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE | VMMDEV_MOUSE_NEW_PROTOCOL; + int ret; + + ret = vbg_core_set_mouse_status(gdev, feat); + if (ret) + return ret; + + return 0; +} + +/** + * Called if all open handles to the input device are closed. + * + * Disables absolute reporting. + */ +static void vbg_input_close(struct input_dev *input) +{ + struct vbg_dev *gdev = input_get_drvdata(input); + + vbg_core_set_mouse_status(gdev, 0); +} + +/** + * Creates the kernel input device. + * + * @returns 0 on success, negated errno on failure. + */ +static int vbg_create_input_device(struct vbg_dev *gdev) +{ + struct input_dev *input; + + input = devm_input_allocate_device(gdev->dev); + if (!input) + return -ENOMEM; + + input->id.bustype = BUS_PCI; + input->id.vendor = VBOX_VENDORID; + input->id.product = VMMDEV_DEVICEID; + input->open = vbg_input_open; + input->close = vbg_input_close; + input->dev.parent = gdev->dev; + input->name = "VirtualBox mouse integration"; + + input_set_abs_params(input, ABS_X, VMMDEV_MOUSE_RANGE_MIN, + VMMDEV_MOUSE_RANGE_MAX, 0, 0); + input_set_abs_params(input, ABS_Y, VMMDEV_MOUSE_RANGE_MIN, + VMMDEV_MOUSE_RANGE_MAX, 0, 0); + input_set_capability(input, EV_KEY, BTN_MOUSE); + input_set_drvdata(input, gdev); + + gdev->input = input; + + return input_register_device(gdev->input); +} + +static ssize_t host_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct vbg_dev *gdev = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", gdev->host_version); +} + +static ssize_t host_features_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct vbg_dev *gdev = dev_get_drvdata(dev); + + return sprintf(buf, "%#x\n", gdev->host_features); +} + +static DEVICE_ATTR_RO(host_version); +static DEVICE_ATTR_RO(host_features); + +/** + * Does the PCI detection and init of the device. + * + * @returns 0 on success, negated errno on failure. + */ +static int vbg_pci_probe(struct pci_dev *pci, const struct pci_device_id *id) +{ + struct device *dev = &pci->dev; + resource_size_t io, io_len, mmio, mmio_len; + VMMDevMemory *vmmdev; + struct vbg_dev *gdev; + int ret; + + gdev = devm_kzalloc(dev, sizeof(*gdev), GFP_KERNEL); + if (!gdev) + return -ENOMEM; + + ret = pci_enable_device(pci); + if (ret != 0) { + vbg_err("vboxguest: Error enabling device: %d\n", ret); + return ret; + } + + ret = -ENODEV; + + io = pci_resource_start(pci, 0); + io_len = pci_resource_len(pci, 0); + if (!io || !io_len) { + vbg_err("vboxguest: Error IO-port resource (0) is missing\n"); + goto err_disable_pcidev; + } + if (devm_request_region(dev, io, io_len, DEVICE_NAME) == NULL) { + vbg_err("vboxguest: Error could not claim IO resource\n"); + ret = -EBUSY; + goto err_disable_pcidev; + } + + mmio = pci_resource_start(pci, 1); + mmio_len = pci_resource_len(pci, 1); + if (!mmio || !mmio_len) { + vbg_err("vboxguest: Error MMIO resource (1) is missing\n"); + goto err_disable_pcidev; + } + + if (devm_request_mem_region(dev, mmio, mmio_len, DEVICE_NAME) == NULL) { + vbg_err("vboxguest: Error could not claim MMIO resource\n"); + ret = -EBUSY; + goto err_disable_pcidev; + } + + vmmdev = devm_ioremap(dev, mmio, mmio_len); + if (!vmmdev) { + vbg_err("vboxguest: Error ioremap failed; MMIO addr=%p size=%d\n", + (void *)mmio, (int)mmio_len); + goto err_disable_pcidev; + } + + /* Validate MMIO region version and size. */ + if (vmmdev->u32Version != VMMDEV_MEMORY_VERSION || + vmmdev->u32Size < 32 || vmmdev->u32Size > mmio_len) { + vbg_err("vboxguest: Bogus VMMDev memory; u32Version=%08x (expected %08x) u32Size=%d (expected <= %d)\n", + vmmdev->u32Version, VMMDEV_MEMORY_VERSION, + vmmdev->u32Size, (int)mmio_len); + goto err_disable_pcidev; + } + + gdev->io_port = io; + gdev->mmio = vmmdev; + gdev->dev = dev; + gdev->misc_device.minor = MISC_DYNAMIC_MINOR; + gdev->misc_device.name = DEVICE_NAME; + gdev->misc_device.fops = &vbg_misc_device_fops; + gdev->misc_device_user.minor = MISC_DYNAMIC_MINOR; + gdev->misc_device_user.name = DEVICE_NAME_USER; + gdev->misc_device_user.fops = &vbg_misc_device_user_fops; + + ret = vbg_core_init(gdev, VMMDEV_EVENT_MOUSE_POSITION_CHANGED); + if (ret) + goto err_disable_pcidev; + + ret = vbg_create_input_device(gdev); + if (ret) { + vbg_err("vboxguest: Error creating input device: %d\n", ret); + goto err_vbg_core_exit; + } + + ret = devm_request_irq(dev, pci->irq, vbg_core_isr, IRQF_SHARED, + DEVICE_NAME, gdev); + if (ret) { + vbg_err("vboxguest: Error requesting irq: %d\n", ret); + goto err_vbg_core_exit; + } + + ret = misc_register(&gdev->misc_device); + if (ret) { + vbg_err("vboxguest: Error misc_register %s failed: %d\n", + DEVICE_NAME, ret); + goto err_vbg_core_exit; + } + + ret = misc_register(&gdev->misc_device_user); + if (ret) { + vbg_err("vboxguest: Error misc_register %s failed: %d\n", + DEVICE_NAME_USER, ret); + goto err_unregister_misc_device; + } + + mutex_lock(&vbg_gdev_mutex); + if (!vbg_gdev) + vbg_gdev = gdev; + else + ret = -EBUSY; + mutex_unlock(&vbg_gdev_mutex); + + if (ret) { + vbg_err("vboxguest: Error more then 1 vbox guest pci device\n"); + goto err_unregister_misc_device_user; + } + + pci_set_drvdata(pci, gdev); + device_create_file(dev, &dev_attr_host_version); + device_create_file(dev, &dev_attr_host_features); + + vbg_info("vboxguest: misc device minor %d, IRQ %d, I/O port %x, MMIO at %p (size %d)\n", + gdev->misc_device.minor, pci->irq, gdev->io_port, + (void *)mmio, (int)mmio_len); + + return 0; + +err_unregister_misc_device_user: + misc_deregister(&gdev->misc_device_user); +err_unregister_misc_device: + misc_deregister(&gdev->misc_device); +err_vbg_core_exit: + vbg_core_exit(gdev); +err_disable_pcidev: + pci_disable_device(pci); + + return ret; +} + +static void vbg_pci_remove(struct pci_dev *pci) +{ + struct vbg_dev *gdev = pci_get_drvdata(pci); + + mutex_lock(&vbg_gdev_mutex); + vbg_gdev = NULL; + mutex_unlock(&vbg_gdev_mutex); + + device_remove_file(gdev->dev, &dev_attr_host_features); + device_remove_file(gdev->dev, &dev_attr_host_version); + misc_deregister(&gdev->misc_device_user); + misc_deregister(&gdev->misc_device); + vbg_core_exit(gdev); + pci_disable_device(pci); +} + +struct vbg_dev *vbg_get_gdev(void) +{ + mutex_lock(&vbg_gdev_mutex); + + /* + * Note on success we keep the mutex locked until vbg_put_gdev(), + * this stops vbg_pci_remove from removing the device from underneath + * vboxsf. vboxsf will only hold a reference for a short while. + */ + if (vbg_gdev) + return vbg_gdev; + + mutex_unlock(&vbg_gdev_mutex); + return ERR_PTR(-ENODEV); +} +EXPORT_SYMBOL(vbg_get_gdev); + +void vbg_put_gdev(struct vbg_dev *gdev) +{ + WARN_ON(gdev != vbg_gdev); + mutex_unlock(&vbg_gdev_mutex); +} +EXPORT_SYMBOL(vbg_put_gdev); + +/** + * Callback for mouse events. + * + * This is called at the end of the ISR, after leaving the event spinlock, if + * VMMDEV_EVENT_MOUSE_POSITION_CHANGED was raised by the host. + * + * @param gdev The device extension. + */ +void vbg_linux_mouse_event(struct vbg_dev *gdev) +{ + int rc; + + /* Report events to the kernel input device */ + gdev->mouse_status_req->mouseFeatures = 0; + gdev->mouse_status_req->pointerXPos = 0; + gdev->mouse_status_req->pointerYPos = 0; + rc = vbg_req_perform(gdev, gdev->mouse_status_req); + if (rc >= 0) { + input_report_abs(gdev->input, ABS_X, + gdev->mouse_status_req->pointerXPos); + input_report_abs(gdev->input, ABS_Y, + gdev->mouse_status_req->pointerYPos); + input_sync(gdev->input); + } +} + +static const struct pci_device_id vbg_pci_ids[] = { + { .vendor = VBOX_VENDORID, .device = VMMDEV_DEVICEID }, + {} +}; +MODULE_DEVICE_TABLE(pci, vbg_pci_ids); + +static struct pci_driver vbg_pci_driver = { + .name = DEVICE_NAME, + .id_table = vbg_pci_ids, + .probe = vbg_pci_probe, + .remove = vbg_pci_remove, +}; + +module_pci_driver(vbg_pci_driver); + +MODULE_AUTHOR("Oracle Corporation"); +MODULE_DESCRIPTION("Oracle VM VirtualBox Guest Additions for Linux Module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/virt/vboxguest/vboxguest_utils.c b/drivers/virt/vboxguest/vboxguest_utils.c new file mode 100644 index 000000000000..4d5a0b64f312 --- /dev/null +++ b/drivers/virt/vboxguest/vboxguest_utils.c @@ -0,0 +1,1094 @@ +/* + * vboxguest vmm-req and hgcm-call code, GenericRequest.c, HGCMInternal.c + * and RTErrConvertToErrno.cpp in vbox upstream svn. + * + * Copyright (C) 2006-2016 Oracle Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, in which case the provisions of the CDDL are applicable + * instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/sizes.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/vmalloc.h> +#include <linux/vbox_err.h> +#include <linux/vbox_utils.h> +#include "vboxguest_core.h" + +/** The max parameter buffer size for a user request. */ +#define VBGLR0_MAX_HGCM_USER_PARM (24 * SZ_1M) +/** The max parameter buffer size for a kernel request. */ +#define VBGLR0_MAX_HGCM_KERNEL_PARM (16 * SZ_1M) + +#define VBG_DEBUG_PORT 0x504 + +/* This protects vbg_log_buf and serializes VBG_DEBUG_PORT accesses */ +static DEFINE_SPINLOCK(vbg_log_lock); +static char vbg_log_buf[128]; + +#define VBG_LOG(name, pr_func) \ +void name(const char *fmt, ...) \ +{ \ + unsigned long flags; \ + va_list args; \ + int i, count; \ + \ + va_start(args, fmt); \ + spin_lock_irqsave(&vbg_log_lock, flags); \ + \ + count = vscnprintf(vbg_log_buf, sizeof(vbg_log_buf), fmt, args);\ + for (i = 0; i < count; i++) \ + outb(vbg_log_buf[i], VBG_DEBUG_PORT); \ + \ + pr_func("%s", vbg_log_buf); \ + \ + spin_unlock_irqrestore(&vbg_log_lock, flags); \ + va_end(args); \ +} \ +EXPORT_SYMBOL(name) + +VBG_LOG(vbg_info, pr_info); +VBG_LOG(vbg_warn, pr_warn); +VBG_LOG(vbg_err, pr_err); +#if defined(DEBUG) && !defined(CONFIG_DYNAMIC_DEBUG) +VBG_LOG(vbg_debug, pr_debug); +#endif + +/** + * Helper to determine the minimum request size for the given request. + * Returns 0 if the given operation is not handled and/or supported. + * + * @returns Size. + * @param req The VMMDev request to get the size for. + */ +static size_t vbg_req_get_min_size(const VMMDevRequestHeader *req) +{ + switch (req->requestType) { + case VMMDevReq_GetMouseStatus: + case VMMDevReq_SetMouseStatus: + return sizeof(VMMDevReqMouseStatus); + case VMMDevReq_SetPointerShape: + return sizeof(VMMDevReqMousePointer); + case VMMDevReq_GetHostVersion: + return sizeof(VMMDevReqHostVersion); + case VMMDevReq_Idle: + return sizeof(VMMDevReqIdle); + case VMMDevReq_GetHostTime: + return sizeof(VMMDevReqHostTime); + case VMMDevReq_GetHypervisorInfo: + case VMMDevReq_SetHypervisorInfo: + return sizeof(VMMDevReqHypervisorInfo); + case VMMDevReq_RegisterPatchMemory: + case VMMDevReq_DeregisterPatchMemory: + return sizeof(VMMDevReqPatchMemory); + case VMMDevReq_SetPowerStatus: + return sizeof(VMMDevPowerStateRequest); + case VMMDevReq_AcknowledgeEvents: + return sizeof(VMMDevEvents); + case VMMDevReq_ReportGuestInfo: + return sizeof(VMMDevReportGuestInfo); + case VMMDevReq_ReportGuestInfo2: + return sizeof(VMMDevReportGuestInfo2); + case VMMDevReq_ReportGuestStatus: + return sizeof(VMMDevReportGuestStatus); + case VMMDevReq_ReportGuestUserState: + return sizeof(VMMDevReportGuestUserState); + case VMMDevReq_GetDisplayChangeRequest: + return sizeof(VMMDevDisplayChangeRequest); + case VMMDevReq_GetDisplayChangeRequest2: + return sizeof(VMMDevDisplayChangeRequest2); + case VMMDevReq_GetDisplayChangeRequestEx: + return sizeof(VMMDevDisplayChangeRequestEx); + case VMMDevReq_VideoModeSupported: + return sizeof(VMMDevVideoModeSupportedRequest); + case VMMDevReq_GetHeightReduction: + return sizeof(VMMDevGetHeightReductionRequest); + case VMMDevReq_ReportGuestCapabilities: + return sizeof(VMMDevReqGuestCapabilities); + case VMMDevReq_SetGuestCapabilities: + return sizeof(VMMDevReqGuestCapabilities2); + case VMMDevReq_HGCMConnect: + return sizeof(VMMDevHGCMConnect); + case VMMDevReq_HGCMDisconnect: + return sizeof(VMMDevHGCMDisconnect); + case VMMDevReq_HGCMCall32: + return sizeof(VMMDevHGCMCall); + case VMMDevReq_HGCMCall64: + return sizeof(VMMDevHGCMCall); + case VMMDevReq_HGCMCancel: + return sizeof(VMMDevHGCMCancel); + case VMMDevReq_VideoAccelEnable: + return sizeof(VMMDevVideoAccelEnable); + case VMMDevReq_VideoAccelFlush: + return sizeof(VMMDevVideoAccelFlush); + case VMMDevReq_VideoSetVisibleRegion: + /* + * The original protocol didn't consider a guest with NO + * visible windows. + */ + return sizeof(VMMDevVideoSetVisibleRegion) - sizeof(RTRECT); + case VMMDevReq_GetSeamlessChangeRequest: + return sizeof(VMMDevSeamlessChangeRequest); + case VMMDevReq_QueryCredentials: + return sizeof(VMMDevCredentials); + case VMMDevReq_ReportGuestStats: + return sizeof(VMMDevReportGuestStats); + case VMMDevReq_GetMemBalloonChangeRequest: + return sizeof(VMMDevGetMemBalloonChangeRequest); + case VMMDevReq_GetStatisticsChangeRequest: + return sizeof(VMMDevGetStatisticsChangeRequest); + case VMMDevReq_ChangeMemBalloon: + return sizeof(VMMDevChangeMemBalloon); + case VMMDevReq_GetVRDPChangeRequest: + return sizeof(VMMDevVRDPChangeRequest); + case VMMDevReq_LogString: + return sizeof(VMMDevReqLogString); + case VMMDevReq_CtlGuestFilterMask: + return sizeof(VMMDevCtlGuestFilterMask); + case VMMDevReq_GetCpuHotPlugRequest: + return sizeof(VMMDevGetCpuHotPlugRequest); + case VMMDevReq_SetCpuHotPlugStatus: + return sizeof(VMMDevCpuHotPlugStatusRequest); + case VMMDevReq_RegisterSharedModule: + return sizeof(VMMDevSharedModuleRegistrationRequest); + case VMMDevReq_UnregisterSharedModule: + return sizeof(VMMDevSharedModuleUnregistrationRequest); + case VMMDevReq_CheckSharedModules: + return sizeof(VMMDevSharedModuleCheckRequest); + case VMMDevReq_GetPageSharingStatus: + return sizeof(VMMDevPageSharingStatusRequest); + case VMMDevReq_DebugIsPageShared: + return sizeof(VMMDevPageIsSharedRequest); + case VMMDevReq_GetSessionId: + return sizeof(VMMDevReqSessionId); + case VMMDevReq_HeartbeatConfigure: + return sizeof(VMMDevReqHeartbeat); + case VMMDevReq_GuestHeartbeat: + return sizeof(VMMDevRequestHeader); + default: + break; + } + + return 0; +} + +int vbg_req_verify(const VMMDevRequestHeader *req, size_t buffer_size) +{ + size_t min_size; + + if (!req || buffer_size < sizeof(VMMDevRequestHeader)) { + vbg_debug("VbglGRVerify: Invalid parameter: req = %p, buffer_size = %zu\n", + req, buffer_size); + return VERR_INVALID_PARAMETER; + } + + if (req->size > buffer_size) { + vbg_debug("VbglGRVerify: request size %u > buffer size %zu\n", + req->size, buffer_size); + return VERR_INVALID_PARAMETER; + } + + /* The request size must correspond to the request type. */ + min_size = vbg_req_get_min_size(req); + + if (buffer_size < min_size) { + vbg_debug("VbglGRVerify: buffer size %zu < expected size %zu\n", + buffer_size, min_size); + return VERR_INVALID_PARAMETER; + } + + if (req->size < min_size) { + vbg_debug("VbglGRVerify: header size %u < expected size %zu\n", + req->size, min_size); + return VERR_INVALID_PARAMETER; + } + + if (buffer_size == min_size) { + /* + * This is most likely a fixed size request, and in this case + * the request size must be also equal to the expected size. + */ + if (req->size != min_size) { + vbg_debug("VbglGRVerify: request size %u != expected size %zu\n", + req->size, min_size); + return VERR_INVALID_PARAMETER; + } + + return VINF_SUCCESS; + } + + /* + * This can be a variable size request. Check the request type and limit + * the size to VMMDEV_MAX_VMMDEVREQ_SIZE, which is max size supported by + * the host. + * + * Note: Keep this list sorted for easier human lookup! + */ + if (req->requestType == VMMDevReq_ChangeMemBalloon || + req->requestType == VMMDevReq_HGCMCall32 || + req->requestType == VMMDevReq_HGCMCall64 || + req->requestType == VMMDevReq_RegisterSharedModule || + req->requestType == VMMDevReq_ReportGuestUserState || + req->requestType == VMMDevReq_LogString || + req->requestType == VMMDevReq_SetPointerShape || + req->requestType == VMMDevReq_VideoSetVisibleRegion) { + if (buffer_size > VMMDEV_MAX_VMMDEVREQ_SIZE) { + vbg_debug("VbglGRVerify: VMMDevReq_LogString: buffer size %zu too big\n", + buffer_size); + return VERR_BUFFER_OVERFLOW; + } + } else { + vbg_debug("VbglGRVerify: unknown request-type %#08x\n", + req->requestType); + return VERR_IO_BAD_LENGTH; /* ??? */ + } + + return VINF_SUCCESS; +} + +void *vbg_req_alloc(size_t len, VMMDevRequestType req_type) +{ + VMMDevRequestHeader *req; + + req = kmalloc(len, GFP_KERNEL | __GFP_DMA32); + if (!req) + return NULL; + + memset(req, 0xaa, len); + + req->size = len; + req->version = VMMDEV_REQUEST_HEADER_VERSION; + req->requestType = req_type; + req->rc = VERR_GENERAL_FAILURE; + req->reserved1 = 0; + req->reserved2 = 0; + + return req; +} + +/* Note this function returns a VBox status code, not a negative errno!! */ +int vbg_req_perform(struct vbg_dev *gdev, void *req) +{ + unsigned long phys_req = virt_to_phys(req); + + outl(phys_req, gdev->io_port + VMMDEV_PORT_OFF_REQUEST); + /* + * The host changes the request as a result of the outl, make sure + * the outl and any reads of the req happen in the correct order. + */ + mb(); + + return ((VMMDevRequestHeader *)req)->rc; +} + +static bool hgcm_req_done(struct vbg_dev *gdev, + VMMDevHGCMRequestHeader *header) +{ + unsigned long flags; + bool done; + + spin_lock_irqsave(&gdev->event_spinlock, flags); + done = header->fu32Flags & VBOX_HGCM_REQ_DONE; + spin_unlock_irqrestore(&gdev->event_spinlock, flags); + + return done; +} + +int vbg_hgcm_connect(struct vbg_dev *gdev, HGCMServiceLocation *loc, + u32 *client_id) +{ + VMMDevHGCMConnect *hgcm_connect = NULL; + int rc; + + hgcm_connect = vbg_req_alloc(sizeof(*hgcm_connect), + VMMDevReq_HGCMConnect); + if (!hgcm_connect) + return VERR_NO_MEMORY; + + hgcm_connect->header.fu32Flags = 0; + memcpy(&hgcm_connect->loc, loc, sizeof(*loc)); + hgcm_connect->u32ClientID = 0; + + rc = vbg_req_perform(gdev, hgcm_connect); + + if (rc == VINF_HGCM_ASYNC_EXECUTE) + wait_event(gdev->hgcm_wq, + hgcm_req_done(gdev, &hgcm_connect->header)); + + if (rc >= 0) { + *client_id = hgcm_connect->u32ClientID; + rc = hgcm_connect->header.result; + } + + kfree(hgcm_connect); + + return rc; +} +EXPORT_SYMBOL(vbg_hgcm_connect); + +int vbg_hgcm_disconnect(struct vbg_dev *gdev, u32 client_id) +{ + VMMDevHGCMDisconnect *hgcm_disconnect = NULL; + int rc; + + hgcm_disconnect = vbg_req_alloc(sizeof(*hgcm_disconnect), + VMMDevReq_HGCMDisconnect); + if (!hgcm_disconnect) + return VERR_NO_MEMORY; + + hgcm_disconnect->header.fu32Flags = 0; + hgcm_disconnect->u32ClientID = client_id; + + rc = vbg_req_perform(gdev, hgcm_disconnect); + + if (rc == VINF_HGCM_ASYNC_EXECUTE) + wait_event(gdev->hgcm_wq, + hgcm_req_done(gdev, &hgcm_disconnect->header)); + + if (rc >= 0) + rc = hgcm_disconnect->header.result; + + kfree(hgcm_disconnect); + + return rc; +} +EXPORT_SYMBOL(vbg_hgcm_disconnect); + +static u32 hgcm_call_buf_size_in_pages(void *buf, u32 len) +{ + u32 size = PAGE_ALIGN(len + ((unsigned long)buf & ~PAGE_MASK)); + + return size >> PAGE_SHIFT; +} + +static void hgcm_call_inc_pcb_extra(void *buf, u32 len, size_t *pcb_extra) +{ + u32 pages; + + pages = hgcm_call_buf_size_in_pages(buf, len); + *pcb_extra += offsetof(HGCMPageListInfo, aPages[pages]); +} + +/* Kernel mode use only, use WARN_ON for sanity checks. */ +static int hgcm_call_check_pagelist(const HGCMFunctionParameter *src_parm, + const VBoxGuestHGCMCallInfo *info, u32 info_size, size_t *pcb_extra) +{ + HGCMPageListInfo *pg_lst; + u32 u, offset, size; + + offset = src_parm->u.PageList.offset; + size = src_parm->u.PageList.size; + if (!size) + return VINF_SUCCESS; + + if (WARN_ON(size > VBGLR0_MAX_HGCM_KERNEL_PARM)) + return VERR_OUT_OF_RANGE; + + if (WARN_ON(offset < info->cParms * sizeof(HGCMFunctionParameter) || + offset > info_size - sizeof(HGCMPageListInfo))) + return VERR_INVALID_PARAMETER; + + pg_lst = (HGCMPageListInfo *)((u8 *)info + offset); + + u = offset + offsetof(HGCMPageListInfo, aPages[pg_lst->cPages]); + if (WARN_ON(u > info_size)) + return VERR_INVALID_PARAMETER; + + if (WARN_ON(pg_lst->offFirstPage >= PAGE_SIZE)) + return VERR_INVALID_PARAMETER; + + u = PAGE_ALIGN(pg_lst->offFirstPage + size) >> PAGE_SHIFT; + if (WARN_ON(u != pg_lst->cPages)) + return VERR_INVALID_PARAMETER; + + if (WARN_ON(!VBOX_HGCM_F_PARM_ARE_VALID(pg_lst->flags))) + return VERR_INVALID_PARAMETER; + + for (u = 0; u < pg_lst->cPages; u++) { + if (WARN_ON(pg_lst->aPages[u] & + (0xfff0000000000000ULL | ~PAGE_MASK))) + return VERR_INVALID_PARAMETER; + } + + *pcb_extra += offsetof(HGCMPageListInfo, aPages[pg_lst->cPages]); + + return VINF_SUCCESS; +} + +static int hgcm_call_preprocess_linaddr(const HGCMFunctionParameter *src_parm, + bool is_user, void **bounce_buf_ret, + size_t *pcb_extra) +{ + void *buf, *bounce_buf; + bool copy_in; + u32 len; + int ret; + + buf = (void *)src_parm->u.Pointer.u.linearAddr; + len = src_parm->u.Pointer.size; + copy_in = src_parm->type != VMMDevHGCMParmType_LinAddr_Out; + + if (!is_user) { + if (WARN_ON(len > VBGLR0_MAX_HGCM_KERNEL_PARM)) + return VERR_OUT_OF_RANGE; + + hgcm_call_inc_pcb_extra(buf, len, pcb_extra); + return VINF_SUCCESS; + } + + if (len > VBGLR0_MAX_HGCM_USER_PARM) + return VERR_OUT_OF_RANGE; + + bounce_buf = kvmalloc(len, GFP_KERNEL); + if (!bounce_buf) + return VERR_NO_MEMORY; + + if (copy_in) { + ret = copy_from_user(bounce_buf, (void __user *)buf, len); + if (ret) + return VERR_ACCESS_DENIED; + } else { + memset(bounce_buf, 0, len); + } + + *bounce_buf_ret = bounce_buf; + hgcm_call_inc_pcb_extra(bounce_buf, len, pcb_extra); + return VINF_SUCCESS; +} + +/** + * Preprocesses the HGCM call, validate parameters, alloc bounce buffers and + * figure out how much extra storage we need for page lists. + * + * @returns VBox status code + * + * @param info The call info. + * @param info_size The size of the call info structure. + * @param is_user Is it a user request or kernel request. + * @param bounce_bufs_ret Where to return the allocated bouncebuffer array + * @param pcb_extra Where to return the extra request space needed for + * physical page lists. + */ +static int hgcm_call_preprocess(const VBoxGuestHGCMCallInfo *info, + u32 info_size, bool is_user, void ***bounce_bufs_ret, size_t *pcb_extra) +{ + const HGCMFunctionParameter *src_parm = VBOXGUEST_HGCM_CALL_PARMS(info); + u32 i, parms = info->cParms; + void **bounce_bufs = NULL; + int rc; + + *bounce_bufs_ret = NULL; + *pcb_extra = 0; + + for (i = 0; i < parms; i++, src_parm++) { + switch (src_parm->type) { + case VMMDevHGCMParmType_32bit: + case VMMDevHGCMParmType_64bit: + break; + + case VMMDevHGCMParmType_PageList: + if (is_user) + return VERR_INVALID_PARAMETER; + + rc = hgcm_call_check_pagelist(src_parm, info, info_size, + pcb_extra); + if (rc) + return rc; + + break; + + case VMMDevHGCMParmType_LinAddr_In: + case VMMDevHGCMParmType_LinAddr_Out: + case VMMDevHGCMParmType_LinAddr: + if (is_user && !bounce_bufs) { + bounce_bufs = kcalloc(parms, sizeof(void *), + GFP_KERNEL); + if (!bounce_bufs) + return VERR_NO_MEMORY; + + *bounce_bufs_ret = bounce_bufs; + } + + rc = hgcm_call_preprocess_linaddr(src_parm, is_user, + &bounce_bufs[i], + pcb_extra); + if (rc) + return rc; + + break; + + default: + return VERR_INVALID_PARAMETER; + } + } + + return VINF_SUCCESS; +} + +/** + * Translates linear address types to page list direction flags. + * + * @returns page list flags. + * @param type The type. + */ +static u32 hgcm_call_linear_addr_type_to_pagelist_flags( + HGCMFunctionParameterType type) +{ + switch (type) { + case VMMDevHGCMParmType_LinAddr_In: + return VBOX_HGCM_F_PARM_DIRECTION_TO_HOST; + + case VMMDevHGCMParmType_LinAddr_Out: + return VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST; + + default: + WARN_ON(1); + case VMMDevHGCMParmType_LinAddr: + return VBOX_HGCM_F_PARM_DIRECTION_BOTH; + } +} + +static void hgcm_call_init_pagelist(VMMDevHGCMCall *call, + const VBoxGuestHGCMCallInfo *info, + HGCMFunctionParameter *dst_parm, + const HGCMFunctionParameter *src_parm, + u32 *off_extra) +{ + const HGCMPageListInfo *src_pg_list; + HGCMPageListInfo *dst_pg_list; + u32 i, pages; + + dst_parm->type = VMMDevHGCMParmType_PageList; + dst_parm->u.PageList.size = src_parm->u.PageList.size; + + if (src_parm->u.PageList.size == 0) { + dst_parm->u.PageList.offset = 0; + return; + } + + src_pg_list = (void *)info + src_parm->u.PageList.offset; + dst_pg_list = (void *)call + *off_extra; + pages = src_pg_list->cPages; + + dst_parm->u.PageList.offset = *off_extra; + dst_pg_list->flags = src_pg_list->flags; + dst_pg_list->offFirstPage = src_pg_list->offFirstPage; + dst_pg_list->cPages = pages; + + for (i = 0; i < pages; i++) + dst_pg_list->aPages[i] = src_pg_list->aPages[i]; + + *off_extra += offsetof(HGCMPageListInfo, aPages[pages]); +} + +static void hgcm_call_init_linaddr(VMMDevHGCMCall *call, + HGCMFunctionParameter *dst_parm, + void *buf, u32 len, + HGCMFunctionParameterType type, + u32 *off_extra) +{ + HGCMPageListInfo *dst_pg_lst; + struct page *page; + bool is_vmalloc; + u32 i, pages; + + dst_parm->type = type; + + if (len == 0) { + dst_parm->u.Pointer.size = 0; + dst_parm->u.Pointer.u.linearAddr = 0; + return; + } + + dst_pg_lst = (void *)call + *off_extra; + pages = hgcm_call_buf_size_in_pages(buf, len); + is_vmalloc = is_vmalloc_addr(buf); + + dst_parm->type = VMMDevHGCMParmType_PageList; + dst_parm->u.PageList.size = len; + dst_parm->u.PageList.offset = *off_extra; + dst_pg_lst->flags = hgcm_call_linear_addr_type_to_pagelist_flags(type); + dst_pg_lst->offFirstPage = (unsigned long)buf & ~PAGE_MASK; + dst_pg_lst->cPages = pages; + + for (i = 0; i < pages; i++) { + if (is_vmalloc) + page = vmalloc_to_page(buf); + else + page = virt_to_page(buf); + + dst_pg_lst->aPages[i] = page_to_phys(page); + buf += PAGE_SIZE; + } + + *off_extra += offsetof(HGCMPageListInfo, aPages[pages]); +} + +/** + * Initializes the call request that we're sending to the host. + * + * @param call The call to initialize. + * @param info The call info. + * @param info_size The size of the call info structure. + * @param bounce_bufs The bouncebuffer array. + */ +static void hgcm_call_init_call(VMMDevHGCMCall *call, + const VBoxGuestHGCMCallInfo *info, + u32 info_size, void **bounce_bufs) +{ + const HGCMFunctionParameter *src_parm = VBOXGUEST_HGCM_CALL_PARMS(info); + HGCMFunctionParameter *dst_parm = VMMDEV_HGCM_CALL_PARMS(call); + u32 i, parms = info->cParms; + u32 off_extra = (uintptr_t)(dst_parm + parms) - (uintptr_t)call; + void *buf; + + call->header.fu32Flags = 0; + call->header.result = VINF_SUCCESS; + call->u32ClientID = info->u32ClientID; + call->u32Function = info->u32Function; + call->cParms = parms; + + for (i = 0; i < parms; i++, src_parm++, dst_parm++) { + switch (src_parm->type) { + case VMMDevHGCMParmType_32bit: + case VMMDevHGCMParmType_64bit: + *dst_parm = *src_parm; + break; + + case VMMDevHGCMParmType_PageList: + hgcm_call_init_pagelist(call, info, dst_parm, src_parm, + &off_extra); + break; + + case VMMDevHGCMParmType_LinAddr_In: + case VMMDevHGCMParmType_LinAddr_Out: + case VMMDevHGCMParmType_LinAddr: + if (bounce_bufs && bounce_bufs[i]) + buf = bounce_bufs[i]; + else + buf = (void *)src_parm->u.Pointer.u.linearAddr; + + hgcm_call_init_linaddr(call, dst_parm, buf, + src_parm->u.Pointer.size, + src_parm->type, &off_extra); + break; + + default: + WARN_ON(1); + dst_parm->type = VMMDevHGCMParmType_Invalid; + } + } +} + +/** + * Tries to cancel a pending HGCM call. + * + * @returns VBox status code + */ +static int hgcm_cancel_call(struct vbg_dev *gdev, VMMDevHGCMCall *call) +{ + int rc; + + /* + * We use a pre-allocated request for cancellations, which is + * protected by cancel_req_mutex. This means that all cancellations + * get serialized, this should be fine since they should be rare. + */ + mutex_lock(&gdev->cancel_req_mutex); + gdev->cancel_req->physReqToCancel = virt_to_phys(call); + rc = vbg_req_perform(gdev, gdev->cancel_req); + mutex_unlock(&gdev->cancel_req_mutex); + + /** @todo ADDVER: Remove this on next minor version change. */ + if (rc == VERR_NOT_IMPLEMENTED) { + call->header.fu32Flags |= VBOX_HGCM_REQ_CANCELLED; + call->header.header.requestType = VMMDevReq_HGCMCancel; + + rc = vbg_req_perform(gdev, call); + if (rc == VERR_INVALID_PARAMETER) + rc = VERR_NOT_FOUND; + } + + if (rc >= 0) + call->header.fu32Flags |= VBOX_HGCM_REQ_CANCELLED; + + return rc; +} + +/** + * Performs the call and completion wait. + * + * @returns VBox status code + * + * @param gdev The VBoxGuest device extension. + * @param call The HGCM call info. + * @param timeout_ms Timeout in ms. + * @param is_user Is this an in kernel call or from userspace ? + * @param leak_it Where to return the leak it / free it, + * indicator. Cancellation fun. + */ +static int vbg_hgcm_do_call(struct vbg_dev *gdev, VMMDevHGCMCall *call, + u32 timeout_ms, bool is_user, bool *leak_it) +{ + long timeout; + int rc, cancel_rc; + + *leak_it = false; + + rc = vbg_req_perform(gdev, call); + + /* + * If the call failed, then pretend success. + * Upper layers will interpret the result code in the packet. + */ + if (rc < 0) { + WARN_ON(!(call->header.fu32Flags & VBOX_HGCM_REQ_DONE)); + return VINF_SUCCESS; + } + + if (rc != VINF_HGCM_ASYNC_EXECUTE) + return rc; + + /* Host decided to process the request asynchronously, wait for it */ + if (timeout_ms == U32_MAX) + timeout = MAX_SCHEDULE_TIMEOUT; + else + timeout = msecs_to_jiffies(timeout_ms); + + if (is_user) { + timeout = wait_event_interruptible_timeout(gdev->hgcm_wq, + hgcm_req_done + (gdev, + &call->header), + timeout); + } else { + timeout = wait_event_timeout(gdev->hgcm_wq, + hgcm_req_done(gdev, + &call->header), + timeout); + } + + /* timeout > 0 means hgcm_req_done has returned true, so success */ + if (timeout > 0) + return VINF_SUCCESS; + + if (timeout == 0) + rc = VERR_TIMEOUT; + else + rc = VERR_INTERRUPTED; + + /* Cancel the request */ + cancel_rc = hgcm_cancel_call(gdev, call); + if (cancel_rc >= 0) + return rc; + + /* + * Failed to cancel, this should mean that the cancel has lost the + * race with normal completion, wait while the host completes it. + */ + if (cancel_rc == VERR_NOT_FOUND || cancel_rc == VERR_SEM_DESTROYED) + timeout = msecs_to_jiffies(500); + else + timeout = msecs_to_jiffies(2000); + + timeout = wait_event_timeout(gdev->hgcm_wq, + hgcm_req_done(gdev, &call->header), + timeout); + + if (WARN_ON(timeout == 0)) { + /* We really should never get here */ + vbg_err("%s: Call timedout and cancellation failed, leaking the request\n", + __func__); + *leak_it = true; + return rc; + } + + /* The call has completed normally after all */ + return VINF_SUCCESS; +} + +/** + * Copies the result of the call back to the caller info structure and user + * buffers. + * + * @returns VBox status code + * @param info Call info structure to update. + * @param call HGCM call request. + * @param bounce_bufs The bouncebuffer array. + */ +static int hgcm_call_copy_back_result(VBoxGuestHGCMCallInfo *info, + const VMMDevHGCMCall *call, + void **bounce_bufs) +{ + const HGCMFunctionParameter *src_parm = VMMDEV_HGCM_CALL_PARMS(call); + HGCMFunctionParameter *dst_parm = VBOXGUEST_HGCM_CALL_PARMS(info); + u32 i, parms = info->cParms; + void __user *userp; + int ret; + + /* The call result. */ + info->result = call->header.result; + + /* Copy back parameters. */ + for (i = 0; i < parms; i++, src_parm++, dst_parm++) { + switch (dst_parm->type) { + case VMMDevHGCMParmType_32bit: + case VMMDevHGCMParmType_64bit: + *dst_parm = *src_parm; + break; + + case VMMDevHGCMParmType_PageList: + dst_parm->u.PageList.size = src_parm->u.PageList.size; + break; + + case VMMDevHGCMParmType_LinAddr_In: + dst_parm->u.Pointer.size = src_parm->u.Pointer.size; + break; + + case VMMDevHGCMParmType_LinAddr_Out: + case VMMDevHGCMParmType_LinAddr: + dst_parm->u.Pointer.size = src_parm->u.Pointer.size; + if (!bounce_bufs) + break; /* In kernel call */ + + userp = (void __user *)dst_parm->u.Pointer.u.linearAddr; + ret = copy_to_user(userp, bounce_bufs[i], + min(src_parm->u.Pointer.size, + dst_parm->u.Pointer.size)); + if (ret) + return VERR_ACCESS_DENIED; + break; + + default: + WARN_ON(1); + return VERR_INTERNAL_ERROR_4; + } + } + + return VINF_SUCCESS; +} + +int vbg_hgcm_call(struct vbg_dev *gdev, VBoxGuestHGCMCallInfo *info, + u32 info_size, u32 timeout_ms, bool is_user) +{ + VMMDevHGCMCall *call; + void **bounce_bufs; + size_t extra_size; + bool leak_it; + int i, rc; + + /* + * Validate, lock and buffer the parameters for the call. + * This will calculate the amount of extra space for physical page list. + */ + rc = hgcm_call_preprocess(info, info_size, is_user, + &bounce_bufs, &extra_size); + if (rc) { + /* Even on error bounce bufs may still have been allocated */ + goto free_bounce_bufs; + } + + call = vbg_req_alloc(sizeof(VMMDevHGCMCall) + info->cParms * + sizeof(HGCMFunctionParameter) + extra_size, + VMMDevReq_HGCMCall); + if (!call) { + rc = VERR_NO_MEMORY; + goto free_bounce_bufs; + } + + hgcm_call_init_call(call, info, info_size, bounce_bufs); + + rc = vbg_hgcm_do_call(gdev, call, timeout_ms, is_user, &leak_it); + if (rc >= 0) + rc = hgcm_call_copy_back_result(info, call, bounce_bufs); + + if (!leak_it) + kfree(call); + +free_bounce_bufs: + if (bounce_bufs) { + for (i = 0; i < info->cParms; i++) + kvfree(bounce_bufs[i]); + kfree(bounce_bufs); + } + + return rc; +} +EXPORT_SYMBOL(vbg_hgcm_call); + +#ifdef CONFIG_X86_64 +int vbg_hgcm_call32(struct vbg_dev *gdev, VBoxGuestHGCMCallInfo *info, + u32 info_size, u32 timeout_ms, bool is_user) +{ + VBoxGuestHGCMCallInfo *info64 = NULL; + HGCMFunctionParameter *parm64 = NULL; + HGCMFunctionParameter32 *parm32 = NULL; + u32 i, info64_size, parms = info->cParms; + int rc = VINF_SUCCESS; + + /* KISS allocate a temporary request and convert the parameters. */ + info64_size = sizeof(*info64) + parms * sizeof(HGCMFunctionParameter); + info64 = kzalloc(info64_size, GFP_KERNEL); + if (!info64) + return VERR_NO_MEMORY; + + *info64 = *info; + parm32 = VBOXGUEST_HGCM_CALL_PARMS32(info); + parm64 = VBOXGUEST_HGCM_CALL_PARMS(info64); + for (i = 0; i < parms; i++, parm32++, parm64++) { + switch (parm32->type) { + case VMMDevHGCMParmType_32bit: + parm64->type = VMMDevHGCMParmType_32bit; + parm64->u.value32 = parm32->u.value32; + break; + + case VMMDevHGCMParmType_64bit: + parm64->type = VMMDevHGCMParmType_64bit; + parm64->u.value64 = parm32->u.value64; + break; + + case VMMDevHGCMParmType_LinAddr_Out: + case VMMDevHGCMParmType_LinAddr: + case VMMDevHGCMParmType_LinAddr_In: + parm64->type = parm32->type; + parm64->u.Pointer.size = parm32->u.Pointer.size; + parm64->u.Pointer.u.linearAddr = + parm32->u.Pointer.u.linearAddr; + break; + + default: + rc = VERR_INVALID_PARAMETER; + } + if (rc < 0) + goto out_free; + } + + rc = vbg_hgcm_call(gdev, info64, info64_size, timeout_ms, is_user); + if (rc < 0) + goto out_free; + + /* Copy back. */ + *info = *info64; + parm32 = VBOXGUEST_HGCM_CALL_PARMS32(info); + parm64 = VBOXGUEST_HGCM_CALL_PARMS(info64); + for (i = 0; i < parms; i++, parm32++, parm64++) { + switch (parm64->type) { + case VMMDevHGCMParmType_32bit: + parm32->u.value32 = parm64->u.value32; + break; + + case VMMDevHGCMParmType_64bit: + parm32->u.value64 = parm64->u.value64; + break; + + case VMMDevHGCMParmType_LinAddr_Out: + case VMMDevHGCMParmType_LinAddr: + case VMMDevHGCMParmType_LinAddr_In: + parm32->u.Pointer.size = parm64->u.Pointer.size; + break; + + default: + WARN_ON(1); + rc = VERR_INTERNAL_ERROR_3; + } + } + +out_free: + kfree(info64); + return rc; +} +#endif + +int vbg_status_code_to_errno(int rc) +{ + if (rc >= 0) + return 0; + + switch (rc) { + case VERR_ACCESS_DENIED: return -EPERM; + case VERR_FILE_NOT_FOUND: return -ENOENT; + case VERR_PROCESS_NOT_FOUND: return -ESRCH; + case VERR_INTERRUPTED: return -EINTR; + case VERR_DEV_IO_ERROR: return -EIO; + case VERR_TOO_MUCH_DATA: return -E2BIG; + case VERR_BAD_EXE_FORMAT: return -ENOEXEC; + case VERR_INVALID_HANDLE: return -EBADF; + case VERR_TRY_AGAIN: return -EAGAIN; + case VERR_NO_MEMORY: return -ENOMEM; + case VERR_INVALID_POINTER: return -EFAULT; + case VERR_RESOURCE_BUSY: return -EBUSY; + case VERR_ALREADY_EXISTS: return -EEXIST; + case VERR_NOT_SAME_DEVICE: return -EXDEV; + case VERR_NOT_A_DIRECTORY: + case VERR_PATH_NOT_FOUND: return -ENOTDIR; + case VERR_IS_A_DIRECTORY: return -EISDIR; + case VERR_INVALID_PARAMETER: return -EINVAL; + case VERR_TOO_MANY_OPEN_FILES: return -ENFILE; + case VERR_INVALID_FUNCTION: return -ENOTTY; + case VERR_SHARING_VIOLATION: return -ETXTBSY; + case VERR_FILE_TOO_BIG: return -EFBIG; + case VERR_DISK_FULL: return -ENOSPC; + case VERR_SEEK_ON_DEVICE: return -ESPIPE; + case VERR_WRITE_PROTECT: return -EROFS; + case VERR_BROKEN_PIPE: return -EPIPE; + case VERR_DEADLOCK: return -EDEADLK; + case VERR_FILENAME_TOO_LONG: return -ENAMETOOLONG; + case VERR_FILE_LOCK_FAILED: return -ENOLCK; + case VERR_NOT_IMPLEMENTED: + case VERR_NOT_SUPPORTED: return -ENOSYS; + case VERR_DIR_NOT_EMPTY: return -ENOTEMPTY; + case VERR_TOO_MANY_SYMLINKS: return -ELOOP; + case VERR_NO_DATA: return -ENODATA; + case VERR_NET_NO_NETWORK: return -ENONET; + case VERR_NET_NOT_UNIQUE_NAME: return -ENOTUNIQ; + case VERR_NO_TRANSLATION: return -EILSEQ; + case VERR_NET_NOT_SOCKET: return -ENOTSOCK; + case VERR_NET_DEST_ADDRESS_REQUIRED: return -EDESTADDRREQ; + case VERR_NET_MSG_SIZE: return -EMSGSIZE; + case VERR_NET_PROTOCOL_TYPE: return -EPROTOTYPE; + case VERR_NET_PROTOCOL_NOT_AVAILABLE: return -ENOPROTOOPT; + case VERR_NET_PROTOCOL_NOT_SUPPORTED: return -EPROTONOSUPPORT; + case VERR_NET_SOCKET_TYPE_NOT_SUPPORTED: return -ESOCKTNOSUPPORT; + case VERR_NET_OPERATION_NOT_SUPPORTED: return -EOPNOTSUPP; + case VERR_NET_PROTOCOL_FAMILY_NOT_SUPPORTED: return -EPFNOSUPPORT; + case VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED: return -EAFNOSUPPORT; + case VERR_NET_ADDRESS_IN_USE: return -EADDRINUSE; + case VERR_NET_ADDRESS_NOT_AVAILABLE: return -EADDRNOTAVAIL; + case VERR_NET_DOWN: return -ENETDOWN; + case VERR_NET_UNREACHABLE: return -ENETUNREACH; + case VERR_NET_CONNECTION_RESET: return -ENETRESET; + case VERR_NET_CONNECTION_ABORTED: return -ECONNABORTED; + case VERR_NET_CONNECTION_RESET_BY_PEER: return -ECONNRESET; + case VERR_NET_NO_BUFFER_SPACE: return -ENOBUFS; + case VERR_NET_ALREADY_CONNECTED: return -EISCONN; + case VERR_NET_NOT_CONNECTED: return -ENOTCONN; + case VERR_NET_SHUTDOWN: return -ESHUTDOWN; + case VERR_NET_TOO_MANY_REFERENCES: return -ETOOMANYREFS; + case VERR_TIMEOUT: return -ETIMEDOUT; + case VERR_NET_CONNECTION_REFUSED: return -ECONNREFUSED; + case VERR_NET_HOST_DOWN: return -EHOSTDOWN; + case VERR_NET_HOST_UNREACHABLE: return -EHOSTUNREACH; + case VERR_NET_ALREADY_IN_PROGRESS: return -EALREADY; + case VERR_NET_IN_PROGRESS: return -EINPROGRESS; + case VERR_MEDIA_NOT_PRESENT: return -ENOMEDIUM; + case VERR_MEDIA_NOT_RECOGNIZED: return -EMEDIUMTYPE; + default: + vbg_warn("%s: Unhandled err %d\n", __func__, rc); + return -EPROTO; + } +} +EXPORT_SYMBOL(vbg_status_code_to_errno); diff --git a/drivers/virt/vboxguest/vboxguest_version.h b/drivers/virt/vboxguest/vboxguest_version.h new file mode 100644 index 000000000000..47a53151fcd0 --- /dev/null +++ b/drivers/virt/vboxguest/vboxguest_version.h @@ -0,0 +1,18 @@ +/* + * VBox Guest additions version info, this is used by the host to determine + * supported guest-addition features in some cases. So this will need to be + * synced with vbox upstreams versioning scheme when we implement / port + * new features from the upstream out-of-tree vboxguest driver. + */ + +#ifndef __VBOX_VERSION_H__ +#define __VBOX_VERSION_H__ + +/* Last synced July 12th 2017 */ +#define VBOX_VERSION_MAJOR 5 +#define VBOX_VERSION_MINOR 1 +#define VBOX_VERSION_BUILD 51 +#define VBOX_SVN_REV 67325 +#define VBOX_VERSION_STRING "5.1.51" + +#endif diff --git a/include/linux/vbox_utils.h b/include/linux/vbox_utils.h new file mode 100644 index 000000000000..81aadd77c8f8 --- /dev/null +++ b/include/linux/vbox_utils.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2006-2016 Oracle Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, in which case the provisions of the CDDL are applicable + * instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + +#ifndef __VBOX_UTILS_H__ +#define __VBOX_UTILS_H__ + +#include <linux/printk.h> +#include <linux/vbox_vmmdev.h> +#include <linux/vboxguest.h> + +struct vbg_dev; + +/** + * vboxguest logging functions, these log both to the backdoor and call + * the equivalent kernel pr_foo function. + */ +__printf(1, 2) void vbg_info(const char *fmt, ...); +__printf(1, 2) void vbg_warn(const char *fmt, ...); +__printf(1, 2) void vbg_err(const char *fmt, ...); + +/* Only use backdoor logging for non-dynamic debug builds */ +#if defined(DEBUG) && !defined(CONFIG_DYNAMIC_DEBUG) +__printf(1, 2) void vbg_debug(const char *fmt, ...); +#else +#define vbg_debug pr_debug +#endif + +/** @name Generic request functions. + * @{ + */ + +/** + * Allocate memory for generic request and initialize the request header. + * + * @returns the allocated memory + * @param len Size of memory block required for the request. + * @param req_type The generic request type. + */ +void *vbg_req_alloc(size_t len, VMMDevRequestType req_type); + +/** + * Perform a generic request. + * + * @returns VBox status code + * @param gdev The Guest extension device. + * @param req Pointer the request structure. + */ +int vbg_req_perform(struct vbg_dev *gdev, void *req); + +/** + * Verify the generic request header. + * + * @returns VBox status code + * @param req Pointer the request header structure. + * @param buffer_size Size of the request memory block. It should be equal to + * the request size for fixed size requests. It can be + * greater for variable size requests. + */ +int vbg_req_verify(const VMMDevRequestHeader *req, size_t buffer_size); +/** @} */ + +int vbg_hgcm_connect(struct vbg_dev *gdev, HGCMServiceLocation *loc, + u32 *client_id); + +int vbg_hgcm_disconnect(struct vbg_dev *gdev, u32 client_id); + +int vbg_hgcm_call(struct vbg_dev *gdev, + VBoxGuestHGCMCallInfo *pCallInfo, u32 cbCallInfo, + u32 timeout_ms, bool is_user); + +int vbg_hgcm_call32(struct vbg_dev *gdev, + VBoxGuestHGCMCallInfo *pCallInfo, u32 cbCallInfo, + u32 timeout_ms, bool is_user); + +/** + * Convert a VirtualBox status code to a standard Linux kernel return value. + * @returns 0 or negative errno value. + * @param rc VirtualBox status code to convert. + */ +int vbg_status_code_to_errno(int rc); + +/** + * Helper for the vboxsf driver to get a reference to the guest device. + * @returns a pointer to the gdev; or a ERR_PTR value on error. + */ +struct vbg_dev *vbg_get_gdev(void); + +/** + * Helper for the vboxsf driver to put a guest device reference. + * @param gdev Reference returned by vbg_get_gdev to put. + */ +void vbg_put_gdev(struct vbg_dev *gdev); + +#endif diff --git a/include/linux/vbox_vmmdev.h b/include/linux/vbox_vmmdev.h new file mode 100644 index 000000000000..10d6cd335446 --- /dev/null +++ b/include/linux/vbox_vmmdev.h @@ -0,0 +1,123 @@ +/* + * Virtual Device for Guest <-> VMM/Host communication (ADD,DEV). + * + * Copyright (C) 2006-2016 Oracle Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, in which case the provisions of the CDDL are applicable + * instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + +#ifndef __VBOX_VMMDEV_H__ +#define __VBOX_VMMDEV_H__ + +#include <linux/sizes.h> +#include <uapi/linux/vbox_vmmdev.h> + +/** + * @name VBVA ring defines. + * + * The VBVA ring buffer is suitable for transferring large (< 2GB) amount of + * data. For example big bitmaps which do not fit to the buffer. + * + * Guest starts writing to the buffer by initializing a record entry in the + * aRecords queue. VBVA_F_RECORD_PARTIAL indicates that the record is being + * written. As data is written to the ring buffer, the guest increases off32End + * for the record. + * + * The host reads the aRecords on flushes and processes all completed records. + * When host encounters situation when only a partial record presents and + * cbRecord & ~VBVA_F_RECORD_PARTIAL >= VBVA_RING_BUFFER_SIZE - + * VBVA_RING_BUFFER_THRESHOLD, the host fetched all record data and updates + * off32Head. After that on each flush the host continues fetching the data + * until the record is completed. + * + * @{ + */ +#define VMMDEV_VBVA_RING_BUFFER_SIZE (SZ_4M - SZ_1K) +#define VMMDEV_VBVA_RING_BUFFER_THRESHOLD (SZ_4K) + +#define VMMDEV_VBVA_MAX_RECORDS (64) +/** @} */ + +/** VBVA record. */ +typedef struct VMMDEVVBVARECORD { + /** The length of the record. Changed by guest. */ + u32 cbRecord; +} VMMDEVVBVARECORD; +VMMDEV_ASSERT_SIZE(VMMDEVVBVARECORD, 4); + +/** + * VBVA memory layout. + * + * This is a subsection of the VMMDevMemory structure. + */ +typedef struct VBVAMEMORY { + /** VBVA_F_MODE_*. */ + u32 fu32ModeFlags; + + /** The offset where the data start in the buffer. */ + u32 off32Data; + /** The offset where next data must be placed in the buffer. */ + u32 off32Free; + + /** The ring buffer for data. */ + u8 au8RingBuffer[VMMDEV_VBVA_RING_BUFFER_SIZE]; + + /** The queue of record descriptions. */ + VMMDEVVBVARECORD aRecords[VMMDEV_VBVA_MAX_RECORDS]; + u32 indexRecordFirst; + u32 indexRecordFree; + + /** + * RDP orders supported by the client. The guest reports only them + * and falls back to DIRTY rects for not supported ones. + * + * (1 << VBVA_VRDP_*) + */ + u32 fu32SupportedOrders; + +} VBVAMEMORY; +VMMDEV_ASSERT_SIZE(VBVAMEMORY, 12 + (SZ_4M-SZ_1K) + 4*64 + 12); + +/** + * The layout of VMMDEV RAM region that contains information for guest. + */ +typedef struct VMMDevMemory { + /** The size of this structure. */ + u32 u32Size; + /** The structure version. (VMMDEV_MEMORY_VERSION) */ + u32 u32Version; + + union { + struct { + /** Flag telling that VMMDev has events pending. */ + bool fHaveEvents; + } V1_04; + + struct { + /** Pending events flags, set by host. */ + u32 u32HostEvents; + /** Mask of events the guest wants, set by guest. */ + u32 u32GuestEventMask; + } V1_03; + } V; + + VBVAMEMORY vbvaMemory; + +} VMMDevMemory; +VMMDEV_ASSERT_SIZE(VMMDevMemory, 8 + 8 + sizeof(VBVAMEMORY)); +VMMDEV_ASSERT_MEMBER_OFFSET(VMMDevMemory, vbvaMemory, 16); + +/** Version of VMMDevMemory structure (VMMDevMemory::u32Version). */ +#define VMMDEV_MEMORY_VERSION (1) + +#endif diff --git a/include/uapi/linux/vbox_err.h b/include/uapi/linux/vbox_err.h new file mode 100644 index 000000000000..e6e7ba835e36 --- /dev/null +++ b/include/uapi/linux/vbox_err.h @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2017 Oracle Corporation + * + * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. + */ + +#ifndef __UAPI_VBOX_ERR_H__ +#define __UAPI_VBOX_ERR_H__ + +/** + * @name VirtualBox virtual-hardware error macros + * @{ + */ + +#define VINF_SUCCESS 0 +#define VERR_GENERAL_FAILURE (-1) +#define VERR_INVALID_PARAMETER (-2) +#define VERR_INVALID_MAGIC (-3) +#define VERR_INVALID_HANDLE (-4) +#define VERR_LOCK_FAILED (-5) +#define VERR_INVALID_POINTER (-6) +#define VERR_IDT_FAILED (-7) +#define VERR_NO_MEMORY (-8) +#define VERR_ALREADY_LOADED (-9) +#define VERR_PERMISSION_DENIED (-10) +#define VERR_VERSION_MISMATCH (-11) +#define VERR_NOT_IMPLEMENTED (-12) +#define VERR_INVALID_FLAGS (-13) + +#define VERR_NOT_EQUAL (-18) +#define VERR_NOT_SYMLINK (-19) +#define VERR_NO_TMP_MEMORY (-20) +#define VERR_INVALID_FMODE (-21) +#define VERR_WRONG_ORDER (-22) +#define VERR_NO_TLS_FOR_SELF (-23) +#define VERR_FAILED_TO_SET_SELF_TLS (-24) +#define VERR_NO_CONT_MEMORY (-26) +#define VERR_NO_PAGE_MEMORY (-27) +#define VERR_THREAD_IS_DEAD (-29) +#define VERR_THREAD_NOT_WAITABLE (-30) +#define VERR_PAGE_TABLE_NOT_PRESENT (-31) +#define VERR_INVALID_CONTEXT (-32) +#define VERR_TIMER_BUSY (-33) +#define VERR_ADDRESS_CONFLICT (-34) +#define VERR_UNRESOLVED_ERROR (-35) +#define VERR_INVALID_FUNCTION (-36) +#define VERR_NOT_SUPPORTED (-37) +#define VERR_ACCESS_DENIED (-38) +#define VERR_INTERRUPTED (-39) +#define VERR_TIMEOUT (-40) +#define VERR_BUFFER_OVERFLOW (-41) +#define VERR_TOO_MUCH_DATA (-42) +#define VERR_MAX_THRDS_REACHED (-43) +#define VERR_MAX_PROCS_REACHED (-44) +#define VERR_SIGNAL_REFUSED (-45) +#define VERR_SIGNAL_PENDING (-46) +#define VERR_SIGNAL_INVALID (-47) +#define VERR_STATE_CHANGED (-48) +#define VERR_INVALID_UUID_FORMAT (-49) +#define VERR_PROCESS_NOT_FOUND (-50) +#define VERR_PROCESS_RUNNING (-51) +#define VERR_TRY_AGAIN (-52) +#define VERR_PARSE_ERROR (-53) +#define VERR_OUT_OF_RANGE (-54) +#define VERR_NUMBER_TOO_BIG (-55) +#define VERR_NO_DIGITS (-56) +#define VERR_NEGATIVE_UNSIGNED (-57) +#define VERR_NO_TRANSLATION (-58) + +#define VERR_NOT_FOUND (-78) +#define VERR_INVALID_STATE (-79) +#define VERR_OUT_OF_RESOURCES (-80) + +#define VERR_FILE_NOT_FOUND (-102) +#define VERR_PATH_NOT_FOUND (-103) +#define VERR_INVALID_NAME (-104) +#define VERR_ALREADY_EXISTS (-105) +#define VERR_TOO_MANY_OPEN_FILES (-106) +#define VERR_SEEK (-107) +#define VERR_NEGATIVE_SEEK (-108) +#define VERR_SEEK_ON_DEVICE (-109) +#define VERR_EOF (-110) +#define VERR_READ_ERROR (-111) +#define VERR_WRITE_ERROR (-112) +#define VERR_WRITE_PROTECT (-113) +#define VERR_SHARING_VIOLATION (-114) +#define VERR_FILE_LOCK_FAILED (-115) +#define VERR_FILE_LOCK_VIOLATION (-116) +#define VERR_CANT_CREATE (-117) +#define VERR_CANT_DELETE_DIRECTORY (-118) +#define VERR_NOT_SAME_DEVICE (-119) +#define VERR_FILENAME_TOO_LONG (-120) +#define VERR_MEDIA_NOT_PRESENT (-121) +#define VERR_MEDIA_NOT_RECOGNIZED (-122) +#define VERR_FILE_NOT_LOCKED (-123) +#define VERR_FILE_LOCK_LOST (-124) +#define VERR_DIR_NOT_EMPTY (-125) +#define VERR_NOT_A_DIRECTORY (-126) +#define VERR_IS_A_DIRECTORY (-127) +#define VERR_FILE_TOO_BIG (-128) + +#define VERR_NET_IO_ERROR (-400) +#define VERR_NET_OUT_OF_RESOURCES (-401) +#define VERR_NET_HOST_NOT_FOUND (-402) +#define VERR_NET_PATH_NOT_FOUND (-403) +#define VERR_NET_PRINT_ERROR (-404) +#define VERR_NET_NO_NETWORK (-405) +#define VERR_NET_NOT_UNIQUE_NAME (-406) + +#define VERR_NET_IN_PROGRESS (-436) +#define VERR_NET_ALREADY_IN_PROGRESS (-437) +#define VERR_NET_NOT_SOCKET (-438) +#define VERR_NET_DEST_ADDRESS_REQUIRED (-439) +#define VERR_NET_MSG_SIZE (-440) +#define VERR_NET_PROTOCOL_TYPE (-441) +#define VERR_NET_PROTOCOL_NOT_AVAILABLE (-442) +#define VERR_NET_PROTOCOL_NOT_SUPPORTED (-443) +#define VERR_NET_SOCKET_TYPE_NOT_SUPPORTED (-444) +#define VERR_NET_OPERATION_NOT_SUPPORTED (-445) +#define VERR_NET_PROTOCOL_FAMILY_NOT_SUPPORTED (-446) +#define VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED (-447) +#define VERR_NET_ADDRESS_IN_USE (-448) +#define VERR_NET_ADDRESS_NOT_AVAILABLE (-449) +#define VERR_NET_DOWN (-450) +#define VERR_NET_UNREACHABLE (-451) +#define VERR_NET_CONNECTION_RESET (-452) +#define VERR_NET_CONNECTION_ABORTED (-453) +#define VERR_NET_CONNECTION_RESET_BY_PEER (-454) +#define VERR_NET_NO_BUFFER_SPACE (-455) +#define VERR_NET_ALREADY_CONNECTED (-456) +#define VERR_NET_NOT_CONNECTED (-457) +#define VERR_NET_SHUTDOWN (-458) +#define VERR_NET_TOO_MANY_REFERENCES (-459) +#define VERR_NET_CONNECTION_TIMED_OUT (-460) +#define VERR_NET_CONNECTION_REFUSED (-461) +#define VERR_NET_HOST_DOWN (-464) +#define VERR_NET_HOST_UNREACHABLE (-465) +#define VERR_NET_PROTOCOL_ERROR (-466) +#define VERR_NET_INCOMPLETE_TX_PACKET (-467) + +/* misc. unsorted codes */ +#define VERR_RESOURCE_BUSY (-138) +#define VERR_DISK_FULL (-152) +#define VERR_TOO_MANY_SYMLINKS (-156) +#define VERR_NO_MORE_FILES (-201) +#define VERR_INTERNAL_ERROR (-225) +#define VERR_INTERNAL_ERROR_2 (-226) +#define VERR_INTERNAL_ERROR_3 (-227) +#define VERR_INTERNAL_ERROR_4 (-228) +#define VERR_DEV_IO_ERROR (-250) +#define VERR_IO_BAD_LENGTH (-255) +#define VERR_BROKEN_PIPE (-301) +#define VERR_NO_DATA (-304) +#define VERR_SEM_DESTROYED (-363) +#define VERR_DEADLOCK (-365) +#define VERR_BAD_EXE_FORMAT (-608) +#define VINF_HGCM_ASYNC_EXECUTE (2903) + +#define RT_SUCCESS(rc) ((rc) >= 0) +#define RT_FAILURE(rc) ((rc) < 0) + +#endif diff --git a/include/uapi/linux/vbox_ostypes.h b/include/uapi/linux/vbox_ostypes.h new file mode 100644 index 000000000000..44a8561e8892 --- /dev/null +++ b/include/uapi/linux/vbox_ostypes.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2006-2016 Oracle Corporation + * + * VirtualBox - Global Guest Operating System definition. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, in which case the provisions of the CDDL are applicable + * instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + +#ifndef __UAPI_VBOX_OSTYPES_H__ +#define __UAPI_VBOX_OSTYPES_H__ + +/** The bit number which indicates 64-bit or 32-bit. */ +#define VBOXOSTYPE_x64_BIT 8 + +/** + * Global list of guest operating system types. + * + * They are grouped into families. A family identifer is always has + * mod 0x10000 == 0. New entries can be added, however other components + * depend on the values (e.g. the Qt GUI and guest additions) so the + * existing values MUST stay the same. + * + * Note: distinguish between 32 & 64 bits guest OSes by checking bit 8. + */ +typedef enum VBOXOSTYPE { + VBOXOSTYPE_Unknown = 0, + VBOXOSTYPE_Unknown_x64 = 0x00100, + VBOXOSTYPE_DOS = 0x10000, + VBOXOSTYPE_Win31 = 0x15000, + VBOXOSTYPE_Win9x = 0x20000, + VBOXOSTYPE_Win95 = 0x21000, + VBOXOSTYPE_Win98 = 0x22000, + VBOXOSTYPE_WinMe = 0x23000, + VBOXOSTYPE_WinNT = 0x30000, + VBOXOSTYPE_WinNT_x64 = 0x30100, + VBOXOSTYPE_WinNT4 = 0x31000, + VBOXOSTYPE_Win2k = 0x32000, + VBOXOSTYPE_WinXP = 0x33000, + VBOXOSTYPE_WinXP_x64 = 0x33100, + VBOXOSTYPE_Win2k3 = 0x34000, + VBOXOSTYPE_Win2k3_x64 = 0x34100, + VBOXOSTYPE_WinVista = 0x35000, + VBOXOSTYPE_WinVista_x64 = 0x35100, + VBOXOSTYPE_Win2k8 = 0x36000, + VBOXOSTYPE_Win2k8_x64 = 0x36100, + VBOXOSTYPE_Win7 = 0x37000, + VBOXOSTYPE_Win7_x64 = 0x37100, + VBOXOSTYPE_Win8 = 0x38000, + VBOXOSTYPE_Win8_x64 = 0x38100, + VBOXOSTYPE_Win2k12_x64 = 0x39100, + VBOXOSTYPE_Win81 = 0x3A000, + VBOXOSTYPE_Win81_x64 = 0x3A100, + VBOXOSTYPE_Win10 = 0x3B000, + VBOXOSTYPE_Win10_x64 = 0x3B100, + VBOXOSTYPE_Win2k16_x64 = 0x3C100, + VBOXOSTYPE_OS2 = 0x40000, + VBOXOSTYPE_OS2Warp3 = 0x41000, + VBOXOSTYPE_OS2Warp4 = 0x42000, + VBOXOSTYPE_OS2Warp45 = 0x43000, + VBOXOSTYPE_ECS = 0x44000, + VBOXOSTYPE_OS21x = 0x48000, + VBOXOSTYPE_Linux = 0x50000, + VBOXOSTYPE_Linux_x64 = 0x50100, + VBOXOSTYPE_Linux22 = 0x51000, + VBOXOSTYPE_Linux24 = 0x52000, + VBOXOSTYPE_Linux24_x64 = 0x52100, + VBOXOSTYPE_Linux26 = 0x53000, + VBOXOSTYPE_Linux26_x64 = 0x53100, + VBOXOSTYPE_ArchLinux = 0x54000, + VBOXOSTYPE_ArchLinux_x64 = 0x54100, + VBOXOSTYPE_Debian = 0x55000, + VBOXOSTYPE_Debian_x64 = 0x55100, + VBOXOSTYPE_OpenSUSE = 0x56000, + VBOXOSTYPE_OpenSUSE_x64 = 0x56100, + VBOXOSTYPE_FedoraCore = 0x57000, + VBOXOSTYPE_FedoraCore_x64 = 0x57100, + VBOXOSTYPE_Gentoo = 0x58000, + VBOXOSTYPE_Gentoo_x64 = 0x58100, + VBOXOSTYPE_Mandriva = 0x59000, + VBOXOSTYPE_Mandriva_x64 = 0x59100, + VBOXOSTYPE_RedHat = 0x5A000, + VBOXOSTYPE_RedHat_x64 = 0x5A100, + VBOXOSTYPE_Turbolinux = 0x5B000, + VBOXOSTYPE_Turbolinux_x64 = 0x5B100, + VBOXOSTYPE_Ubuntu = 0x5C000, + VBOXOSTYPE_Ubuntu_x64 = 0x5C100, + VBOXOSTYPE_Xandros = 0x5D000, + VBOXOSTYPE_Xandros_x64 = 0x5D100, + VBOXOSTYPE_Oracle = 0x5E000, + VBOXOSTYPE_Oracle_x64 = 0x5E100, + VBOXOSTYPE_FreeBSD = 0x60000, + VBOXOSTYPE_FreeBSD_x64 = 0x60100, + VBOXOSTYPE_OpenBSD = 0x61000, + VBOXOSTYPE_OpenBSD_x64 = 0x61100, + VBOXOSTYPE_NetBSD = 0x62000, + VBOXOSTYPE_NetBSD_x64 = 0x62100, + VBOXOSTYPE_Netware = 0x70000, + VBOXOSTYPE_Solaris = 0x80000, + VBOXOSTYPE_Solaris_x64 = 0x80100, + VBOXOSTYPE_OpenSolaris = 0x81000, + VBOXOSTYPE_OpenSolaris_x64 = 0x81100, + VBOXOSTYPE_Solaris11_x64 = 0x82100, + VBOXOSTYPE_L4 = 0x90000, + VBOXOSTYPE_QNX = 0xA0000, + VBOXOSTYPE_MacOS = 0xB0000, + VBOXOSTYPE_MacOS_x64 = 0xB0100, + VBOXOSTYPE_MacOS106 = 0xB2000, + VBOXOSTYPE_MacOS106_x64 = 0xB2100, + VBOXOSTYPE_MacOS107_x64 = 0xB3100, + VBOXOSTYPE_MacOS108_x64 = 0xB4100, + VBOXOSTYPE_MacOS109_x64 = 0xB5100, + VBOXOSTYPE_MacOS1010_x64 = 0xB6100, + VBOXOSTYPE_MacOS1011_x64 = 0xB7100, + VBOXOSTYPE_JRockitVE = 0xC0000, + VBOXOSTYPE_Haiku = 0xD0000, + VBOXOSTYPE_Haiku_x64 = 0xD0100, + VBOXOSTYPE_VBoxBS_x64 = 0xE0100, + /** The mask which indicates 64-bit. */ + VBOXOSTYPE_x64 = 1 << VBOXOSTYPE_x64_BIT, + /** The usual 32-bit hack. */ + VBOXOSTYPE_32BIT_HACK = 0x7fffffff +} VBOXOSTYPE; + +/** + * Global list of guest OS families. + */ +typedef enum VBOXOSFAMILY { + VBOXOSFAMILY_Unknown = 0, + VBOXOSFAMILY_Windows32 = 1, + VBOXOSFAMILY_Windows64 = 2, + VBOXOSFAMILY_Linux32 = 3, + VBOXOSFAMILY_Linux64 = 4, + VBOXOSFAMILY_FreeBSD32 = 5, + VBOXOSFAMILY_FreeBSD64 = 6, + VBOXOSFAMILY_Solaris32 = 7, + VBOXOSFAMILY_Solaris64 = 8, + VBOXOSFAMILY_MacOSX32 = 9, + VBOXOSFAMILY_MacOSX64 = 10, + /** The usual 32-bit hack. */ + VBOXOSFAMILY_32BIT_HACK = 0x7fffffff +} VBOXOSFAMILY; + +#endif diff --git a/include/uapi/linux/vbox_vmmdev.h b/include/uapi/linux/vbox_vmmdev.h new file mode 100644 index 000000000000..ce82b8f499de --- /dev/null +++ b/include/uapi/linux/vbox_vmmdev.h @@ -0,0 +1,1745 @@ +/* + * Virtual Device for Guest <-> VMM/Host communication (ADD,DEV). + * + * Copyright (C) 2006-2016 Oracle Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, in which case the provisions of the CDDL are applicable + * instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + +#ifndef __UAPI_VBOX_VMMDEV_H__ +#define __UAPI_VBOX_VMMDEV_H__ + +#include <asm/bitsperlong.h> +#include <linux/types.h> +#include <linux/vbox_ostypes.h> + +/* + * We cannot use linux' compiletime_assert here because it expects to be used + * inside a function only. Use a typedef to a char array with a negative size. + */ +#define VMMDEV_ASSERT_SIZE(type, size) \ + typedef char type ## _assert_size[1 - 2*!!(sizeof(type) != (size))] +#define VMMDEV_ASSERT_MEMBER_OFFSET(type, member, offset) \ + typedef char type ## _ ## member ## _assert_member_offset \ + [1 - 2*!!(offsetof(type, member) != (offset))] + +/* + * The host expects dwords / 32 bit packing. Using __aligned(4) everywhere is + * not really practical and also does not seem to work. Specifically I've been + * unable to get some structs (HGCMFunctionParameter32|64) to compile to the + * right size using __aligned(), so we're sticking with pragma pack(4) here. + */ +#pragma pack(4) + +/** + * @defgroup grp_vmmdev VMM Device + * + * @note This interface cannot be changed, it can only be extended! + * + * @{ + */ + +/** Port for generic request interface (relative offset). */ +#define VMMDEV_PORT_OFF_REQUEST 0 + +/** + * @name VMMDev events. + * + * Used mainly by VMMDevReq_AcknowledgeEvents/VMMDevEvents and version 1.3 of + * VMMDevMemory. + * + * @{ + */ +/** Host mouse capabilities has been changed. */ +#define VMMDEV_EVENT_MOUSE_CAPABILITIES_CHANGED BIT(0) +/** HGCM event. */ +#define VMMDEV_EVENT_HGCM BIT(1) +/** A display change request has been issued. */ +#define VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST BIT(2) +/** Credentials are available for judgement. */ +#define VMMDEV_EVENT_JUDGE_CREDENTIALS BIT(3) +/** The guest has been restored. */ +#define VMMDEV_EVENT_RESTORED BIT(4) +/** Seamless mode state changed. */ +#define VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST BIT(5) +/** Memory balloon size changed. */ +#define VMMDEV_EVENT_BALLOON_CHANGE_REQUEST BIT(6) +/** Statistics interval changed. */ +#define VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST BIT(7) +/** VRDP status changed. */ +#define VMMDEV_EVENT_VRDP BIT(8) +/** New mouse position data available. */ +#define VMMDEV_EVENT_MOUSE_POSITION_CHANGED BIT(9) +/** CPU hotplug event occurred. */ +#define VMMDEV_EVENT_CPU_HOTPLUG BIT(10) +/** The mask of valid events, for sanity checking. */ +#define VMMDEV_EVENT_VALID_EVENT_MASK 0x000007ffU +/** @} */ + +/** @defgroup grp_vmmdev_req VMMDev Generic Request Interface + * @{ + */ + +/** @name Current version of the VMMDev interface. + * + * Additions are allowed to work only if + * additions_major == vmmdev_current && additions_minor <= vmmdev_current. + * Additions version is reported to host (VMMDev) by VMMDevReq_ReportGuestInfo. + * + * @remarks These defines also live in the 16-bit and assembly versions of this + * header. + */ +#define VMMDEV_VERSION 0x00010004 +#define VMMDEV_VERSION_MAJOR (VMMDEV_VERSION >> 16) +#define VMMDEV_VERSION_MINOR (VMMDEV_VERSION & 0xffff) +/** @} */ + +/** Maximum request packet size. */ +#define VMMDEV_MAX_VMMDEVREQ_SIZE 1048576 +/** Maximum number of HGCM parameters. */ +#define VMMDEV_MAX_HGCM_PARMS 1024 +/** Maximum total size of hgcm buffers in one call. */ +#define VMMDEV_MAX_HGCM_DATA_SIZE 0x7fffffffU + +/** + * VMMDev request types. + * @note when updating this, adjust vmmdevGetRequestSize() as well + */ +typedef enum { + VMMDevReq_InvalidRequest = 0, + VMMDevReq_GetMouseStatus = 1, + VMMDevReq_SetMouseStatus = 2, + VMMDevReq_SetPointerShape = 3, + VMMDevReq_GetHostVersion = 4, + VMMDevReq_Idle = 5, + VMMDevReq_GetHostTime = 10, + VMMDevReq_GetHypervisorInfo = 20, + VMMDevReq_SetHypervisorInfo = 21, + VMMDevReq_RegisterPatchMemory = 22, /* since version 3.0.6 */ + VMMDevReq_DeregisterPatchMemory = 23, /* since version 3.0.6 */ + VMMDevReq_SetPowerStatus = 30, + VMMDevReq_AcknowledgeEvents = 41, + VMMDevReq_CtlGuestFilterMask = 42, + VMMDevReq_ReportGuestInfo = 50, + VMMDevReq_ReportGuestInfo2 = 58, /* since version 3.2.0 */ + VMMDevReq_ReportGuestStatus = 59, /* since version 3.2.8 */ + VMMDevReq_ReportGuestUserState = 74, /* since version 4.3 */ + /** + * Retrieve a display resize request sent by the host using + * @a IDisplay:setVideoModeHint. Deprecated. + * + * Similar to @a VMMDevReq_GetDisplayChangeRequest2, except that it only + * considers host requests sent for the first virtual display. This + * guest-req should not be used in new guest code, and the results are + * undefined if a guest mixes calls to this and + * @a VMMDevReq_GetDisplayChangeRequest2. + */ + VMMDevReq_GetDisplayChangeRequest = 51, + VMMDevReq_VideoModeSupported = 52, + VMMDevReq_GetHeightReduction = 53, + /** + * Retrieve a display resize request sent by the host using + * @a IDisplay:setVideoModeHint. + * + * Queries a display resize request sent from the host. If the + * @a eventAck member is sent to true and there is an unqueried request + * available for one of the virtual display then that request will + * be returned. If several displays have unqueried requests the lowest + * numbered display will be chosen first. Only the most recent unseen + * request for each display is remembered. + * If @a eventAck is set to false, the last host request queried with + * @a eventAck set is resent, or failing that the most recent received + * from the host. If no host request was ever received then all zeros + * are returned. + */ + VMMDevReq_GetDisplayChangeRequest2 = 54, + VMMDevReq_ReportGuestCapabilities = 55, + VMMDevReq_SetGuestCapabilities = 56, + VMMDevReq_VideoModeSupported2 = 57, /* since version 3.2.0 */ + VMMDevReq_GetDisplayChangeRequestEx = 80, /* since version 4.2.4 */ + VMMDevReq_HGCMConnect = 60, + VMMDevReq_HGCMDisconnect = 61, + VMMDevReq_HGCMCall32 = 62, + VMMDevReq_HGCMCall64 = 63, + VMMDevReq_HGCMCancel = 64, + VMMDevReq_HGCMCancel2 = 65, + VMMDevReq_VideoAccelEnable = 70, + VMMDevReq_VideoAccelFlush = 71, + VMMDevReq_VideoSetVisibleRegion = 72, + VMMDevReq_GetSeamlessChangeRequest = 73, + VMMDevReq_QueryCredentials = 100, + VMMDevReq_ReportCredentialsJudgement = 101, + VMMDevReq_ReportGuestStats = 110, + VMMDevReq_GetMemBalloonChangeRequest = 111, + VMMDevReq_GetStatisticsChangeRequest = 112, + VMMDevReq_ChangeMemBalloon = 113, + VMMDevReq_GetVRDPChangeRequest = 150, + VMMDevReq_LogString = 200, + VMMDevReq_GetCpuHotPlugRequest = 210, + VMMDevReq_SetCpuHotPlugStatus = 211, + VMMDevReq_RegisterSharedModule = 212, + VMMDevReq_UnregisterSharedModule = 213, + VMMDevReq_CheckSharedModules = 214, + VMMDevReq_GetPageSharingStatus = 215, + VMMDevReq_DebugIsPageShared = 216, + VMMDevReq_GetSessionId = 217, /* since version 3.2.8 */ + VMMDevReq_WriteCoreDump = 218, + VMMDevReq_GuestHeartbeat = 219, + VMMDevReq_HeartbeatConfigure = 220, + VMMDevReq_SizeHack = 0x7fffffff +} VMMDevRequestType; + +#if __BITS_PER_LONG == 64 +#define VMMDevReq_HGCMCall VMMDevReq_HGCMCall64 +#else +#define VMMDevReq_HGCMCall VMMDevReq_HGCMCall32 +#endif + +/** Version of VMMDevRequestHeader structure. */ +#define VMMDEV_REQUEST_HEADER_VERSION (0x10001) + +/** + * Generic VMMDev request header. + */ +typedef struct { + /** IN: Size of the structure in bytes (including body). */ + __u32 size; + /** IN: Version of the structure. */ + __u32 version; + /** IN: Type of the request. */ + VMMDevRequestType requestType; + /** OUT: Return code. */ + __s32 rc; + /** Reserved field no.1. MBZ. */ + __u32 reserved1; + /** Reserved field no.2. MBZ. */ + __u32 reserved2; +} VMMDevRequestHeader; +VMMDEV_ASSERT_SIZE(VMMDevRequestHeader, 24); + +/** + * Mouse status request structure. + * + * Used by VMMDevReq_GetMouseStatus and VMMDevReq_SetMouseStatus. + */ +typedef struct { + /** header */ + VMMDevRequestHeader header; + /** Mouse feature mask. See VMMDEV_MOUSE_*. */ + __u32 mouseFeatures; + /** Mouse x position. */ + __s32 pointerXPos; + /** Mouse y position. */ + __s32 pointerYPos; +} VMMDevReqMouseStatus; +VMMDEV_ASSERT_SIZE(VMMDevReqMouseStatus, 24+12); + +/** + * @name Mouse capability bits (VMMDevReqMouseStatus::mouseFeatures). + * @{ + */ +/** The guest can (== wants to) handle absolute coordinates. */ +#define VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE BIT(0) +/** + * The host can (== wants to) send absolute coordinates. + * (Input not captured.) + */ +#define VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE BIT(1) +/** + * The guest can *NOT* switch to software cursor and therefore depends on the + * host cursor. + * + * When guest additions are installed and the host has promised to display the + * cursor itself, the guest installs a hardware mouse driver. Don't ask the + * guest to switch to a software cursor then. + */ +#define VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR BIT(2) +/** The host does NOT provide support for drawing the cursor itself. */ +#define VMMDEV_MOUSE_HOST_CANNOT_HWPOINTER BIT(3) +/** The guest can read VMMDev events to find out about pointer movement */ +#define VMMDEV_MOUSE_NEW_PROTOCOL BIT(4) +/** + * If the guest changes the status of the VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR + * bit, the host will honour this. + */ +#define VMMDEV_MOUSE_HOST_RECHECKS_NEEDS_HOST_CURSOR BIT(5) +/** + * The host supplies an absolute pointing device. The Guest Additions may + * wish to use this to decide whether to install their own driver. + */ +#define VMMDEV_MOUSE_HOST_HAS_ABS_DEV BIT(6) +/** The mask of all VMMDEV_MOUSE_* flags */ +#define VMMDEV_MOUSE_MASK 0x0000007fU +/** + * The mask of guest capability changes for which notification events should + * be sent. + */ +#define VMMDEV_MOUSE_NOTIFY_HOST_MASK \ + (VMMDEV_MOUSE_GUEST_CAN_ABSOLUTE | VMMDEV_MOUSE_GUEST_NEEDS_HOST_CURSOR) +/** The mask of all capabilities which the guest can legitimately change */ +#define VMMDEV_MOUSE_GUEST_MASK \ + (VMMDEV_MOUSE_NOTIFY_HOST_MASK | VMMDEV_MOUSE_NEW_PROTOCOL) +/** + * The mask of host capability changes for which notification events should + * be sent. + */ +#define VMMDEV_MOUSE_NOTIFY_GUEST_MASK \ + VMMDEV_MOUSE_HOST_WANTS_ABSOLUTE +/** The mask of all capabilities which the host can legitimately change */ +#define VMMDEV_MOUSE_HOST_MASK \ + (VMMDEV_MOUSE_NOTIFY_GUEST_MASK |\ + VMMDEV_MOUSE_HOST_CANNOT_HWPOINTER |\ + VMMDEV_MOUSE_HOST_RECHECKS_NEEDS_HOST_CURSOR| \ + VMMDEV_MOUSE_HOST_HAS_ABS_DEV) +/** @} */ + +/** + * @name Absolute mouse reporting range + * @{ + */ +/** @todo Should these be here? They are needed by both host and guest. */ +/** The minimum value our pointing device can return. */ +#define VMMDEV_MOUSE_RANGE_MIN 0 +/** The maximum value our pointing device can return. */ +#define VMMDEV_MOUSE_RANGE_MAX 0xFFFF +/** The full range our pointing device can return. */ +#define VMMDEV_MOUSE_RANGE (VMMDEV_MOUSE_RANGE_MAX - VMMDEV_MOUSE_RANGE_MIN) +/** @} */ + +/** + * Mouse pointer shape/visibility change request. + * + * Used by VMMDevReq_SetPointerShape. The size is variable. + */ +typedef struct VMMDevReqMousePointer { + /** Header. */ + VMMDevRequestHeader header; + /** VBOX_MOUSE_POINTER_* bit flags from VBox/Graphics/VBoxVideo.h. */ + __u32 fFlags; + /** x coordinate of hot spot. */ + __u32 xHot; + /** y coordinate of hot spot. */ + __u32 yHot; + /** Width of the pointer in pixels. */ + __u32 width; + /** Height of the pointer in scanlines. */ + __u32 height; + /** + * Pointer data. + * + **** + * The data consists of 1 bpp AND mask followed by 32 bpp XOR (color) + * mask. + * + * For pointers without alpha channel the XOR mask pixels are 32 bit + * values: (lsb)BGR0(msb). + * For pointers with alpha channel the XOR mask consists of + * (lsb)BGRA(msb) 32 bit values. + * + * Guest driver must create the AND mask for pointers with alpha chan, + * so if host does not support alpha, the pointer could be displayed as + * a normal color pointer. The AND mask can be constructed from alpha + * values. For example alpha value >= 0xf0 means bit 0 in the AND mask. + * + * The AND mask is 1 bpp bitmap with byte aligned scanlines. Size of AND + * mask, therefore, is cbAnd = (width + 7) / 8 * height. The padding + * bits at the end of any scanline are undefined. + * + * The XOR mask follows the AND mask on the next 4 bytes aligned offset: + * u8 *pXor = pAnd + (cbAnd + 3) & ~3 + * Bytes in the gap between the AND and the XOR mask are undefined. + * XOR mask scanlines have no gap between them and size of XOR mask is: + * cXor = width * 4 * height. + **** + * + * Preallocate 4 bytes for accessing actual data as p->pointerData. + */ + char pointerData[4]; +} VMMDevReqMousePointer; +VMMDEV_ASSERT_SIZE(VMMDevReqMousePointer, 24+24); + +/** + * String log request structure. + * + * Used by VMMDevReq_LogString. + * @deprecated Use the IPRT logger or VbglR3WriteLog instead. + */ +typedef struct { + /** header */ + VMMDevRequestHeader header; + /** variable length string data */ + char szString[1]; +} VMMDevReqLogString; +VMMDEV_ASSERT_SIZE(VMMDevReqLogString, 24+4); + +/** + * VirtualBox host version request structure. + * + * Used by VMMDevReq_GetHostVersion. + * + * @remarks VBGL uses this to detect the precense of new features in the + * interface. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Major version. */ + __u16 major; + /** Minor version. */ + __u16 minor; + /** Build number. */ + __u32 build; + /** SVN revision. */ + __u32 revision; + /** Feature mask. */ + __u32 features; +} VMMDevReqHostVersion; +VMMDEV_ASSERT_SIZE(VMMDevReqHostVersion, 24+16); + +/** + * @name VMMDevReqHostVersion::features + * @{ + */ +/** Physical page lists are supported by HGCM. */ +#define VMMDEV_HVF_HGCM_PHYS_PAGE_LIST BIT(0) +/** @} */ + +/** + * Guest capabilities structure. + * + * Used by VMMDevReq_ReportGuestCapabilities. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Capabilities (VMMDEV_GUEST_*). */ + __u32 caps; +} VMMDevReqGuestCapabilities; +VMMDEV_ASSERT_SIZE(VMMDevReqGuestCapabilities, 24+4); + +/** + * Guest capabilities structure, version 2. + * + * Used by VMMDevReq_SetGuestCapabilities. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Mask of capabilities to be added. */ + __u32 u32OrMask; + /** Mask of capabilities to be removed. */ + __u32 u32NotMask; +} VMMDevReqGuestCapabilities2; +VMMDEV_ASSERT_SIZE(VMMDevReqGuestCapabilities2, 24+8); + +/** + * @name Guest capability bits. + * Used by VMMDevReq_ReportGuestCapabilities and VMMDevReq_SetGuestCapabilities. + * @{ + */ +/** The guest supports seamless display rendering. */ +#define VMMDEV_GUEST_SUPPORTS_SEAMLESS BIT(0) +/** The guest supports mapping guest to host windows. */ +#define VMMDEV_GUEST_SUPPORTS_GUEST_HOST_WINDOW_MAPPING BIT(1) +/** + * The guest graphical additions are active. + * Used for fast activation and deactivation of certain graphical operations + * (e.g. resizing & seamless). The legacy VMMDevReq_ReportGuestCapabilities + * request sets this automatically, but VMMDevReq_SetGuestCapabilities does + * not. + */ +#define VMMDEV_GUEST_SUPPORTS_GRAPHICS BIT(2) +/** The mask of valid events, for sanity checking. */ +#define VMMDEV_GUEST_CAPABILITIES_MASK 0x00000007U +/** @} */ + +/** + * Idle request structure. + * + * Used by VMMDevReq_Idle. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; +} VMMDevReqIdle; +VMMDEV_ASSERT_SIZE(VMMDevReqIdle, 24); + +/** + * Host time request structure. + * + * Used by VMMDevReq_GetHostTime. + */ +typedef struct { + /** Header */ + VMMDevRequestHeader header; + /** OUT: Time in milliseconds since unix epoch. */ + __u64 time; +} VMMDevReqHostTime; +VMMDEV_ASSERT_SIZE(VMMDevReqHostTime, 24+8); + +/** + * Hypervisor info structure. + * + * Used by VMMDevReq_GetHypervisorInfo and VMMDevReq_SetHypervisorInfo. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** + * Guest virtual address of proposed hypervisor start. + * Not used by VMMDevReq_GetHypervisorInfo. + * @todo Make this 64-bit compatible? + */ + __u32 hypervisorStart; + /** Hypervisor size in bytes. */ + __u32 hypervisorSize; +} VMMDevReqHypervisorInfo; +VMMDEV_ASSERT_SIZE(VMMDevReqHypervisorInfo, 24+8); + +/** + * @name Default patch memory size . + * Used by VMMDevReq_RegisterPatchMemory and VMMDevReq_DeregisterPatchMemory. + * @{ + */ +#define VMMDEV_GUEST_DEFAULT_PATCHMEM_SIZE 8192 +/** @} */ + +/** + * Patching memory structure. (locked executable & read-only page from the + * guest's perspective) + * + * Used by VMMDevReq_RegisterPatchMemory and VMMDevReq_DeregisterPatchMemory + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Guest virtual address of the patching page(s). */ + __u64 pPatchMem; + /** Patch page size in bytes. */ + __u32 cbPatchMem; +} VMMDevReqPatchMemory; +VMMDEV_ASSERT_SIZE(VMMDevReqPatchMemory, 24+12); + +/** + * Guest power requests. + * + * See VMMDevReq_SetPowerStatus and VMMDevPowerStateRequest. + */ +typedef enum { + VMMDevPowerState_Invalid = 0, + VMMDevPowerState_Pause = 1, + VMMDevPowerState_PowerOff = 2, + VMMDevPowerState_SaveState = 3, + VMMDevPowerState_SizeHack = 0x7fffffff +} VMMDevPowerState; +VMMDEV_ASSERT_SIZE(VMMDevPowerState, 4); + +/** + * VM power status structure. + * + * Used by VMMDevReq_SetPowerStatus. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Power state request. */ + VMMDevPowerState powerState; +} VMMDevPowerStateRequest; +VMMDEV_ASSERT_SIZE(VMMDevPowerStateRequest, 24+4); + +/** + * Pending events structure. + * + * Used by VMMDevReq_AcknowledgeEvents. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** OUT: Pending event mask. */ + __u32 events; +} VMMDevEvents; +VMMDEV_ASSERT_SIZE(VMMDevEvents, 24+4); + +/** + * Guest event filter mask control. + * + * Used by VMMDevReq_CtlGuestFilterMask. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Mask of events to be added to the filter. */ + __u32 u32OrMask; + /** Mask of events to be removed from the filter. */ + __u32 u32NotMask; +} VMMDevCtlGuestFilterMask; +VMMDEV_ASSERT_SIZE(VMMDevCtlGuestFilterMask, 24+8); + +/** + * Guest information structure. + * + * Used by VMMDevReportGuestInfo and PDMIVMMDEVCONNECTOR::pfnUpdateGuestVersion. + */ +typedef struct VBoxGuestInfo { + /** + * The VMMDev interface version expected by additions. + * *Deprecated*, do not use anymore! Will be removed. + */ + __u32 interfaceVersion; + /** Guest OS type. */ + VBOXOSTYPE osType; +} VBoxGuestInfo; +VMMDEV_ASSERT_SIZE(VBoxGuestInfo, 8); + +/** + * Guest information report. + * + * Used by VMMDevReq_ReportGuestInfo. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Guest information. */ + VBoxGuestInfo guestInfo; +} VMMDevReportGuestInfo; +VMMDEV_ASSERT_SIZE(VMMDevReportGuestInfo, 24+8); + +/** + * Guest information structure, version 2. + * + * Used by VMMDevReportGuestInfo2. + */ +typedef struct VBoxGuestInfo2 { + /** Major version. */ + __u16 additionsMajor; + /** Minor version. */ + __u16 additionsMinor; + /** Build number. */ + __u32 additionsBuild; + /** SVN revision. */ + __u32 additionsRevision; + /** Feature mask, currently unused. */ + __u32 additionsFeatures; + /** + * The intentional meaning of this field was: + * Some additional information, for example 'Beta 1' or something like + * that. + * + * The way it was implemented was implemented: VBOX_VERSION_STRING. + * + * This means the first three members are duplicated in this field (if + * the guest build config is sane). So, the user must check this and + * chop it off before usage. There is, because of the Main code's blind + * trust in the field's content, no way back. + */ + char szName[128]; +} VBoxGuestInfo2; +VMMDEV_ASSERT_SIZE(VBoxGuestInfo2, 144); + +/** + * Guest information report, version 2. + * + * Used by VMMDevReq_ReportGuestInfo2. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Guest information. */ + VBoxGuestInfo2 guestInfo; +} VMMDevReportGuestInfo2; +VMMDEV_ASSERT_SIZE(VMMDevReportGuestInfo2, 24+144); + +/** + * The guest facility. + * This needs to be kept in sync with AdditionsFacilityType of the Main API! + */ +typedef enum { + VBoxGuestFacilityType_Unknown = 0, + VBoxGuestFacilityType_VBoxGuestDriver = 20, + /* VBoxGINA / VBoxCredProv / pam_vbox. */ + VBoxGuestFacilityType_AutoLogon = 90, + VBoxGuestFacilityType_VBoxService = 100, + /* VBoxTray (Windows), VBoxClient (Linux, Unix). */ + VBoxGuestFacilityType_VBoxTrayClient = 101, + VBoxGuestFacilityType_Seamless = 1000, + VBoxGuestFacilityType_Graphics = 1100, + VBoxGuestFacilityType_All = 0x7ffffffe, + VBoxGuestFacilityType_SizeHack = 0x7fffffff +} VBoxGuestFacilityType; +VMMDEV_ASSERT_SIZE(VBoxGuestFacilityType, 4); + +/** + * The current guest status of a facility. + * This needs to be kept in sync with AdditionsFacilityStatus of the Main API! + * + * @remarks r=bird: Pretty please, for future types like this, simply do a + * linear allocation without any gaps. This stuff is impossible to work + * efficiently with, let alone validate. Applies to the other facility + * enums too. + */ +typedef enum { + VBoxGuestFacilityStatus_Inactive = 0, + VBoxGuestFacilityStatus_Paused = 1, + VBoxGuestFacilityStatus_PreInit = 20, + VBoxGuestFacilityStatus_Init = 30, + VBoxGuestFacilityStatus_Active = 50, + VBoxGuestFacilityStatus_Terminating = 100, + VBoxGuestFacilityStatus_Terminated = 101, + VBoxGuestFacilityStatus_Failed = 800, + VBoxGuestFacilityStatus_Unknown = 999, + VBoxGuestFacilityStatus_SizeHack = 0x7fffffff +} VBoxGuestFacilityStatus; +VMMDEV_ASSERT_SIZE(VBoxGuestFacilityStatus, 4); + +/** + * The facility class. + * This needs to be kept in sync with AdditionsFacilityClass of the Main API! + */ +typedef enum { + VBoxGuestFacilityClass_None = 0, + VBoxGuestFacilityClass_Driver = 10, + VBoxGuestFacilityClass_Service = 30, + VBoxGuestFacilityClass_Program = 50, + VBoxGuestFacilityClass_Feature = 100, + VBoxGuestFacilityClass_ThirdParty = 999, + VBoxGuestFacilityClass_All = 0x7ffffffe, + VBoxGuestFacilityClass_SizeHack = 0x7fffffff +} VBoxGuestFacilityClass; +VMMDEV_ASSERT_SIZE(VBoxGuestFacilityClass, 4); + +/** + * Guest status structure. + * + * Used by VMMDevReqGuestStatus. + */ +typedef struct VBoxGuestStatus { + /** Facility the status is indicated for. */ + VBoxGuestFacilityType facility; + /** Current guest status. */ + VBoxGuestFacilityStatus status; + /** Flags, not used at the moment. */ + __u32 flags; +} VBoxGuestStatus; +VMMDEV_ASSERT_SIZE(VBoxGuestStatus, 12); + +/** + * Guest Additions status structure. + * + * Used by VMMDevReq_ReportGuestStatus. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Guest information. */ + VBoxGuestStatus guestStatus; +} VMMDevReportGuestStatus; +VMMDEV_ASSERT_SIZE(VMMDevReportGuestStatus, 24+12); + +/** + * The current status of specific guest user. + * This needs to be kept in sync with GuestUserState of the Main API! + */ +typedef enum VBoxGuestUserState { + VBoxGuestUserState_Unknown = 0, + VBoxGuestUserState_LoggedIn = 1, + VBoxGuestUserState_LoggedOut = 2, + VBoxGuestUserState_Locked = 3, + VBoxGuestUserState_Unlocked = 4, + VBoxGuestUserState_Disabled = 5, + VBoxGuestUserState_Idle = 6, + VBoxGuestUserState_InUse = 7, + VBoxGuestUserState_Created = 8, + VBoxGuestUserState_Deleted = 9, + VBoxGuestUserState_SessionChanged = 10, + VBoxGuestUserState_CredentialsChanged = 11, + VBoxGuestUserState_RoleChanged = 12, + VBoxGuestUserState_GroupAdded = 13, + VBoxGuestUserState_GroupRemoved = 14, + VBoxGuestUserState_Elevated = 15, + VBoxGuestUserState_SizeHack = 0x7fffffff +} VBoxGuestUserState; +VMMDEV_ASSERT_SIZE(VBoxGuestUserState, 4); + +/** + * Guest user status updates. + */ +typedef struct VBoxGuestUserStatus { + /** The guest user state to send. */ + VBoxGuestUserState state; + /** Size (in bytes) of szUser. */ + __u32 cbUser; + /** Size (in bytes) of szDomain. */ + __u32 cbDomain; + /** Size (in bytes) of aDetails. */ + __u32 cbDetails; + /** Note: Here begins the dynamically allocated region. */ + /** Guest user to report state for. */ + char szUser[1]; + /** Domain the guest user is bound to. */ + char szDomain[1]; + /** Optional details of the state. */ + __u8 aDetails[1]; +} VBoxGuestUserStatus; +VMMDEV_ASSERT_SIZE(VBoxGuestUserStatus, 20); + +/** + * Guest user status structure. + * + * Used by VMMDevReq_ReportGuestUserStatus. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Guest user status. */ + VBoxGuestUserStatus status; +} VMMDevReportGuestUserState; +VMMDEV_ASSERT_SIZE(VMMDevReportGuestUserState, 24+20); + +/** + * Guest statistics structure. + * + * Used by VMMDevReportGuestStats and PDMIVMMDEVCONNECTOR::pfnReportStatistics. + */ +typedef struct VBoxGuestStatistics { + /** Virtual CPU ID. */ + __u32 u32CpuId; + /** Reported statistics. */ + __u32 u32StatCaps; + /** Idle CPU load (0-100) for last interval. */ + __u32 u32CpuLoad_Idle; + /** Kernel CPU load (0-100) for last interval. */ + __u32 u32CpuLoad_Kernel; + /** User CPU load (0-100) for last interval. */ + __u32 u32CpuLoad_User; + /** Nr of threads. */ + __u32 u32Threads; + /** Nr of processes. */ + __u32 u32Processes; + /** Nr of handles. */ + __u32 u32Handles; + /** Memory load (0-100). */ + __u32 u32MemoryLoad; + /** Page size of guest system. */ + __u32 u32PageSize; + /** Total physical memory (in 4KB pages). */ + __u32 u32PhysMemTotal; + /** Available physical memory (in 4KB pages). */ + __u32 u32PhysMemAvail; + /** Ballooned physical memory (in 4KB pages). */ + __u32 u32PhysMemBalloon; + /** Total committed memory (not necessarily in-use) (in 4KB pages). */ + __u32 u32MemCommitTotal; + /** Total amount of memory used by the kernel (in 4KB pages). */ + __u32 u32MemKernelTotal; + /** Total amount of paged memory used by the kernel (in 4KB pages). */ + __u32 u32MemKernelPaged; + /** Total amount of nonpaged memory used by the kernel (4KB pages). */ + __u32 u32MemKernelNonPaged; + /** Total amount of memory used for the system cache (in 4KB pages). */ + __u32 u32MemSystemCache; + /** Pagefile size (in 4KB pages). */ + __u32 u32PageFileSize; +} VBoxGuestStatistics; +VMMDEV_ASSERT_SIZE(VBoxGuestStatistics, 19*4); + +/** + * @name Guest statistics values (VBoxGuestStatistics::u32StatCaps). + * @{ + */ +#define VBOX_GUEST_STAT_CPU_LOAD_IDLE BIT(0) +#define VBOX_GUEST_STAT_CPU_LOAD_KERNEL BIT(1) +#define VBOX_GUEST_STAT_CPU_LOAD_USER BIT(2) +#define VBOX_GUEST_STAT_THREADS BIT(3) +#define VBOX_GUEST_STAT_PROCESSES BIT(4) +#define VBOX_GUEST_STAT_HANDLES BIT(5) +#define VBOX_GUEST_STAT_MEMORY_LOAD BIT(6) +#define VBOX_GUEST_STAT_PHYS_MEM_TOTAL BIT(7) +#define VBOX_GUEST_STAT_PHYS_MEM_AVAIL BIT(8) +#define VBOX_GUEST_STAT_PHYS_MEM_BALLOON BIT(9) +#define VBOX_GUEST_STAT_MEM_COMMIT_TOTAL BIT(10) +#define VBOX_GUEST_STAT_MEM_KERNEL_TOTAL BIT(11) +#define VBOX_GUEST_STAT_MEM_KERNEL_PAGED BIT(12) +#define VBOX_GUEST_STAT_MEM_KERNEL_NONPAGED BIT(13) +#define VBOX_GUEST_STAT_MEM_SYSTEM_CACHE BIT(14) +#define VBOX_GUEST_STAT_PAGE_FILE_SIZE BIT(15) +/** @} */ + +/** + * Guest statistics command structure. + * + * Used by VMMDevReq_ReportGuestStats. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Guest information. */ + VBoxGuestStatistics guestStats; +} VMMDevReportGuestStats; +VMMDEV_ASSERT_SIZE(VMMDevReportGuestStats, 24+19*4); + +/** + * @name The ballooning chunk size which VMMDev works at. + * @{ + */ +#define VMMDEV_MEMORY_BALLOON_CHUNK_SIZE (1048576) +#define VMMDEV_MEMORY_BALLOON_CHUNK_PAGES (1048576 / 4096) +/** @} */ + +/** + * Poll for ballooning change request. + * + * Used by VMMDevReq_GetMemBalloonChangeRequest. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Balloon size in megabytes. */ + __u32 cBalloonChunks; + /** Guest ram size in megabytes. */ + __u32 cPhysMemChunks; + /** + * Setting this to VMMDEV_EVENT_BALLOON_CHANGE_REQUEST indicates that + * the request is a response to that event. + * (Don't confuse this with VMMDevReq_AcknowledgeEvents.) + */ + __u32 eventAck; +} VMMDevGetMemBalloonChangeRequest; +VMMDEV_ASSERT_SIZE(VMMDevGetMemBalloonChangeRequest, 24+12); + +/** + * Change the size of the balloon. + * + * Used by VMMDevReq_ChangeMemBalloon. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** The number of pages in the array. */ + __u32 pages; + /** true = inflate, false = deflate. */ + __u32 inflate; + /** Physical address (u64) of each page. */ + __u64 phys_page[VMMDEV_MEMORY_BALLOON_CHUNK_PAGES]; +} VMMDevChangeMemBalloon; + +/** + * Guest statistics interval change request structure. + * + * Used by VMMDevReq_GetStatisticsChangeRequest. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** The interval in seconds. */ + __u32 u32StatInterval; + /** + * Setting this to VMMDEV_EVENT_STATISTICS_INTERVAL_CHANGE_REQUEST + * indicates that the request is a response to that event. + * (Don't confuse this with VMMDevReq_AcknowledgeEvents.) + */ + __u32 eventAck; +} VMMDevGetStatisticsChangeRequest; +VMMDEV_ASSERT_SIZE(VMMDevGetStatisticsChangeRequest, 24+8); + +/** + * The size of a string field in the credentials request (including '\\0'). + * @see VMMDevCredentials + */ +#define VMMDEV_CREDENTIALS_SZ_SIZE 128 + +/** + * Credentials request structure. + * + * Used by VMMDevReq_QueryCredentials. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** IN/OUT: Request flags. */ + __u32 u32Flags; + /** OUT: User name (UTF-8). */ + char szUserName[VMMDEV_CREDENTIALS_SZ_SIZE]; + /** OUT: Password (UTF-8). */ + char szPassword[VMMDEV_CREDENTIALS_SZ_SIZE]; + /** OUT: Domain name (UTF-8). */ + char szDomain[VMMDEV_CREDENTIALS_SZ_SIZE]; +} VMMDevCredentials; +VMMDEV_ASSERT_SIZE(VMMDevCredentials, 24+4+3*128); + +/** + * @name Credentials request flag (VMMDevCredentials::u32Flags) + * @{ + */ +/** query from host whether credentials are present */ +#define VMMDEV_CREDENTIALS_QUERYPRESENCE BIT(1) +/** read credentials from host (can be combined with clear) */ +#define VMMDEV_CREDENTIALS_READ BIT(2) +/** clear credentials on host (can be combined with read) */ +#define VMMDEV_CREDENTIALS_CLEAR BIT(3) +/** read credentials for judgement in the guest */ +#define VMMDEV_CREDENTIALS_READJUDGE BIT(8) +/** clear credentials for judegement on the host */ +#define VMMDEV_CREDENTIALS_CLEARJUDGE BIT(9) +/** report credentials acceptance by guest */ +#define VMMDEV_CREDENTIALS_JUDGE_OK BIT(10) +/** report credentials denial by guest */ +#define VMMDEV_CREDENTIALS_JUDGE_DENY BIT(11) +/** report that no judgement could be made by guest */ +#define VMMDEV_CREDENTIALS_JUDGE_NOJUDGEMENT BIT(12) + +/** flag telling the guest that credentials are present */ +#define VMMDEV_CREDENTIALS_PRESENT BIT(16) +/** flag telling guest that local logons should be prohibited */ +#define VMMDEV_CREDENTIALS_NOLOCALLOGON BIT(17) +/** @} */ + +/** + * Seamless mode. + * + * Used by VbglR3SeamlessWaitEvent + * + * @ingroup grp_vmmdev_req + * + * @todo DARN! DARN! DARN! Who forgot to do the 32-bit hack here??? + * FIXME! XXX! + * + * We will now have to carefully check how our compilers have treated this + * flag. If any are compressing it into a byte type, we'll have to check + * how the request memory is initialized. If we are 104% sure it's ok to + * expand it, we'll expand it. If not, we must redefine the field to a + * u8 and a 3 byte padding. + */ +typedef enum { + /** normal mode; entire guest desktop displayed. */ + VMMDev_Seamless_Disabled = 0, + /** visible region mode; only top-level guest windows displayed. */ + VMMDev_Seamless_Visible_Region = 1, + /** + * windowed mode; each top-level guest window is represented in a + * host window. + */ + VMMDev_Seamless_Host_Window = 2 +} VMMDevSeamlessMode; + +/** + * Seamless mode change request structure. + * + * Used by VMMDevReq_GetSeamlessChangeRequest. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + + /** New seamless mode. */ + VMMDevSeamlessMode mode; + /** + * Setting this to VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST indicates + * that the request is a response to that event. + * (Don't confuse this with VMMDevReq_AcknowledgeEvents.) + */ + __u32 eventAck; +} VMMDevSeamlessChangeRequest; +VMMDEV_ASSERT_SIZE(VMMDevSeamlessChangeRequest, 24+8); +VMMDEV_ASSERT_MEMBER_OFFSET(VMMDevSeamlessChangeRequest, eventAck, 24+4); + +/** + * Display change request structure. + * + * Used by VMMDevReq_GetDisplayChangeRequest. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Horizontal pixel resolution (0 = do not change). */ + __u32 xres; + /** Vertical pixel resolution (0 = do not change). */ + __u32 yres; + /** Bits per pixel (0 = do not change). */ + __u32 bpp; + /** + * Setting this to VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST indicates + * that the request is a response to that event. + * (Don't confuse this with VMMDevReq_AcknowledgeEvents.) + */ + __u32 eventAck; +} VMMDevDisplayChangeRequest; +VMMDEV_ASSERT_SIZE(VMMDevDisplayChangeRequest, 24+16); + +/** + * Display change request structure, version 2. + * + * Used by VMMDevReq_GetDisplayChangeRequest2. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Horizontal pixel resolution (0 = do not change). */ + __u32 xres; + /** Vertical pixel resolution (0 = do not change). */ + __u32 yres; + /** Bits per pixel (0 = do not change). */ + __u32 bpp; + /** + * Setting this to VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST indicates + * that the request is a response to that event. + * (Don't confuse this with VMMDevReq_AcknowledgeEvents.) + */ + __u32 eventAck; + /** 0 for primary display, 1 for the first secondary, etc. */ + __u32 display; +} VMMDevDisplayChangeRequest2; +VMMDEV_ASSERT_SIZE(VMMDevDisplayChangeRequest2, 24+20); + +/** + * Display change request structure, version Extended. + * + * Used by VMMDevReq_GetDisplayChangeRequestEx. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Horizontal pixel resolution (0 = do not change). */ + __u32 xres; + /** Vertical pixel resolution (0 = do not change). */ + __u32 yres; + /** Bits per pixel (0 = do not change). */ + __u32 bpp; + /** + * Setting this to VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST indicates + * that the request is a response to that event. + * (Don't confuse this with VMMDevReq_AcknowledgeEvents.) + */ + __u32 eventAck; + /** 0 for primary display, 1 for the first secondary, etc. */ + __u32 display; + /** New OriginX of secondary virtual screen */ + __u32 cxOrigin; + /** New OriginY of secondary virtual screen */ + __u32 cyOrigin; + /** Change in origin of the secondary virtual screen is required */ + u8 fChangeOrigin; + /** Secondary virtual screen enabled or disabled */ + u8 fEnabled; + /** Alignment */ + u8 alignment[2]; +} VMMDevDisplayChangeRequestEx; +VMMDEV_ASSERT_SIZE(VMMDevDisplayChangeRequestEx, 24+32); + +/** + * Video mode supported request structure. + * + * Used by VMMDevReq_VideoModeSupported. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** IN: Horizontal pixel resolution. */ + __u32 width; + /** IN: Vertical pixel resolution. */ + __u32 height; + /** IN: Bits per pixel. */ + __u32 bpp; + /** OUT: Support indicator. */ + u8 fSupported; + /** Alignment */ + u8 alignment[3]; +} VMMDevVideoModeSupportedRequest; +VMMDEV_ASSERT_SIZE(VMMDevVideoModeSupportedRequest, 24+16); + +/** + * Video mode supported request structure for a specific display. + * + * Used by VMMDevReq_VideoModeSupported2. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** IN: The guest display number. */ + __u32 display; + /** IN: Horizontal pixel resolution. */ + __u32 width; + /** IN: Vertical pixel resolution. */ + __u32 height; + /** IN: Bits per pixel. */ + __u32 bpp; + /** OUT: Support indicator. */ + u8 fSupported; + /** Alignment */ + u8 alignment[3]; +} VMMDevVideoModeSupportedRequest2; +VMMDEV_ASSERT_SIZE(VMMDevVideoModeSupportedRequest2, 24+20); + +/** + * Video modes height reduction request structure. + * + * Used by VMMDevReq_GetHeightReduction. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** OUT: Height reduction in pixels. */ + __u32 heightReduction; +} VMMDevGetHeightReductionRequest; +VMMDEV_ASSERT_SIZE(VMMDevGetHeightReductionRequest, 24+4); + +/** + * VRDP change request structure. + * + * Used by VMMDevReq_GetVRDPChangeRequest. + */ +typedef struct { + /** Header */ + VMMDevRequestHeader header; + /** Whether VRDP is active or not. */ + __u8 u8VRDPActive; + /** The configured experience level for active VRDP. */ + __u32 u32VRDPExperienceLevel; +} VMMDevVRDPChangeRequest; +VMMDEV_ASSERT_SIZE(VMMDevVRDPChangeRequest, 24+8); +VMMDEV_ASSERT_MEMBER_OFFSET(VMMDevVRDPChangeRequest, u8VRDPActive, 24); +VMMDEV_ASSERT_MEMBER_OFFSET(VMMDevVRDPChangeRequest, u32VRDPExperienceLevel, + 24+4); + +/** + * @name VRDP Experience level (VMMDevVRDPChangeRequest::u32VRDPExperienceLevel) + * @{ + */ +#define VRDP_EXPERIENCE_LEVEL_ZERO 0 /**< Theming disabled. */ +#define VRDP_EXPERIENCE_LEVEL_LOW 1 /**< Full win drag + wallpaper dis. */ +#define VRDP_EXPERIENCE_LEVEL_MEDIUM 2 /**< Font smoothing, gradients. */ +#define VRDP_EXPERIENCE_LEVEL_HIGH 3 /**< Animation effects disabled. */ +#define VRDP_EXPERIENCE_LEVEL_FULL 4 /**< Everything enabled. */ +/** @} */ + +/** + * VBVA enable request structure. + * + * Used by VMMDevReq_VideoAccelEnable. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** 0 - disable, !0 - enable. */ + __u32 u32Enable; + /** + * The size of VBVAMEMORY::au8RingBuffer expected by driver. + * The host will refuse to enable VBVA if the size is not equal to + * VBVA_RING_BUFFER_SIZE. + */ + __u32 cbRingBuffer; + /** + * Guest initializes the status to 0. Host sets appropriate + * VBVA_F_STATUS_ flags. + */ + __u32 fu32Status; +} VMMDevVideoAccelEnable; +VMMDEV_ASSERT_SIZE(VMMDevVideoAccelEnable, 24+12); + +/** + * @name VMMDevVideoAccelEnable::fu32Status. + * @{ + */ +#define VBVA_F_STATUS_ACCEPTED (0x01) +#define VBVA_F_STATUS_ENABLED (0x02) +/** @} */ + +/** + * VBVA flush request structure. + * + * Used by VMMDevReq_VideoAccelFlush. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; +} VMMDevVideoAccelFlush; +VMMDEV_ASSERT_SIZE(VMMDevVideoAccelFlush, 24); + +/** + * Rectangle data type, double point. + */ +typedef struct RTRECT { + /** left X coordinate. */ + __s32 xLeft; + /** top Y coordinate. */ + __s32 yTop; + /** right X coordinate. (exclusive) */ + __s32 xRight; + /** bottom Y coordinate. (exclusive) */ + __s32 yBottom; +} RTRECT; + +/** + * VBVA set visible region request structure. + * + * Used by VMMDevReq_VideoSetVisibleRegion. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Number of rectangles */ + __u32 cRect; + /** + * Rectangle array. + * @todo array is spelled aRects[1]. + */ + RTRECT Rect; +} VMMDevVideoSetVisibleRegion; +VMMDEV_ASSERT_SIZE(RTRECT, 16); +VMMDEV_ASSERT_SIZE(VMMDevVideoSetVisibleRegion, 24+4+16); + +/** + * CPU event types. + */ +typedef enum { + VMMDevCpuStatusType_Invalid = 0, + VMMDevCpuStatusType_Disable = 1, + VMMDevCpuStatusType_Enable = 2, + VMMDevCpuStatusType_SizeHack = 0x7fffffff +} VMMDevCpuStatusType; + +/** + * CPU hotplug event status request. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Status type */ + VMMDevCpuStatusType enmStatusType; +} VMMDevCpuHotPlugStatusRequest; +VMMDEV_ASSERT_SIZE(VMMDevCpuHotPlugStatusRequest, 24+4); + +/** + * CPU event types. + * + * Used by VbglR3CpuHotplugWaitForEvent + * + * @ingroup grp_vmmdev_req + */ +typedef enum { + VMMDevCpuEventType_Invalid = 0, + VMMDevCpuEventType_None = 1, + VMMDevCpuEventType_Plug = 2, + VMMDevCpuEventType_Unplug = 3, + VMMDevCpuEventType_SizeHack = 0x7fffffff +} VMMDevCpuEventType; + +/** + * Get the ID of the changed CPU and event type. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Event type */ + VMMDevCpuEventType enmEventType; + /** core id of the CPU changed */ + __u32 idCpuCore; + /** package id of the CPU changed */ + __u32 idCpuPackage; +} VMMDevGetCpuHotPlugRequest; +VMMDEV_ASSERT_SIZE(VMMDevGetCpuHotPlugRequest, 24+4+4+4); + +/** + * Shared region description + */ +typedef struct VMMDEVSHAREDREGIONDESC { + __u64 GCRegionAddr; + __u32 cbRegion; + __u32 alignment; +} VMMDEVSHAREDREGIONDESC; +VMMDEV_ASSERT_SIZE(VMMDEVSHAREDREGIONDESC, 16); + +#define VMMDEVSHAREDREGIONDESC_MAX 32 + +/** + * Shared module registration + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Shared module size. */ + __u32 cbModule; + /** Number of included region descriptors */ + __u32 cRegions; + /** Base address of the shared module. */ + __u64 GCBaseAddr; + /** Guest OS type. */ + VBOXOSFAMILY enmGuestOS; + /** Alignment. */ + __u32 alignment; + /** Module name */ + char szName[128]; + /** Module version */ + char szVersion[16]; + /** Shared region descriptor(s). */ + VMMDEVSHAREDREGIONDESC aRegions[1]; +} VMMDevSharedModuleRegistrationRequest; +VMMDEV_ASSERT_SIZE(VMMDevSharedModuleRegistrationRequest, + 24+4+4+8+4+4+128+16+16); + +/** + * Shared module unregistration + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Shared module size. */ + __u32 cbModule; + /** Align at 8 byte boundary. */ + __u32 alignment; + /** Base address of the shared module. */ + __u64 GCBaseAddr; + /** Module name */ + char szName[128]; + /** Module version */ + char szVersion[16]; +} VMMDevSharedModuleUnregistrationRequest; +VMMDEV_ASSERT_SIZE(VMMDevSharedModuleUnregistrationRequest, 24+4+4+8+128+16); + +/** + * Shared module periodic check + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; +} VMMDevSharedModuleCheckRequest; +VMMDEV_ASSERT_SIZE(VMMDevSharedModuleCheckRequest, 24); + +/** + * Paging sharing enabled query + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Enabled flag (out) */ + u8 fEnabled; + /** Alignment */ + u8 alignment[3]; +} VMMDevPageSharingStatusRequest; +VMMDEV_ASSERT_SIZE(VMMDevPageSharingStatusRequest, 24+4); + +/** + * Page sharing status query (debug build only) + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Page address, 32 bits on 32 bit builds, 64 bit on 64 bit builds */ + unsigned long GCPtrPage; + /** Page flags. */ + __u64 uPageFlags; + /** Shared flag (out) */ + u8 fShared; + /** Alignment */ + u8 alignment[3]; +} VMMDevPageIsSharedRequest; + +/** + * Session id request structure. + * + * Used by VMMDevReq_GetSessionId. + */ +typedef struct { + /** Header */ + VMMDevRequestHeader header; + /** + * OUT: unique session id; the id will be different after each start, + * reset or restore of the VM. + */ + __u64 idSession; +} VMMDevReqSessionId; +VMMDEV_ASSERT_SIZE(VMMDevReqSessionId, 24+8); + +/** + * Write Core Dump request. + * + * Used by VMMDevReq_WriteCoreDump. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** Flags (reserved, MBZ). */ + __u32 fFlags; +} VMMDevReqWriteCoreDump; +VMMDEV_ASSERT_SIZE(VMMDevReqWriteCoreDump, 24+4); + +/** Heart beat check state structure. Used by VMMDevReq_HeartbeatConfigure. */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** OUT: Guest heartbeat interval in nanosec. */ + __u64 cNsInterval; + /** Heartbeat check flag. */ + u8 fEnabled; + /** Alignment */ + u8 alignment[3]; +} VMMDevReqHeartbeat; +VMMDEV_ASSERT_SIZE(VMMDevReqHeartbeat, 24+12); + +/** + * @name HGCM flags. + * @{ + */ +#define VBOX_HGCM_REQ_DONE BIT(VBOX_HGCM_REQ_DONE_BIT) +#define VBOX_HGCM_REQ_DONE_BIT 0 +#define VBOX_HGCM_REQ_CANCELLED (0x2) +/** @} */ + +/** + * HGCM request header. + */ +typedef struct VMMDevHGCMRequestHeader { + /** Request header. */ + VMMDevRequestHeader header; + + /** HGCM flags. */ + __u32 fu32Flags; + + /** Result code. */ + __s32 result; +} VMMDevHGCMRequestHeader; +VMMDEV_ASSERT_SIZE(VMMDevHGCMRequestHeader, 24+8); + +/** + * HGCM service location types. + * @ingroup grp_vmmdev_req + */ +typedef enum { + VMMDevHGCMLoc_Invalid = 0, + VMMDevHGCMLoc_LocalHost = 1, + VMMDevHGCMLoc_LocalHost_Existing = 2, + VMMDevHGCMLoc_SizeHack = 0x7fffffff +} HGCMServiceLocationType; +VMMDEV_ASSERT_SIZE(HGCMServiceLocationType, 4); + +/** + * HGCM host service location. + * @ingroup grp_vmmdev_req + */ +typedef struct { + char achName[128]; /**< This is really szName. */ +} HGCMServiceLocationHost; +VMMDEV_ASSERT_SIZE(HGCMServiceLocationHost, 128); + +/** + * HGCM service location. + * @ingroup grp_vmmdev_req + */ +typedef struct HGCMSERVICELOCATION { + /** Type of the location. */ + HGCMServiceLocationType type; + + union { + HGCMServiceLocationHost host; + } u; +} HGCMServiceLocation; +VMMDEV_ASSERT_SIZE(HGCMServiceLocation, 128+4); + +/** + * HGCM connect request structure. + * + * Used by VMMDevReq_HGCMConnect. + */ +typedef struct { + /** HGCM request header. */ + VMMDevHGCMRequestHeader header; + + /** IN: Description of service to connect to. */ + HGCMServiceLocation loc; + + /** OUT: Client identifier assigned by local instance of HGCM. */ + __u32 u32ClientID; +} VMMDevHGCMConnect; +VMMDEV_ASSERT_SIZE(VMMDevHGCMConnect, 32+132+4); + +/** + * HGCM disconnect request structure. + * + * Used by VMMDevReq_HGCMDisconnect. + */ +typedef struct { + /** HGCM request header. */ + VMMDevHGCMRequestHeader header; + + /** IN: Client identifier. */ + __u32 u32ClientID; +} VMMDevHGCMDisconnect; +VMMDEV_ASSERT_SIZE(VMMDevHGCMDisconnect, 32+4); + +/** + * HGCM parameter type. + */ +typedef enum { + VMMDevHGCMParmType_Invalid = 0, + VMMDevHGCMParmType_32bit = 1, + VMMDevHGCMParmType_64bit = 2, + /** @deprecated Doesn't work, use PageList. */ + VMMDevHGCMParmType_PhysAddr = 3, + /** In and Out */ + VMMDevHGCMParmType_LinAddr = 4, + /** In (read; host<-guest) */ + VMMDevHGCMParmType_LinAddr_In = 5, + /** Out (write; host->guest) */ + VMMDevHGCMParmType_LinAddr_Out = 6, + /* 7 - 9 VMMDevHGCMParmType_LinAddr_Locked*, non Linux R0 usage only */ + /** Physical addresses of locked pages for a buffer. */ + VMMDevHGCMParmType_PageList = 10, + VMMDevHGCMParmType_SizeHack = 0x7fffffff +} HGCMFunctionParameterType; +VMMDEV_ASSERT_SIZE(HGCMFunctionParameterType, 4); + +/** + * HGCM function parameter, 32-bit client. + */ +typedef struct HGCMFunctionParameter32 { + HGCMFunctionParameterType type; + union { + __u32 value32; + __u64 value64; + struct { + __u32 size; + union { + __u32 physAddr; + __u32 linearAddr; + } u; + } Pointer; + struct { + /** Size of the buffer described by the page list. */ + __u32 size; + /** Relative to the request header. */ + __u32 offset; + } PageList; + } u; +} HGCMFunctionParameter32; +VMMDEV_ASSERT_SIZE(HGCMFunctionParameter32, 4+8); + +/** + * HGCM function parameter, 64-bit client. + */ +typedef struct HGCMFunctionParameter64 { + HGCMFunctionParameterType type; + union { + __u32 value32; + __u64 value64; + struct { + __u32 size; + union { + __u64 physAddr; + __u64 linearAddr; + } u; + } Pointer; + struct { + /** Size of the buffer described by the page list. */ + __u32 size; + /** Relative to the request header. */ + __u32 offset; + } PageList; + } u; +} HGCMFunctionParameter64; +VMMDEV_ASSERT_SIZE(HGCMFunctionParameter64, 4+12); + +#if __BITS_PER_LONG == 64 +#define HGCMFunctionParameter HGCMFunctionParameter64 +#else +#define HGCMFunctionParameter HGCMFunctionParameter32 +#endif + +/** + * HGCM call request structure. + * + * Used by VMMDevReq_HGCMCall32 and VMMDevReq_HGCMCall64. + */ +typedef struct { + /* request header */ + VMMDevHGCMRequestHeader header; + + /** IN: Client identifier. */ + __u32 u32ClientID; + /** IN: Service function number. */ + __u32 u32Function; + /** IN: Number of parameters. */ + __u32 cParms; + /** Parameters follow in form: HGCMFunctionParameter32|64 aParms[X]; */ +} VMMDevHGCMCall; +VMMDEV_ASSERT_SIZE(VMMDevHGCMCall, 32+12); + +/** + * @name Direction of data transfer (HGCMPageListInfo::flags). Bit flags. + * @{ + */ +#define VBOX_HGCM_F_PARM_DIRECTION_NONE 0x00000000U +#define VBOX_HGCM_F_PARM_DIRECTION_TO_HOST 0x00000001U +#define VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST 0x00000002U +#define VBOX_HGCM_F_PARM_DIRECTION_BOTH 0x00000003U +/** + * Macro for validating that the specified flags are valid. + * Note BOTH is not valid. + */ +#define VBOX_HGCM_F_PARM_ARE_VALID(fFlags) \ + ((fFlags) > VBOX_HGCM_F_PARM_DIRECTION_NONE && \ + (fFlags) < VBOX_HGCM_F_PARM_DIRECTION_BOTH) +/** @} */ + +/** + * VMMDevHGCMParmType_PageList points to this structure to actually describe + * the buffer. + */ +typedef struct { + __u32 flags; /**< VBOX_HGCM_F_PARM_*. */ + __u16 offFirstPage; /**< Offset in the first page where data begins. */ + __u16 cPages; /**< Number of pages. */ + __u64 aPages[1]; /**< Page addresses. */ +} HGCMPageListInfo; +VMMDEV_ASSERT_SIZE(HGCMPageListInfo, 4+2+2+8); + +/** Get the pointer to the first parmater of a HGCM call request. */ +#define VMMDEV_HGCM_CALL_PARMS(a) \ + ((HGCMFunctionParameter *)((__u8 *)(a) + sizeof(VMMDevHGCMCall))) + +#define VBOX_HGCM_MAX_PARMS 32 + +/** + * HGCM cancel request structure. + * + * The Cancel request is issued using the same physical memory address as was + * used for the corresponding initial HGCMCall. + * + * Used by VMMDevReq_HGCMCancel. + */ +typedef struct { + /** Header. */ + VMMDevHGCMRequestHeader header; +} VMMDevHGCMCancel; +VMMDEV_ASSERT_SIZE(VMMDevHGCMCancel, 32); + +/** + * HGCM cancel request structure, version 2. + * + * Used by VMMDevReq_HGCMCancel2. + * + * VINF_SUCCESS when cancelled. + * VERR_NOT_FOUND if the specified request cannot be found. + * VERR_INVALID_PARAMETER if the address is invalid valid. + */ +typedef struct { + /** Header. */ + VMMDevRequestHeader header; + /** The physical address of the request to cancel. */ + __u32 physReqToCancel; +} VMMDevHGCMCancel2; +VMMDEV_ASSERT_SIZE(VMMDevHGCMCancel2, 24+4); + +/** @} */ + +#pragma pack() + +#endif diff --git a/include/uapi/linux/vboxguest.h b/include/uapi/linux/vboxguest.h new file mode 100644 index 000000000000..7fa83c8b9054 --- /dev/null +++ b/include/uapi/linux/vboxguest.h @@ -0,0 +1,359 @@ +/* + * VBoxGuest - VirtualBox Guest Additions Driver Interface. (ADD,DEV) + * + * Copyright (C) 2006-2016 Oracle Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, in which case the provisions of the CDDL are applicable + * instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + +#ifndef __UAPI_VBOXGUEST_H__ +#define __UAPI_VBOXGUEST_H__ + +#include <asm/bitsperlong.h> +#include <linux/ioctl.h> +#include <linux/vbox_vmmdev.h> /* For HGCMServiceLocation */ + +/** + * @defgroup grp_vboxguest VirtualBox Guest Additions Device Driver + * + * Also know as VBoxGuest. + * + * @{ + */ + +/** + * @defgroup grp_vboxguest_ioc VirtualBox Guest Additions Driver Interface + * @{ + */ + +/** + * @name VBoxGuest IOCTL codes and structures. + * + * The range 0..15 is for basic driver communication. + * The range 16..31 is for HGCM communication. + * The range 32..47 is reserved for future use. + * The range 48..63 is for OS specific communication. + * The 7th bit is reserved for future hacks. + * The 8th bit is reserved for distinguishing between 32-bit and 64-bit + * processes in future 64-bit guest additions. + * @{ + */ +#if __BITS_PER_LONG == 64 +#define VBOXGUEST_IOCTL_FLAG 128 +#else +#define VBOXGUEST_IOCTL_FLAG 0 +#endif +/** @} */ + +#define VBOXGUEST_IOCTL_CODE_(function, size) \ + _IOC(_IOC_READ|_IOC_WRITE, 'V', (function), (size)) + +#define VBOXGUEST_IOCTL_CODE(function, size) \ + VBOXGUEST_IOCTL_CODE_((function) | VBOXGUEST_IOCTL_FLAG, (size)) +/* Define 32 bit codes to support 32 bit applications in 64 bit guest driver. */ +#define VBOXGUEST_IOCTL_CODE_32(function, size) \ + VBOXGUEST_IOCTL_CODE_((function), (size)) + +/** IOCTL to VBoxGuest to wait for a VMMDev host notification */ +#define VBOXGUEST_IOCTL_WAITEVENT \ + VBOXGUEST_IOCTL_CODE_(2, sizeof(VBoxGuestWaitEventInfo)) + +/** + * @name Result codes for VBoxGuestWaitEventInfo::u32Result + * @{ + */ +/** Successful completion, an event occurred. */ +#define VBOXGUEST_WAITEVENT_OK (0) +/** Successful completion, timed out. */ +#define VBOXGUEST_WAITEVENT_TIMEOUT (1) +/** Wait was interrupted. */ +#define VBOXGUEST_WAITEVENT_INTERRUPTED (2) +/** An error occurred while processing the request. */ +#define VBOXGUEST_WAITEVENT_ERROR (3) +/** @} */ + +/** Input and output buffers layout of the IOCTL_VBOXGUEST_WAITEVENT */ +typedef struct VBoxGuestWaitEventInfo { + /** timeout in milliseconds */ + __u32 u32TimeoutIn; + /** events to wait for */ + __u32 u32EventMaskIn; + /** result code */ + __u32 u32Result; + /** events occurred */ + __u32 u32EventFlagsOut; +} VBoxGuestWaitEventInfo; +VMMDEV_ASSERT_SIZE(VBoxGuestWaitEventInfo, 16); + + +/** + * IOCTL to VBoxGuest to perform a VMM request + * @remark The data buffer for this IOCtl has an variable size, keep this in + * mind on systems where this matters. + */ +#define VBOXGUEST_IOCTL_VMMREQUEST(size) \ + VBOXGUEST_IOCTL_CODE_(3, (size)) + + +/** IOCTL to VBoxGuest to control event filter mask. */ +#define VBOXGUEST_IOCTL_CTL_FILTER_MASK \ + VBOXGUEST_IOCTL_CODE_(4, sizeof(VBoxGuestFilterMaskInfo)) + +/** Input and output buffer layout of the IOCTL_VBOXGUEST_CTL_FILTER_MASK. */ +typedef struct VBoxGuestFilterMaskInfo { + __u32 u32OrMask; + __u32 u32NotMask; +} VBoxGuestFilterMaskInfo; +VMMDEV_ASSERT_SIZE(VBoxGuestFilterMaskInfo, 8); + +/** + * IOCTL to VBoxGuest to interrupt (cancel) any pending WAITEVENTs and return. + * Handled inside the guest additions and not seen by the host at all. + * After calling this, VBOXGUEST_IOCTL_WAITEVENT should no longer be called in + * the same session. Any VBOXGUEST_IOCTL_WAITEVENT calls in the same session + * done after calling this will directly exit with VERR_INTERRUPTED. + * @see VBOXGUEST_IOCTL_WAITEVENT + */ +#define VBOXGUEST_IOCTL_CANCEL_ALL_WAITEVENTS \ + VBOXGUEST_IOCTL_CODE_(5, 0) + +/** + * IOCTL to VBoxGuest to perform backdoor logging. + * The argument is a string buffer of the specified size. + */ +#define VBOXGUEST_IOCTL_LOG(size) \ + VBOXGUEST_IOCTL_CODE_(6, (size)) + +/** + * IOCTL to VBoxGuest to check memory ballooning. The guest kernel module / + * device driver will ask the host for the current size of the balloon and + * adjust the size. Or it will set fHandledInR0 = false and R3 is responsible + * for allocating memory and calling R0 (VBOXGUEST_IOCTL_CHANGE_BALLOON). + */ +#define VBOXGUEST_IOCTL_CHECK_BALLOON \ + VBOXGUEST_IOCTL_CODE_(7, sizeof(VBoxGuestCheckBalloonInfo)) + +/** Output buffer layout of the VBOXGUEST_IOCTL_CHECK_BALLOON. */ +typedef struct VBoxGuestCheckBalloonInfo { + /** The size of the balloon in chunks of 1MB. */ + __u32 cBalloonChunks; + /** + * false = handled in R0, no further action required. + * true = allocate balloon memory in R3. + */ + __u32 fHandleInR3; +} VBoxGuestCheckBalloonInfo; +VMMDEV_ASSERT_SIZE(VBoxGuestCheckBalloonInfo, 8); + +/** + * IOCTL to VBoxGuest to supply or revoke one chunk for ballooning. + * The guest kernel module / device driver will lock down supplied memory or + * unlock reclaimed memory and then forward the physical addresses of the + * changed balloon chunk to the host. + */ +#define VBOXGUEST_IOCTL_CHANGE_BALLOON \ + VBOXGUEST_IOCTL_CODE_(8, sizeof(VBoxGuestChangeBalloonInfo)) + +/** + * Input buffer layout of the VBOXGUEST_IOCTL_CHANGE_BALLOON request. + * Information about a memory chunk used to inflate or deflate the balloon. + */ +typedef struct VBoxGuestChangeBalloonInfo { + /** Address of the chunk. */ + __u64 u64ChunkAddr; + /** true = inflate, false = deflate. */ + __u32 fInflate; + /** Alignment padding. */ + __u32 u32Align; +} VBoxGuestChangeBalloonInfo; +VMMDEV_ASSERT_SIZE(VBoxGuestChangeBalloonInfo, 16); + +/** IOCTL to VBoxGuest to write guest core. */ +#define VBOXGUEST_IOCTL_WRITE_CORE_DUMP \ + VBOXGUEST_IOCTL_CODE(9, sizeof(VBoxGuestWriteCoreDump)) + +/** Input and output buffer layout of the VBOXGUEST_IOCTL_WRITE_CORE request. */ +typedef struct VBoxGuestWriteCoreDump { + /** Flags (reserved, MBZ). */ + __u32 fFlags; +} VBoxGuestWriteCoreDump; +VMMDEV_ASSERT_SIZE(VBoxGuestWriteCoreDump, 4); + +/** IOCTL to VBoxGuest to update the mouse status features. */ +#define VBOXGUEST_IOCTL_SET_MOUSE_STATUS \ + VBOXGUEST_IOCTL_CODE_(10, sizeof(__u32)) + +/** IOCTL to VBoxGuest to connect to a HGCM service. */ +#define VBOXGUEST_IOCTL_HGCM_CONNECT \ + VBOXGUEST_IOCTL_CODE(16, sizeof(VBoxGuestHGCMConnectInfo)) + +/** + * HGCM connect info structure. + * + * This is used by VBOXGUEST_IOCTL_HGCM_CONNECT. + */ +struct VBoxGuestHGCMConnectInfo { + __s32 result; /**< OUT */ + HGCMServiceLocation Loc; /**< IN */ + __u32 u32ClientID; /**< OUT */ +} __packed; +typedef struct VBoxGuestHGCMConnectInfo VBoxGuestHGCMConnectInfo; +VMMDEV_ASSERT_SIZE(VBoxGuestHGCMConnectInfo, 4+4+128+4); + +/** IOCTL to VBoxGuest to disconnect from a HGCM service. */ +#define VBOXGUEST_IOCTL_HGCM_DISCONNECT \ + VBOXGUEST_IOCTL_CODE(17, sizeof(VBoxGuestHGCMDisconnectInfo)) + +/** + * HGCM disconnect info structure. + * + * This is used by VBOXGUEST_IOCTL_HGCM_DISCONNECT. + */ +typedef struct VBoxGuestHGCMDisconnectInfo { + __s32 result; /**< OUT */ + __u32 u32ClientID; /**< IN */ +} VBoxGuestHGCMDisconnectInfo; +VMMDEV_ASSERT_SIZE(VBoxGuestHGCMDisconnectInfo, 8); + +/** + * IOCTL to VBoxGuest to make a call to a HGCM service. + * @see VBoxGuestHGCMCallInfo + */ +#define VBOXGUEST_IOCTL_HGCM_CALL(size) \ + VBOXGUEST_IOCTL_CODE(18, (size)) + +/** + * HGCM call info structure. + * + * This is used by VBOXGUEST_IOCTL_HGCM_CALL. + */ +typedef struct VBoxGuestHGCMCallInfo { + __s32 result; /**< OUT Host HGCM return code.*/ + __u32 u32ClientID; /**< IN The id of the caller. */ + __u32 u32Function; /**< IN Function number. */ + __u32 cParms; /**< IN How many parms. */ + /* Parameters follow in form HGCMFunctionParameter aParms[cParms] */ +} VBoxGuestHGCMCallInfo; +VMMDEV_ASSERT_SIZE(VBoxGuestHGCMCallInfo, 16); + +/** IOCTL to VBoxGuest to make a timed call to a HGCM service. */ +#define VBOXGUEST_IOCTL_HGCM_CALL_TIMED(size) \ + VBOXGUEST_IOCTL_CODE(20, (size)) + +/** + * HGCM call info structure. + * + * This is used by VBOXGUEST_IOCTL_HGCM_CALL_TIMED. + */ +struct VBoxGuestHGCMCallInfoTimed { + /** IN How long to wait for completion before cancelling the call. */ + __u32 u32Timeout; + /** IN Is this request interruptible? */ + __u32 fInterruptible; + /** + * IN/OUT The rest of the call information. Placed after the timeout + * so that the parameters follow as they would for a normal call. + */ + VBoxGuestHGCMCallInfo info; + /* Parameters follow in form HGCMFunctionParameter aParms[cParms] */ +} __packed; +typedef struct VBoxGuestHGCMCallInfoTimed VBoxGuestHGCMCallInfoTimed; +VMMDEV_ASSERT_SIZE(VBoxGuestHGCMCallInfoTimed, 8+16); + +/** + * @name IOCTL numbers that 32-bit clients, like the Windows OpenGL guest + * driver, will use when talking to a 64-bit driver. + * @remarks These are only used by the driver implementation! + * @{ + */ +#define VBOXGUEST_IOCTL_HGCM_CONNECT_32 \ + VBOXGUEST_IOCTL_CODE_32(16, sizeof(VBoxGuestHGCMConnectInfo)) +#define VBOXGUEST_IOCTL_HGCM_DISCONNECT_32 \ + VBOXGUEST_IOCTL_CODE_32(17, sizeof(VBoxGuestHGCMDisconnectInfo)) +#define VBOXGUEST_IOCTL_HGCM_CALL_32(size) \ + VBOXGUEST_IOCTL_CODE_32(18, (size)) +#define VBOXGUEST_IOCTL_HGCM_CALL_TIMED_32(size) \ + VBOXGUEST_IOCTL_CODE_32(20, (size)) +/** @} */ + +/** Get the pointer to the first HGCM parameter. */ +#define VBOXGUEST_HGCM_CALL_PARMS(a) \ + ((HGCMFunctionParameter *)((__u8 *)(a) + sizeof(VBoxGuestHGCMCallInfo))) +/** Get the pointer to the first HGCM parameter in a 32-bit request. */ +#define VBOXGUEST_HGCM_CALL_PARMS32(a) \ + ((HGCMFunctionParameter32 *)((__u8 *)(a) + sizeof(VBoxGuestHGCMCallInfo))) + +typedef enum VBOXGUESTCAPSACQUIRE_FLAGS { + VBOXGUESTCAPSACQUIRE_FLAGS_NONE = 0, + /* + * Configures VBoxGuest to use the specified caps in Acquire mode, w/o + * making any caps acquisition/release. so far it is only possible to + * set acquire mode for caps, but not clear it, so u32NotMask is + * ignored for this request. + */ + VBOXGUESTCAPSACQUIRE_FLAGS_CONFIG_ACQUIRE_MODE, + /* To ensure enum is 32bit */ + VBOXGUESTCAPSACQUIRE_FLAGS_32bit = 0x7fffffff +} VBOXGUESTCAPSACQUIRE_FLAGS; + +typedef struct VBoxGuestCapsAquire { + /* + * result status + * VINF_SUCCESS - on success + * VERR_RESOURCE_BUSY - some caps in the u32OrMask are acquired by some + * other VBoxGuest connection. NOTE: no u32NotMask caps are cleaned + * in this case, No modifications are done on failure. + * VER_INVALID_PARAMETER - invalid Caps are specified with either + * u32OrMask or u32NotMask. No modifications are done on failure. + */ + __s32 rc; + /* Acquire command */ + VBOXGUESTCAPSACQUIRE_FLAGS enmFlags; + /* caps to acquire, OR-ed VMMDEV_GUEST_SUPPORTS_XXX flags */ + __u32 u32OrMask; + /* caps to release, OR-ed VMMDEV_GUEST_SUPPORTS_XXX flags */ + __u32 u32NotMask; +} VBoxGuestCapsAquire; + +/** + * IOCTL to for Acquiring/Releasing Guest Caps + * This is used for multiple purposes: + * 1. By doing Acquire r3 client application (e.g. VBoxTray) claims it will use + * the given connection for performing operations like Auto-resize, or + * Seamless. If the application terminates, the driver will automatically + * cleanup the caps reported to host, so that host knows guest does not + * support them anymore. + * 2. In a multy-user environment this will not allow r3 applications (like + * VBoxTray) running in different user sessions simultaneously to interfere + * with each other. An r3 client application (like VBoxTray) is responsible + * for Acquiring/Releasing caps properly as needed. + **/ +#define VBOXGUEST_IOCTL_GUEST_CAPS_ACQUIRE \ + VBOXGUEST_IOCTL_CODE(32, sizeof(VBoxGuestCapsAquire)) + +/** IOCTL to VBoxGuest to set guest capabilities. */ +#define VBOXGUEST_IOCTL_SET_GUEST_CAPABILITIES \ + VBOXGUEST_IOCTL_CODE_(33, sizeof(VBoxGuestSetCapabilitiesInfo)) + +/** Input/output buffer layout for VBOXGUEST_IOCTL_SET_GUEST_CAPABILITIES. */ +typedef struct VBoxGuestSetCapabilitiesInfo { + __u32 u32OrMask; + __u32 u32NotMask; +} VBoxGuestSetCapabilitiesInfo; +VMMDEV_ASSERT_SIZE(VBoxGuestSetCapabilitiesInfo, 8); + +/** @} */ + +/** @} */ + +#endif -- 2.13.4
|  |