Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,20 @@
# SPDX-License-Identifier: GPL-2.0-only
config VBOXGUEST
tristate "Virtual Box Guest integration support"
depends on (ARM64 || X86 || COMPILE_TEST) && PCI && INPUT
depends on HAS_IOPORT
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.
If you enable this driver you should also enable the VBOXVIDEO option.
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.
@@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
vboxguest-y := vboxguest_linux.o vboxguest_core.o vboxguest_utils.o
obj-$(CONFIG_VBOXGUEST) += vboxguest.o
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,214 @@
/* SPDX-License-Identifier: (GPL-2.0 OR CDDL-1.0) */
/* Copyright (C) 2010-2016 Oracle Corporation */
#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/vboxguest.h>
#include "vmmdev.h"
/*
* The mainline kernel version (this version) of the vboxguest module
* contained a bug where it defined VBGL_IOCTL_VMMDEV_REQUEST_BIG and
* VBGL_IOCTL_LOG using _IOC(_IOC_READ | _IOC_WRITE, 'V', ...) instead
* of _IO(V, ...) as the out of tree VirtualBox upstream version does.
*
* These _ALT definitions keep compatibility with the wrong defines the
* mainline kernel version used for a while.
* Note the VirtualBox userspace bits have always been built against
* VirtualBox upstream's headers, so this is likely not necessary. But
* we must never break our ABI so we keep these around to be 100% sure.
*/
#define VBG_IOCTL_VMMDEV_REQUEST_BIG_ALT _IOC(_IOC_READ | _IOC_WRITE, 'V', 3, 0)
#define VBG_IOCTL_LOG_ALT(s) _IOC(_IOC_READ | _IOC_WRITE, 'V', 9, s)
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 vmmdev_memballoon_info req for query */
struct vmmdev_memballoon_info *get_req;
/** Pre-allocated vmmdev_memballoon_change req for inflate / deflate */
struct vmmdev_memballoon_change *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. */
struct vmmdev_memory *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 struct vmmdev_events for the IRQ handler. */
struct vmmdev_events *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 */
struct vmmdev_hgcm_cancel2 *cancel_req;
/** Mutex protecting cancel_req accesses */
struct mutex cancel_req_mutex;
/** Pre-allocated mouse-status request for the input-device handling. */
struct vmmdev_mouse_status *mouse_status_req;
/** Input device for reporting abs mouse coordinates to the guest. */
struct input_dev *input;
/** Memory balloon information. */
struct vbg_mem_balloon mem_balloon;
/** Lock for session related items in vbg_dev and vbg_session */
struct mutex session_mutex;
/** Events we won't permit anyone to filter out. */
u32 fixed_events;
/**
* Usage counters for the host events (excludes fixed events),
* Protected by session_mutex.
*/
struct vbg_bit_usage_tracker event_filter_tracker;
/**
* The event filter last reported to the host (or UINT32_MAX).
* Protected by session_mutex.
*/
u32 event_filter_host;
/**
* Guest capabilities which have been switched to acquire_mode.
*/
u32 acquire_mode_guest_caps;
/**
* Guest capabilities acquired by vbg_acquire_session_capabilities().
* Only one session can acquire a capability at a time.
*/
u32 acquired_guest_caps;
/**
* Usage counters for guest capabilities requested through
* vbg_set_session_capabilities(). Indexed by capability bit
* number, one count per session using a capability.
* Protected by session_mutex.
*/
struct vbg_bit_usage_tracker set_guest_caps_tracker;
/**
* The guest capabilities last reported to the host (or UINT32_MAX).
* Protected by session_mutex.
*/
u32 guest_caps_host;
/**
* Heartbeat timer which fires with interval
* cNsHearbeatInterval and its handler sends
* VMMDEVREQ_GUEST_HEARTBEAT to VMMDev.
*/
struct timer_list heartbeat_timer;
/** Heartbeat timer interval in ms. */
int heartbeat_interval_ms;
/** Preallocated VMMDEVREQ_GUEST_HEARTBEAT request. */
struct vmmdev_request_header *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 {
/** 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.
* Protected by vbg_gdev.session_mutex.
*/
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 vbg_gdev.session_mutex.
*/
u32 event_filter;
/**
* Guest capabilities acquired by vbg_acquire_session_capabilities().
* Only one session can acquire a capability at a time.
*/
u32 acquired_guest_caps;
/**
* Guest capabilities set through vbg_set_session_capabilities().
* A capability claimed by any guest session will be reported to the
* host. Protected by vbg_gdev.session_mutex.
*/
u32 set_guest_caps;
/** VMMDEV_REQUESTOR_* flags */
u32 requestor;
/** Set on CANCEL_ALL_WAITEVENTS, protected by vbg_devevent_spinlock. */
bool cancel_waiters;
};
int vbg_core_init(struct vbg_dev *gdev, u32 fixed_events);
void vbg_core_exit(struct vbg_dev *gdev);
struct vbg_session *vbg_core_open_session(struct vbg_dev *gdev, u32 requestor);
void vbg_core_close_session(struct vbg_session *session);
int vbg_core_ioctl(struct vbg_session *session, unsigned int req, void *data);
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);
/* Private (non exported) functions form vboxguest_utils.c */
void *vbg_req_alloc(size_t len, enum vmmdev_request_type req_type,
u32 requestor);
void vbg_req_free(void *req, size_t len);
int vbg_req_perform(struct vbg_dev *gdev, void *req);
int vbg_hgcm_call32(
struct vbg_dev *gdev, u32 requestor, u32 client_id, u32 function,
u32 timeout_ms, struct vmmdev_hgcm_function_parameter32 *parm32,
u32 parm_count, int *vbox_status);
#endif
@@ -0,0 +1,502 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* vboxguest linux pci driver, char-dev and input-device code,
*
* Copyright (C) 2006-2016 Oracle Corporation
*/
#include <linux/cred.h>
#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 u32 vbg_misc_device_requestor(struct inode *inode)
{
u32 requestor = VMMDEV_REQUESTOR_USERMODE |
VMMDEV_REQUESTOR_CON_DONT_KNOW |
VMMDEV_REQUESTOR_TRUST_NOT_GIVEN;
if (from_kuid(current_user_ns(), current_uid()) == 0)
requestor |= VMMDEV_REQUESTOR_USR_ROOT;
else
requestor |= VMMDEV_REQUESTOR_USR_USER;
if (in_egroup_p(inode->i_gid))
requestor |= VMMDEV_REQUESTOR_GRP_VBOX;
return requestor;
}
static int vbg_misc_device_open(struct inode *inode, struct file *filp)
{
struct vbg_session *session;
struct vbg_dev *gdev;
/* misc_open sets filp->private_data to our misc device */
gdev = container_of(filp->private_data, struct vbg_dev, misc_device);
session = vbg_core_open_session(gdev, vbg_misc_device_requestor(inode));
if (IS_ERR(session))
return PTR_ERR(session);
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;
/* misc_open sets filp->private_data to our misc device */
gdev = container_of(filp->private_data, struct vbg_dev,
misc_device_user);
session = vbg_core_open_session(gdev, vbg_misc_device_requestor(inode) |
VMMDEV_REQUESTOR_USER_DEVICE);
if (IS_ERR(session))
return PTR_ERR(session);
filp->private_data = session;
return 0;
}
/**
* vbg_misc_device_close - Close device.
* @inode: Pointer to inode info structure.
* @filp: Associated file pointer.
*
* Return: %0 on success, negated errno on failure.
*/
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;
}
/**
* vbg_misc_device_ioctl - Device I/O Control entry point.
* @filp: Associated file pointer.
* @req: The request specified to ioctl().
* @arg: The argument specified to ioctl().
*
* Return: %0 on success, negated errno on failure.
*/
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_size, size;
struct vbg_ioctl_hdr hdr;
bool is_vmmdev_req;
int ret = 0;
void *buf;
if (copy_from_user(&hdr, (void *)arg, sizeof(hdr)))
return -EFAULT;
if (hdr.version != VBG_IOCTL_HDR_VERSION)
return -EINVAL;
if (hdr.size_in < sizeof(hdr) ||
(hdr.size_out && hdr.size_out < sizeof(hdr)))
return -EINVAL;
size = max(hdr.size_in, hdr.size_out);
if (_IOC_SIZE(req) && _IOC_SIZE(req) != size)
return -EINVAL;
if (size > SZ_16M)
return -E2BIG;
/*
* IOCTL_VMMDEV_REQUEST needs the buffer to be below 4G to avoid
* the need for a bounce-buffer and another copy later on.
*/
is_vmmdev_req = (req & ~IOCSIZE_MASK) == VBG_IOCTL_VMMDEV_REQUEST(0) ||
req == VBG_IOCTL_VMMDEV_REQUEST_BIG ||
req == VBG_IOCTL_VMMDEV_REQUEST_BIG_ALT;
if (is_vmmdev_req)
buf = vbg_req_alloc(size, VBG_IOCTL_HDR_TYPE_DEFAULT,
session->requestor);
else
buf = kmalloc(size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
*((struct vbg_ioctl_hdr *)buf) = hdr;
if (copy_from_user(buf + sizeof(hdr), (void *)arg + sizeof(hdr),
hdr.size_in - sizeof(hdr))) {
ret = -EFAULT;
goto out;
}
if (hdr.size_in < size)
memset(buf + hdr.size_in, 0, size - hdr.size_in);
ret = vbg_core_ioctl(session, req, buf);
if (ret)
goto out;
returned_size = ((struct vbg_ioctl_hdr *)buf)->size_out;
if (returned_size > size) {
vbg_debug("%s: too much output data %zu > %zu\n",
__func__, returned_size, size);
returned_size = size;
}
if (copy_to_user((void *)arg, buf, returned_size) != 0)
ret = -EFAULT;
out:
if (is_vmmdev_req)
vbg_req_free(buf, size);
else
kfree(buf);
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;
return vbg_core_set_mouse_status(gdev, feat);
}
/*
* 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.
*
* Return: 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);
static struct attribute *vbg_pci_attrs[] = {
&dev_attr_host_version.attr,
&dev_attr_host_features.attr,
NULL,
};
ATTRIBUTE_GROUPS(vbg_pci);
/*
* Does the PCI detection and init of the device.
*
* Return: 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;
struct vmmdev_memory *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=%pap size=%pap\n",
&mmio, &mmio_len);
goto err_disable_pcidev;
}
/* Validate MMIO region version and size. */
if (vmmdev->version != VMMDEV_MEMORY_VERSION ||
vmmdev->size < 32 || vmmdev->size > mmio_len) {
vbg_err("vboxguest: Bogus VMMDev memory; version=%08x (expected %08x) size=%d (expected <= %d)\n",
vmmdev->version, VMMDEV_MEMORY_VERSION,
vmmdev->size, (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 = request_irq(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_free_irq;
}
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);
return 0;
err_unregister_misc_device_user:
misc_deregister(&gdev->misc_device_user);
err_unregister_misc_device:
misc_deregister(&gdev->misc_device);
err_free_irq:
free_irq(pci->irq, gdev);
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);
free_irq(pci->irq, gdev);
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.
*
* @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->mouse_features = 0;
gdev->mouse_status_req->pointer_pos_x = 0;
gdev->mouse_status_req->pointer_pos_y = 0;
rc = vbg_req_perform(gdev, gdev->mouse_status_req);
if (rc >= 0) {
input_report_abs(gdev->input, ABS_X,
gdev->mouse_status_req->pointer_pos_x);
input_report_abs(gdev->input, ABS_Y,
gdev->mouse_status_req->pointer_pos_y);
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,
.dev_groups = vbg_pci_groups,
.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");
@@ -0,0 +1,834 @@
/* SPDX-License-Identifier: (GPL-2.0 OR CDDL-1.0) */
/*
* vboxguest vmm-req and hgcm-call code, VBoxGuestR0LibHGCMInternal.cpp,
* VBoxGuestR0LibGenericRequest.cpp and RTErrConvertToErrno.cpp in vbox svn.
*
* Copyright (C) 2006-2016 Oracle Corporation
*/
#include <linux/errno.h>
#include <linux/io.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"
/* Get the pointer to the first parameter of a HGCM call request. */
#define VMMDEV_HGCM_CALL_PARMS(a) \
((struct vmmdev_hgcm_function_parameter *)( \
(u8 *)(a) + sizeof(struct vmmdev_hgcm_call)))
/* The max parameter buffer size for a user request. */
#define VBG_MAX_HGCM_USER_PARM (24 * SZ_1M)
/* The max parameter buffer size for a kernel request. */
#define VBG_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);
VBG_LOG(vbg_err_ratelimited, pr_err_ratelimited);
#if defined(DEBUG) && !defined(CONFIG_DYNAMIC_DEBUG)
VBG_LOG(vbg_debug, pr_debug);
#endif
void *vbg_req_alloc(size_t len, enum vmmdev_request_type req_type,
u32 requestor)
{
struct vmmdev_request_header *req;
int order = get_order(PAGE_ALIGN(len));
req = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA32, order);
if (!req)
return NULL;
memset(req, 0xaa, len);
req->size = len;
req->version = VMMDEV_REQUEST_HEADER_VERSION;
req->request_type = req_type;
req->rc = VERR_GENERAL_FAILURE;
req->reserved1 = 0;
req->requestor = requestor;
return req;
}
void vbg_req_free(void *req, size_t len)
{
if (!req)
return;
free_pages((unsigned long)req, get_order(PAGE_ALIGN(len)));
}
/* 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 ((struct vmmdev_request_header *)req)->rc;
}
static bool hgcm_req_done(struct vbg_dev *gdev,
struct vmmdev_hgcmreq_header *header)
{
unsigned long flags;
bool done;
spin_lock_irqsave(&gdev->event_spinlock, flags);
done = header->flags & VMMDEV_HGCM_REQ_DONE;
spin_unlock_irqrestore(&gdev->event_spinlock, flags);
return done;
}
int vbg_hgcm_connect(struct vbg_dev *gdev, u32 requestor,
struct vmmdev_hgcm_service_location *loc,
u32 *client_id, int *vbox_status)
{
struct vmmdev_hgcm_connect *hgcm_connect = NULL;
int rc;
hgcm_connect = vbg_req_alloc(sizeof(*hgcm_connect),
VMMDEVREQ_HGCM_CONNECT, requestor);
if (!hgcm_connect)
return -ENOMEM;
hgcm_connect->header.flags = 0;
memcpy(&hgcm_connect->loc, loc, sizeof(*loc));
hgcm_connect->client_id = 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->client_id;
rc = hgcm_connect->header.result;
}
vbg_req_free(hgcm_connect, sizeof(*hgcm_connect));
*vbox_status = rc;
return 0;
}
EXPORT_SYMBOL(vbg_hgcm_connect);
int vbg_hgcm_disconnect(struct vbg_dev *gdev, u32 requestor,
u32 client_id, int *vbox_status)
{
struct vmmdev_hgcm_disconnect *hgcm_disconnect = NULL;
int rc;
hgcm_disconnect = vbg_req_alloc(sizeof(*hgcm_disconnect),
VMMDEVREQ_HGCM_DISCONNECT,
requestor);
if (!hgcm_disconnect)
return -ENOMEM;
hgcm_disconnect->header.flags = 0;
hgcm_disconnect->client_id = 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;
vbg_req_free(hgcm_disconnect, sizeof(*hgcm_disconnect));
*vbox_status = rc;
return 0;
}
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_add_pagelist_size(void *buf, u32 len, size_t *extra)
{
u32 page_count;
page_count = hgcm_call_buf_size_in_pages(buf, len);
*extra += offsetof(struct vmmdev_hgcm_pagelist, pages[page_count]);
}
static int hgcm_call_preprocess_linaddr(
const struct vmmdev_hgcm_function_parameter *src_parm,
void **bounce_buf_ret, size_t *extra)
{
void *buf, *bounce_buf;
bool copy_in;
u32 len;
int ret;
buf = (void *)src_parm->u.pointer.u.linear_addr;
len = src_parm->u.pointer.size;
copy_in = src_parm->type != VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT;
if (len > VBG_MAX_HGCM_USER_PARM)
return -E2BIG;
bounce_buf = kvmalloc(len, GFP_KERNEL);
if (!bounce_buf)
return -ENOMEM;
*bounce_buf_ret = bounce_buf;
if (copy_in) {
ret = copy_from_user(bounce_buf, (void __user *)buf, len);
if (ret)
return -EFAULT;
} else {
memset(bounce_buf, 0, len);
}
hgcm_call_add_pagelist_size(bounce_buf, len, extra);
return 0;
}
/**
* hgcm_call_preprocess - Preprocesses the HGCM call, validate parameters,
* alloc bounce buffers and figure out how much extra storage we need for
* page lists.
* @src_parm: Pointer to source function call parameters
* @parm_count: Number of function call parameters.
* @bounce_bufs_ret: Where to return the allocated bouncebuffer array
* @extra: Where to return the extra request space needed for
* physical page lists.
*
* Return: %0 or negative errno value.
*/
static int hgcm_call_preprocess(
const struct vmmdev_hgcm_function_parameter *src_parm,
u32 parm_count, void ***bounce_bufs_ret, size_t *extra)
{
void *buf, **bounce_bufs = NULL;
u32 i, len;
int ret;
for (i = 0; i < parm_count; i++, src_parm++) {
switch (src_parm->type) {
case VMMDEV_HGCM_PARM_TYPE_32BIT:
case VMMDEV_HGCM_PARM_TYPE_64BIT:
break;
case VMMDEV_HGCM_PARM_TYPE_LINADDR:
case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN:
case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT:
if (!bounce_bufs) {
bounce_bufs = kcalloc(parm_count,
sizeof(void *),
GFP_KERNEL);
if (!bounce_bufs)
return -ENOMEM;
*bounce_bufs_ret = bounce_bufs;
}
ret = hgcm_call_preprocess_linaddr(src_parm,
&bounce_bufs[i],
extra);
if (ret)
return ret;
break;
case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL:
case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_IN:
case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_OUT:
buf = (void *)src_parm->u.pointer.u.linear_addr;
len = src_parm->u.pointer.size;
if (WARN_ON(len > VBG_MAX_HGCM_KERNEL_PARM))
return -E2BIG;
hgcm_call_add_pagelist_size(buf, len, extra);
break;
default:
return -EINVAL;
}
}
return 0;
}
/**
* hgcm_call_linear_addr_type_to_pagelist_flags - Translates linear address
* types to page list direction flags.
* @type: The type.
*
* Return: page list flags.
*/
static u32 hgcm_call_linear_addr_type_to_pagelist_flags(
enum vmmdev_hgcm_function_parameter_type type)
{
switch (type) {
default:
WARN_ON(1);
fallthrough;
case VMMDEV_HGCM_PARM_TYPE_LINADDR:
case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL:
return VMMDEV_HGCM_F_PARM_DIRECTION_BOTH;
case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN:
case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_IN:
return VMMDEV_HGCM_F_PARM_DIRECTION_TO_HOST;
case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT:
case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_OUT:
return VMMDEV_HGCM_F_PARM_DIRECTION_FROM_HOST;
}
}
static void hgcm_call_init_linaddr(struct vmmdev_hgcm_call *call,
struct vmmdev_hgcm_function_parameter *dst_parm, void *buf, u32 len,
enum vmmdev_hgcm_function_parameter_type type, u32 *off_extra)
{
struct vmmdev_hgcm_pagelist *dst_pg_lst;
struct page *page;
bool is_vmalloc;
u32 i, page_count;
dst_parm->type = type;
if (len == 0) {
dst_parm->u.pointer.size = 0;
dst_parm->u.pointer.u.linear_addr = 0;
return;
}
dst_pg_lst = (void *)call + *off_extra;
page_count = hgcm_call_buf_size_in_pages(buf, len);
is_vmalloc = is_vmalloc_addr(buf);
dst_parm->type = VMMDEV_HGCM_PARM_TYPE_PAGELIST;
dst_parm->u.page_list.size = len;
dst_parm->u.page_list.offset = *off_extra;
dst_pg_lst->flags = hgcm_call_linear_addr_type_to_pagelist_flags(type);
dst_pg_lst->offset_first_page = (unsigned long)buf & ~PAGE_MASK;
dst_pg_lst->page_count = page_count;
for (i = 0; i < page_count; i++) {
if (is_vmalloc)
page = vmalloc_to_page(buf);
else
page = virt_to_page(buf);
dst_pg_lst->pages[i] = page_to_phys(page);
buf += PAGE_SIZE;
}
*off_extra += offsetof(struct vmmdev_hgcm_pagelist, pages[page_count]);
}
/**
* hgcm_call_init_call - Initializes the call request that we're sending
* to the host.
* @call: The call to initialize.
* @client_id: The client ID of the caller.
* @function: The function number of the function to call.
* @src_parm: Pointer to source function call parameters.
* @parm_count: Number of function call parameters.
* @bounce_bufs: The bouncebuffer array.
*/
static void hgcm_call_init_call(
struct vmmdev_hgcm_call *call, u32 client_id, u32 function,
const struct vmmdev_hgcm_function_parameter *src_parm,
u32 parm_count, void **bounce_bufs)
{
struct vmmdev_hgcm_function_parameter *dst_parm =
VMMDEV_HGCM_CALL_PARMS(call);
u32 i, off_extra = (uintptr_t)(dst_parm + parm_count) - (uintptr_t)call;
void *buf;
call->header.flags = 0;
call->header.result = VINF_SUCCESS;
call->client_id = client_id;
call->function = function;
call->parm_count = parm_count;
for (i = 0; i < parm_count; i++, src_parm++, dst_parm++) {
switch (src_parm->type) {
case VMMDEV_HGCM_PARM_TYPE_32BIT:
case VMMDEV_HGCM_PARM_TYPE_64BIT:
*dst_parm = *src_parm;
break;
case VMMDEV_HGCM_PARM_TYPE_LINADDR:
case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN:
case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT:
hgcm_call_init_linaddr(call, dst_parm, bounce_bufs[i],
src_parm->u.pointer.size,
src_parm->type, &off_extra);
break;
case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL:
case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_IN:
case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_OUT:
buf = (void *)src_parm->u.pointer.u.linear_addr;
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 = VMMDEV_HGCM_PARM_TYPE_INVALID;
}
}
}
/**
* hgcm_cancel_call - Tries to cancel a pending HGCM call.
* @gdev: The VBoxGuest device extension.
* @call: The call to cancel.
*
* Return: VBox status code
*/
static int hgcm_cancel_call(struct vbg_dev *gdev, struct vmmdev_hgcm_call *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->phys_req_to_cancel = virt_to_phys(call);
rc = vbg_req_perform(gdev, gdev->cancel_req);
mutex_unlock(&gdev->cancel_req_mutex);
if (rc == VERR_NOT_IMPLEMENTED) {
call->header.flags |= VMMDEV_HGCM_REQ_CANCELLED;
call->header.header.request_type = VMMDEVREQ_HGCM_CANCEL;
rc = vbg_req_perform(gdev, call);
if (rc == VERR_INVALID_PARAMETER)
rc = VERR_NOT_FOUND;
}
if (rc >= 0)
call->header.flags |= VMMDEV_HGCM_REQ_CANCELLED;
return rc;
}
/**
* vbg_hgcm_do_call - Performs the call and completion wait.
* @gdev: The VBoxGuest device extension.
* @call: The call to execute.
* @timeout_ms: Timeout in ms.
* @interruptible: whether this call is interruptible
* @leak_it: Where to return the leak it / free it, indicator.
* Cancellation fun.
*
* Return: %0 or negative errno value.
*/
static int vbg_hgcm_do_call(struct vbg_dev *gdev, struct vmmdev_hgcm_call *call,
u32 timeout_ms, bool interruptible, bool *leak_it)
{
int rc, cancel_rc, ret;
long timeout;
*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) {
call->header.result = rc;
return 0;
}
if (rc != VINF_HGCM_ASYNC_EXECUTE)
return 0;
/* 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 (interruptible) {
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 0;
if (timeout == 0)
ret = -ETIMEDOUT;
else
ret = -EINTR;
/* Cancel the request */
cancel_rc = hgcm_cancel_call(gdev, call);
if (cancel_rc >= 0)
return ret;
/*
* 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 ret;
}
/* The call has completed normally after all */
return 0;
}
/**
* hgcm_call_copy_back_result - Copies the result of the call back to
* the caller info structure and user buffers.
* @call: HGCM call request.
* @dst_parm: Pointer to function call parameters destination.
* @parm_count: Number of function call parameters.
* @bounce_bufs: The bouncebuffer array.
*
* Return: %0 or negative errno value.
*/
static int hgcm_call_copy_back_result(
const struct vmmdev_hgcm_call *call,
struct vmmdev_hgcm_function_parameter *dst_parm,
u32 parm_count, void **bounce_bufs)
{
const struct vmmdev_hgcm_function_parameter *src_parm =
VMMDEV_HGCM_CALL_PARMS(call);
void __user *p;
int ret;
u32 i;
/* Copy back parameters. */
for (i = 0; i < parm_count; i++, src_parm++, dst_parm++) {
switch (dst_parm->type) {
case VMMDEV_HGCM_PARM_TYPE_32BIT:
case VMMDEV_HGCM_PARM_TYPE_64BIT:
*dst_parm = *src_parm;
break;
case VMMDEV_HGCM_PARM_TYPE_PAGELIST:
dst_parm->u.page_list.size = src_parm->u.page_list.size;
break;
case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN:
case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL:
case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_IN:
case VMMDEV_HGCM_PARM_TYPE_LINADDR_KERNEL_OUT:
dst_parm->u.pointer.size = src_parm->u.pointer.size;
break;
case VMMDEV_HGCM_PARM_TYPE_LINADDR:
case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT:
dst_parm->u.pointer.size = src_parm->u.pointer.size;
p = (void __user *)dst_parm->u.pointer.u.linear_addr;
ret = copy_to_user(p, bounce_bufs[i],
min(src_parm->u.pointer.size,
dst_parm->u.pointer.size));
if (ret)
return -EFAULT;
break;
default:
WARN_ON(1);
return -EINVAL;
}
}
return 0;
}
int vbg_hgcm_call(struct vbg_dev *gdev, u32 requestor, u32 client_id,
u32 function, u32 timeout_ms,
struct vmmdev_hgcm_function_parameter *parms, u32 parm_count,
int *vbox_status)
{
struct vmmdev_hgcm_call *call;
void **bounce_bufs = NULL;
bool leak_it;
size_t size;
int i, ret;
size = sizeof(struct vmmdev_hgcm_call) +
parm_count * sizeof(struct vmmdev_hgcm_function_parameter);
/*
* Validate and buffer the parameters for the call. This also increases
* call_size with the amount of extra space needed for page lists.
*/
ret = hgcm_call_preprocess(parms, parm_count, &bounce_bufs, &size);
if (ret) {
/* Even on error bounce bufs may still have been allocated */
goto free_bounce_bufs;
}
call = vbg_req_alloc(size, VMMDEVREQ_HGCM_CALL, requestor);
if (!call) {
ret = -ENOMEM;
goto free_bounce_bufs;
}
hgcm_call_init_call(call, client_id, function, parms, parm_count,
bounce_bufs);
ret = vbg_hgcm_do_call(gdev, call, timeout_ms,
requestor & VMMDEV_REQUESTOR_USERMODE, &leak_it);
if (ret == 0) {
*vbox_status = call->header.result;
ret = hgcm_call_copy_back_result(call, parms, parm_count,
bounce_bufs);
}
if (!leak_it)
vbg_req_free(call, size);
free_bounce_bufs:
if (bounce_bufs) {
for (i = 0; i < parm_count; i++)
kvfree(bounce_bufs[i]);
kfree(bounce_bufs);
}
return ret;
}
EXPORT_SYMBOL(vbg_hgcm_call);
#ifdef CONFIG_COMPAT
int vbg_hgcm_call32(
struct vbg_dev *gdev, u32 requestor, u32 client_id, u32 function,
u32 timeout_ms, struct vmmdev_hgcm_function_parameter32 *parm32,
u32 parm_count, int *vbox_status)
{
struct vmmdev_hgcm_function_parameter *parm64 = NULL;
u32 i, size;
int ret = 0;
/* KISS allocate a temporary request and convert the parameters. */
size = parm_count * sizeof(struct vmmdev_hgcm_function_parameter);
parm64 = kzalloc(size, GFP_KERNEL);
if (!parm64)
return -ENOMEM;
for (i = 0; i < parm_count; i++) {
switch (parm32[i].type) {
case VMMDEV_HGCM_PARM_TYPE_32BIT:
parm64[i].type = VMMDEV_HGCM_PARM_TYPE_32BIT;
parm64[i].u.value32 = parm32[i].u.value32;
break;
case VMMDEV_HGCM_PARM_TYPE_64BIT:
parm64[i].type = VMMDEV_HGCM_PARM_TYPE_64BIT;
parm64[i].u.value64 = parm32[i].u.value64;
break;
case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT:
case VMMDEV_HGCM_PARM_TYPE_LINADDR:
case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN:
parm64[i].type = parm32[i].type;
parm64[i].u.pointer.size = parm32[i].u.pointer.size;
parm64[i].u.pointer.u.linear_addr =
parm32[i].u.pointer.u.linear_addr;
break;
default:
ret = -EINVAL;
}
if (ret < 0)
goto out_free;
}
ret = vbg_hgcm_call(gdev, requestor, client_id, function, timeout_ms,
parm64, parm_count, vbox_status);
if (ret < 0)
goto out_free;
/* Copy back. */
for (i = 0; i < parm_count; i++, parm32++, parm64++) {
switch (parm64[i].type) {
case VMMDEV_HGCM_PARM_TYPE_32BIT:
parm32[i].u.value32 = parm64[i].u.value32;
break;
case VMMDEV_HGCM_PARM_TYPE_64BIT:
parm32[i].u.value64 = parm64[i].u.value64;
break;
case VMMDEV_HGCM_PARM_TYPE_LINADDR_OUT:
case VMMDEV_HGCM_PARM_TYPE_LINADDR:
case VMMDEV_HGCM_PARM_TYPE_LINADDR_IN:
parm32[i].u.pointer.size = parm64[i].u.pointer.size;
break;
default:
WARN_ON(1);
ret = -EINVAL;
}
}
out_free:
kfree(parm64);
return ret;
}
#endif
static const int vbg_status_code_to_errno_table[] = {
[-VERR_ACCESS_DENIED] = -EPERM,
[-VERR_FILE_NOT_FOUND] = -ENOENT,
[-VERR_PROCESS_NOT_FOUND] = -ESRCH,
[-VERR_INTERRUPTED] = -EINTR,
[-VERR_DEV_IO_ERROR] = -EIO,
[-VERR_TOO_MUCH_DATA] = -E2BIG,
[-VERR_BAD_EXE_FORMAT] = -ENOEXEC,
[-VERR_INVALID_HANDLE] = -EBADF,
[-VERR_TRY_AGAIN] = -EAGAIN,
[-VERR_NO_MEMORY] = -ENOMEM,
[-VERR_INVALID_POINTER] = -EFAULT,
[-VERR_RESOURCE_BUSY] = -EBUSY,
[-VERR_ALREADY_EXISTS] = -EEXIST,
[-VERR_NOT_SAME_DEVICE] = -EXDEV,
[-VERR_NOT_A_DIRECTORY] = -ENOTDIR,
[-VERR_PATH_NOT_FOUND] = -ENOTDIR,
[-VERR_INVALID_NAME] = -ENOENT,
[-VERR_IS_A_DIRECTORY] = -EISDIR,
[-VERR_INVALID_PARAMETER] = -EINVAL,
[-VERR_TOO_MANY_OPEN_FILES] = -ENFILE,
[-VERR_INVALID_FUNCTION] = -ENOTTY,
[-VERR_SHARING_VIOLATION] = -ETXTBSY,
[-VERR_FILE_TOO_BIG] = -EFBIG,
[-VERR_DISK_FULL] = -ENOSPC,
[-VERR_SEEK_ON_DEVICE] = -ESPIPE,
[-VERR_WRITE_PROTECT] = -EROFS,
[-VERR_BROKEN_PIPE] = -EPIPE,
[-VERR_DEADLOCK] = -EDEADLK,
[-VERR_FILENAME_TOO_LONG] = -ENAMETOOLONG,
[-VERR_FILE_LOCK_FAILED] = -ENOLCK,
[-VERR_NOT_IMPLEMENTED] = -ENOSYS,
[-VERR_NOT_SUPPORTED] = -ENOSYS,
[-VERR_DIR_NOT_EMPTY] = -ENOTEMPTY,
[-VERR_TOO_MANY_SYMLINKS] = -ELOOP,
[-VERR_NO_MORE_FILES] = -ENODATA,
[-VERR_NO_DATA] = -ENODATA,
[-VERR_NET_NO_NETWORK] = -ENONET,
[-VERR_NET_NOT_UNIQUE_NAME] = -ENOTUNIQ,
[-VERR_NO_TRANSLATION] = -EILSEQ,
[-VERR_NET_NOT_SOCKET] = -ENOTSOCK,
[-VERR_NET_DEST_ADDRESS_REQUIRED] = -EDESTADDRREQ,
[-VERR_NET_MSG_SIZE] = -EMSGSIZE,
[-VERR_NET_PROTOCOL_TYPE] = -EPROTOTYPE,
[-VERR_NET_PROTOCOL_NOT_AVAILABLE] = -ENOPROTOOPT,
[-VERR_NET_PROTOCOL_NOT_SUPPORTED] = -EPROTONOSUPPORT,
[-VERR_NET_SOCKET_TYPE_NOT_SUPPORTED] = -ESOCKTNOSUPPORT,
[-VERR_NET_OPERATION_NOT_SUPPORTED] = -EOPNOTSUPP,
[-VERR_NET_PROTOCOL_FAMILY_NOT_SUPPORTED] = -EPFNOSUPPORT,
[-VERR_NET_ADDRESS_FAMILY_NOT_SUPPORTED] = -EAFNOSUPPORT,
[-VERR_NET_ADDRESS_IN_USE] = -EADDRINUSE,
[-VERR_NET_ADDRESS_NOT_AVAILABLE] = -EADDRNOTAVAIL,
[-VERR_NET_DOWN] = -ENETDOWN,
[-VERR_NET_UNREACHABLE] = -ENETUNREACH,
[-VERR_NET_CONNECTION_RESET] = -ENETRESET,
[-VERR_NET_CONNECTION_ABORTED] = -ECONNABORTED,
[-VERR_NET_CONNECTION_RESET_BY_PEER] = -ECONNRESET,
[-VERR_NET_NO_BUFFER_SPACE] = -ENOBUFS,
[-VERR_NET_ALREADY_CONNECTED] = -EISCONN,
[-VERR_NET_NOT_CONNECTED] = -ENOTCONN,
[-VERR_NET_SHUTDOWN] = -ESHUTDOWN,
[-VERR_NET_TOO_MANY_REFERENCES] = -ETOOMANYREFS,
[-VERR_TIMEOUT] = -ETIMEDOUT,
[-VERR_NET_CONNECTION_REFUSED] = -ECONNREFUSED,
[-VERR_NET_HOST_DOWN] = -EHOSTDOWN,
[-VERR_NET_HOST_UNREACHABLE] = -EHOSTUNREACH,
[-VERR_NET_ALREADY_IN_PROGRESS] = -EALREADY,
[-VERR_NET_IN_PROGRESS] = -EINPROGRESS,
[-VERR_MEDIA_NOT_PRESENT] = -ENOMEDIUM,
[-VERR_MEDIA_NOT_RECOGNIZED] = -EMEDIUMTYPE,
};
int vbg_status_code_to_errno(int rc)
{
if (rc >= 0)
return 0;
rc = -rc;
if (rc >= ARRAY_SIZE(vbg_status_code_to_errno_table) ||
vbg_status_code_to_errno_table[rc] == 0) {
vbg_warn("%s: Unhandled err %d\n", __func__, -rc);
return -EPROTO;
}
return vbg_status_code_to_errno_table[rc];
}
EXPORT_SYMBOL(vbg_status_code_to_errno);
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: (GPL-2.0 OR CDDL-1.0) */
/*
* 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__
#define VBG_VERSION_MAJOR 6
#define VBG_VERSION_MINOR 0
#define VBG_VERSION_BUILD 0
#define VBG_SVN_REV 127566
#define VBG_VERSION_STRING "6.0.0"
#endif
+453
View File
@@ -0,0 +1,453 @@
/* SPDX-License-Identifier: (GPL-2.0 OR CDDL-1.0) */
/*
* Virtual Device for Guest <-> VMM/Host communication interface
*
* Copyright (C) 2006-2016 Oracle Corporation
*/
#ifndef __VBOX_VMMDEV_H__
#define __VBOX_VMMDEV_H__
#include <asm/bitsperlong.h>
#include <linux/sizes.h>
#include <linux/types.h>
#include <linux/vbox_vmmdev_types.h>
/* Port for generic request interface (relative offset). */
#define VMMDEV_PORT_OFF_REQUEST 0
/** Layout of VMMDEV RAM region that contains information for guest. */
struct vmmdev_memory {
/** The size of this structure. */
u32 size;
/** The structure version. (VMMDEV_MEMORY_VERSION) */
u32 version;
union {
struct {
/** Flag telling that VMMDev has events pending. */
u8 have_events;
/** Explicit padding, MBZ. */
u8 padding[3];
} V1_04;
struct {
/** Pending events flags, set by host. */
u32 host_events;
/** Mask of events the guest wants, set by guest. */
u32 guest_event_mask;
} V1_03;
} V;
/* struct vbva_memory, not used */
};
VMMDEV_ASSERT_SIZE(vmmdev_memory, 8 + 8);
/** Version of vmmdev_memory structure (vmmdev_memory::version). */
#define VMMDEV_MEMORY_VERSION (1)
/* 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
/*
* Additions are allowed to work only if additions_major == vmmdev_current &&
* additions_minor <= vmmdev_current. Additions version is reported to host
* (VMMDev) by VMMDEVREQ_REPORT_GUEST_INFO.
*/
#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
/* Version of vmmdev_request_header structure. */
#define VMMDEV_REQUEST_HEADER_VERSION 0x10001
/** struct vmmdev_request_header - Generic VMMDev request header. */
struct vmmdev_request_header {
/** IN: Size of the structure in bytes (including body). */
u32 size;
/** IN: Version of the structure. */
u32 version;
/** IN: Type of the request. */
enum vmmdev_request_type request_type;
/** OUT: Return code. */
s32 rc;
/** Reserved field no.1. MBZ. */
u32 reserved1;
/** IN: Requestor information (VMMDEV_REQUESTOR_*) */
u32 requestor;
};
VMMDEV_ASSERT_SIZE(vmmdev_request_header, 24);
/**
* struct vmmdev_mouse_status - Mouse status request structure.
*
* Used by VMMDEVREQ_GET_MOUSE_STATUS and VMMDEVREQ_SET_MOUSE_STATUS.
*/
struct vmmdev_mouse_status {
/** header */
struct vmmdev_request_header header;
/** Mouse feature mask. See VMMDEV_MOUSE_*. */
u32 mouse_features;
/** Mouse x position. */
s32 pointer_pos_x;
/** Mouse y position. */
s32 pointer_pos_y;
};
VMMDEV_ASSERT_SIZE(vmmdev_mouse_status, 24 + 12);
/* 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 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
/**
* struct vmmdev_host_version - VirtualBox host version request structure.
*
* VBG uses this to detect the precense of new features in the interface.
*/
struct vmmdev_host_version {
/** Header. */
struct vmmdev_request_header header;
/** Major version. */
u16 major;
/** Minor version. */
u16 minor;
/** Build number. */
u32 build;
/** SVN revision. */
u32 revision;
/** Feature mask. */
u32 features;
};
VMMDEV_ASSERT_SIZE(vmmdev_host_version, 24 + 16);
/* Physical page lists are supported by HGCM. */
#define VMMDEV_HVF_HGCM_PHYS_PAGE_LIST BIT(0)
/**
* struct vmmdev_mask - Structure to set / clear bits in a mask used for
* VMMDEVREQ_SET_GUEST_CAPABILITIES and VMMDEVREQ_CTL_GUEST_FILTER_MASK.
*/
struct vmmdev_mask {
/** Header. */
struct vmmdev_request_header header;
/** Mask of bits to be set. */
u32 or_mask;
/** Mask of bits to be cleared. */
u32 not_mask;
};
VMMDEV_ASSERT_SIZE(vmmdev_mask, 24 + 8);
/* 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_REPORT_GUEST_CAPABILITIES
* request sets this automatically, but VMMDEVREQ_SET_GUEST_CAPABILITIES does
* not.
*/
#define VMMDEV_GUEST_SUPPORTS_GRAPHICS BIT(2)
/* The mask of valid capabilities, for sanity checking. */
#define VMMDEV_GUEST_CAPABILITIES_MASK 0x00000007U
/** struct vmmdev_hypervisorinfo - Hypervisor info structure. */
struct vmmdev_hypervisorinfo {
/** Header. */
struct vmmdev_request_header header;
/**
* Guest virtual address of proposed hypervisor start.
* Not used by VMMDEVREQ_GET_HYPERVISOR_INFO.
*/
u32 hypervisor_start;
/** Hypervisor size in bytes. */
u32 hypervisor_size;
};
VMMDEV_ASSERT_SIZE(vmmdev_hypervisorinfo, 24 + 8);
/** struct vmmdev_events - Pending events structure. */
struct vmmdev_events {
/** Header. */
struct vmmdev_request_header header;
/** OUT: Pending event mask. */
u32 events;
};
VMMDEV_ASSERT_SIZE(vmmdev_events, 24 + 4);
#define VMMDEV_OSTYPE_LINUX26 0x53000
#define VMMDEV_OSTYPE_X64 BIT(8)
/** struct vmmdev_guestinfo - Guest information report. */
struct vmmdev_guest_info {
/** Header. */
struct vmmdev_request_header header;
/**
* The VMMDev interface version expected by additions.
* *Deprecated*, do not use anymore! Will be removed.
*/
u32 interface_version;
/** Guest OS type. */
u32 os_type;
};
VMMDEV_ASSERT_SIZE(vmmdev_guest_info, 24 + 8);
#define VMMDEV_GUEST_INFO2_ADDITIONS_FEATURES_REQUESTOR_INFO BIT(0)
/** struct vmmdev_guestinfo2 - Guest information report, version 2. */
struct vmmdev_guest_info2 {
/** Header. */
struct vmmdev_request_header header;
/** Major version. */
u16 additions_major;
/** Minor version. */
u16 additions_minor;
/** Build number. */
u32 additions_build;
/** SVN revision. */
u32 additions_revision;
/** Feature mask. */
u32 additions_features;
/**
* 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: VBG_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 name[128];
};
VMMDEV_ASSERT_SIZE(vmmdev_guest_info2, 24 + 144);
enum vmmdev_guest_facility_type {
VBOXGUEST_FACILITY_TYPE_UNKNOWN = 0,
VBOXGUEST_FACILITY_TYPE_VBOXGUEST_DRIVER = 20,
/* VBoxGINA / VBoxCredProv / pam_vbox. */
VBOXGUEST_FACILITY_TYPE_AUTO_LOGON = 90,
VBOXGUEST_FACILITY_TYPE_VBOX_SERVICE = 100,
/* VBoxTray (Windows), VBoxClient (Linux, Unix). */
VBOXGUEST_FACILITY_TYPE_VBOX_TRAY_CLIENT = 101,
VBOXGUEST_FACILITY_TYPE_SEAMLESS = 1000,
VBOXGUEST_FACILITY_TYPE_GRAPHICS = 1100,
VBOXGUEST_FACILITY_TYPE_ALL = 0x7ffffffe,
/* Ensure the enum is a 32 bit data-type */
VBOXGUEST_FACILITY_TYPE_SIZEHACK = 0x7fffffff
};
enum vmmdev_guest_facility_status {
VBOXGUEST_FACILITY_STATUS_INACTIVE = 0,
VBOXGUEST_FACILITY_STATUS_PAUSED = 1,
VBOXGUEST_FACILITY_STATUS_PRE_INIT = 20,
VBOXGUEST_FACILITY_STATUS_INIT = 30,
VBOXGUEST_FACILITY_STATUS_ACTIVE = 50,
VBOXGUEST_FACILITY_STATUS_TERMINATING = 100,
VBOXGUEST_FACILITY_STATUS_TERMINATED = 101,
VBOXGUEST_FACILITY_STATUS_FAILED = 800,
VBOXGUEST_FACILITY_STATUS_UNKNOWN = 999,
/* Ensure the enum is a 32 bit data-type */
VBOXGUEST_FACILITY_STATUS_SIZEHACK = 0x7fffffff
};
/** struct vmmdev_guest_status - Guest Additions status structure. */
struct vmmdev_guest_status {
/** Header. */
struct vmmdev_request_header header;
/** Facility the status is indicated for. */
enum vmmdev_guest_facility_type facility;
/** Current guest status. */
enum vmmdev_guest_facility_status status;
/** Flags, not used at the moment. */
u32 flags;
};
VMMDEV_ASSERT_SIZE(vmmdev_guest_status, 24 + 12);
#define VMMDEV_MEMORY_BALLOON_CHUNK_SIZE (1048576)
#define VMMDEV_MEMORY_BALLOON_CHUNK_PAGES (1048576 / 4096)
/** struct vmmdev_memballoon_info - Memory-balloon info structure. */
struct vmmdev_memballoon_info {
/** Header. */
struct vmmdev_request_header header;
/** Balloon size in megabytes. */
u32 balloon_chunks;
/** Guest ram size in megabytes. */
u32 phys_mem_chunks;
/**
* Setting this to VMMDEV_EVENT_BALLOON_CHANGE_REQUEST indicates that
* the request is a response to that event.
* (Don't confuse this with VMMDEVREQ_ACKNOWLEDGE_EVENTS.)
*/
u32 event_ack;
};
VMMDEV_ASSERT_SIZE(vmmdev_memballoon_info, 24 + 12);
/** struct vmmdev_memballoon_change - Change the size of the balloon. */
struct vmmdev_memballoon_change {
/** Header. */
struct vmmdev_request_header 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];
};
/** struct vmmdev_write_core_dump - Write Core Dump request data. */
struct vmmdev_write_core_dump {
/** Header. */
struct vmmdev_request_header header;
/** Flags (reserved, MBZ). */
u32 flags;
};
VMMDEV_ASSERT_SIZE(vmmdev_write_core_dump, 24 + 4);
/** struct vmmdev_heartbeat - Heart beat check state structure. */
struct vmmdev_heartbeat {
/** Header. */
struct vmmdev_request_header header;
/** OUT: Guest heartbeat interval in nanosec. */
u64 interval_ns;
/** Heartbeat check flag. */
u8 enabled;
/** Explicit padding, MBZ. */
u8 padding[3];
} __packed;
VMMDEV_ASSERT_SIZE(vmmdev_heartbeat, 24 + 12);
#define VMMDEV_HGCM_REQ_DONE BIT(0)
#define VMMDEV_HGCM_REQ_CANCELLED BIT(1)
/** struct vmmdev_hgcmreq_header - vmmdev HGCM requests header. */
struct vmmdev_hgcmreq_header {
/** Request header. */
struct vmmdev_request_header header;
/** HGCM flags. */
u32 flags;
/** Result code. */
s32 result;
};
VMMDEV_ASSERT_SIZE(vmmdev_hgcmreq_header, 24 + 8);
/** struct vmmdev_hgcm_connect - HGCM connect request structure. */
struct vmmdev_hgcm_connect {
/** HGCM request header. */
struct vmmdev_hgcmreq_header header;
/** IN: Description of service to connect to. */
struct vmmdev_hgcm_service_location loc;
/** OUT: Client identifier assigned by local instance of HGCM. */
u32 client_id;
};
VMMDEV_ASSERT_SIZE(vmmdev_hgcm_connect, 32 + 132 + 4);
/** struct vmmdev_hgcm_disconnect - HGCM disconnect request structure. */
struct vmmdev_hgcm_disconnect {
/** HGCM request header. */
struct vmmdev_hgcmreq_header header;
/** IN: Client identifier. */
u32 client_id;
};
VMMDEV_ASSERT_SIZE(vmmdev_hgcm_disconnect, 32 + 4);
#define VMMDEV_HGCM_MAX_PARMS 32
/** struct vmmdev_hgcm_call - HGCM call request structure. */
struct vmmdev_hgcm_call {
/* request header */
struct vmmdev_hgcmreq_header header;
/** IN: Client identifier. */
u32 client_id;
/** IN: Service function number. */
u32 function;
/** IN: Number of parameters. */
u32 parm_count;
/** Parameters follow in form: HGCMFunctionParameter32|64 parms[X]; */
};
VMMDEV_ASSERT_SIZE(vmmdev_hgcm_call, 32 + 12);
/**
* struct vmmdev_hgcm_cancel2 - HGCM cancel request structure, version 2.
*
* After the request header.rc will be:
*
* VINF_SUCCESS when cancelled.
* VERR_NOT_FOUND if the specified request cannot be found.
* VERR_INVALID_PARAMETER if the address is invalid valid.
*/
struct vmmdev_hgcm_cancel2 {
/** Header. */
struct vmmdev_request_header header;
/** The physical address of the request to cancel. */
u32 phys_req_to_cancel;
};
VMMDEV_ASSERT_SIZE(vmmdev_hgcm_cancel2, 24 + 4);
#endif