guestrpc module to handle VMware backdoor port GuestRPC functionality

Convert existing FreeBSD vmware_hvcall function to take a channel
and parameter arguments.

Added vmware_guestrpc_cmd() to send GuestRPC commands to the VMware
hypervisor. The sbuf argument is used for both the command to send
and to store the data to return to the caller.

The following KPIs can be used to get and set FreeBSD-specific guest
information in key/value pairs:
 * vmware_guestrpc_set_guestinfo
   - set a value into the guestinfo.fbsd.<keyword> key
 * vmware_guestrpc_get_guestinfo
   - get the value stored in the guestinfo.fbsd.<keyword> key

Add VMware devices to x86 NOTES

Reviewed by:	jhb
Obtained from:	Juniper Networks, Inc.
Differential Revision:	https://reviews.freebsd.org/D44528
This commit is contained in:
Stephen J. Kiernan 2024-05-01 15:45:45 -04:00
parent f4b08097d8
commit ecaab0fb5d
8 changed files with 391 additions and 5 deletions

View File

@ -380,6 +380,7 @@ x86/x86/stack_machdep.c optional ddb | stack
x86/x86/tsc.c standard
x86/x86/ucode.c standard
x86/x86/ucode_subr.c standard
x86/x86/vmware_guestrpc.c optional vmware_guestrpc
x86/x86/delay.c standard
x86/xen/hvm.c optional xenhvm
x86/xen/xen_apic.c optional xenhvm smp

View File

@ -159,7 +159,8 @@ madt_x2apic_disable_reason(void)
}
if (vm_guest == VM_GUEST_VMWARE) {
vmware_hvcall(VMW_HVCMD_GETVCPU_INFO, p);
vmware_hvcall(0, VMW_HVCMD_GETVCPU_INFO,
VMW_HVCMD_DEFAULT_PARAM, p);
if ((p[0] & VMW_VCPUINFO_VCPU_RESERVED) != 0 ||
(p[0] & VMW_VCPUINFO_LEGACY_X2APIC) == 0)
return ("inside VMWare without intr redirection");

View File

@ -550,6 +550,11 @@ device kvm_clock # KVM paravirtual clock driver
device hyperv # HyperV drivers
device hvhid # HyperV HID device
# VMware hypervisor support
device pvscsi # Paravirtual SCSI driver
device vmci # Virtual Machine Communication Interface (VMCI)
device vmware_guestrpc # GuestRPC interface
#
# Laptop/Notebook options:
#

View File

@ -31,19 +31,23 @@
#define VMW_HVPORT 0x5658
#define VMW_HVCMD_GETVERSION 10
#define VMW_HVCMD_GUESTRPC 30
#define VMW_HVCMD_GETHZ 45
#define VMW_HVCMD_GETVCPU_INFO 68
#define VMW_HVCMD_DEFAULT_PARAM UINT_MAX
#define VMW_VCPUINFO_LEGACY_X2APIC (1 << 3)
#define VMW_VCPUINFO_VCPU_RESERVED (1 << 31)
static __inline void
vmware_hvcall(u_int cmd, u_int *p)
vmware_hvcall(int chan, u_int cmd, u_int param, u_int *p)
{
__asm __volatile("inl %w3, %0"
: "=a" (p[0]), "=b" (p[1]), "=c" (p[2]), "=d" (p[3])
: "0" (VMW_HVMAGIC), "1" (UINT_MAX), "2" (cmd), "3" (VMW_HVPORT)
: "0" (VMW_HVMAGIC), "1" (param), "2" (cmd),
"3" (VMW_HVPORT | (chan << 16))
: "memory");
}

View File

@ -0,0 +1,37 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2015-2024, Juniper Networks, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef _X86_VMWARE_GUESTRPC_H_
#define _X86_VMWARE_GUESTRPC_H_
struct sbuf;
int vmware_guestrpc_cmd(struct sbuf *sbufp);
int vmware_guestrpc_set_guestinfo(const char *keyword, const char *val);
int vmware_guestrpc_get_guestinfo(const char *keyword, struct sbuf *sbufp);
#endif /* _X86_VMWARE_GUESTRPC_H_ */

View File

@ -1470,7 +1470,8 @@ identify_hypervisor(void)
p = kern_getenv("smbios.system.serial");
if (p != NULL) {
if (strncmp(p, "VMware-", 7) == 0 || strncmp(p, "VMW", 3) == 0) {
vmware_hvcall(VMW_HVCMD_GETVERSION, regs);
vmware_hvcall(0, VMW_HVCMD_GETVERSION,
VMW_HVCMD_DEFAULT_PARAM, regs);
if (regs[1] == VMW_HVMAGIC) {
vm_guest = VM_GUEST_VMWARE;
freeenv(p);

View File

@ -139,7 +139,7 @@ tsc_freq_vmware(void)
{
u_int regs[4];
vmware_hvcall(VMW_HVCMD_GETHZ, regs);
vmware_hvcall(0, VMW_HVCMD_GETHZ, VMW_HVCMD_DEFAULT_PARAM, regs);
if (regs[1] != UINT_MAX)
tsc_freq = regs[0] | ((uint64_t)regs[1] << 32);
tsc_early_calib_exact = 1;

View File

@ -0,0 +1,337 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2013-2024, Juniper Networks, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/limits.h>
#include <sys/bus.h>
#include <sys/sbuf.h>
#include <sys/errno.h>
#include <sys/module.h>
#include <x86/vmware.h>
#include <x86/vmware_guestrpc.h>
/* GuestRPC Subcommands */
#define VMW_HVGUESTRPC_OPEN 0x00
#define VMW_HVGUESTRPC_SEND_LEN 0x01
#define VMW_HVGUESTRPC_SEND_DATA 0x02
#define VMW_HVGUESTRPC_RECV_LEN 0x03
#define VMW_HVGUESTRPC_RECV_DATA 0x04
#define VMW_HVGUESTRPC_FINISH_RECV 0x05
#define VMW_HVGUESTRPC_CLOSE 0x06
/* GuestRPC Parameters */
#define VMW_HVGUESTRPC_OPEN_MAGIC 0x49435052
/* GuestRPC Status */
#define VMW_HVGUESTRPC_FAILURE 0x00000000
#define VMW_HVGUESTRPC_OPEN_SUCCESS 0x00010000
#define VMW_HVGUESTRPC_SEND_LEN_SUCCESS 0x00810000
#define VMW_HVGUESTRPC_SEND_DATA_SUCCESS 0x00010000
#define VMW_HVGUESTRPC_RECV_LEN_SUCCESS 0x00830000
#define VMW_HVGUESTRPC_RECV_DATA_SUCCESS 0x00010000
#define VMW_HVGUESTRPC_FINISH_RECV_SUCCESS 0x00010000
#define VMW_HVGUESTRPC_CLOSE_SUCCESS 0x00010000
#define VMW_GUESTRPC_EBX(_p) ((_p)[1])
#define VMW_GUESTRPC_EDXHI(_p) ((_p)[3] >> 16)
#define VMW_GUESTRPC_STATUS(_p) ((_p)[2])
static __inline void
vmware_guestrpc(int chan, uint16_t subcmd, uint32_t param, u_int *p)
{
#ifdef DEBUG_VMGUESTRPC
printf("%s(%d, %#x, %#x, %p)\n", __func__, chan, subcmd, param, p);
#endif
vmware_hvcall(chan, VMW_HVCMD_GUESTRPC | (subcmd << 16), param, p);
#ifdef DEBUG_VMGUESTRPC
printf("p[0] = %#x\n", p[0]);
printf("p[1] = %#x\n", p[1]);
printf("p[2] = %#x\n", p[2]);
printf("p[3] = %#x\n", p[3]);
#endif
}
/*
* Start a GuestRPC request
*
* Channel number is returned in the EDXHI parameter.
*
* This channel number must be used in successive GuestRPC requests for
* sending and receiving RPC data.
*/
static int
vmware_guestrpc_open(void)
{
u_int p[4];
vmware_guestrpc(0, VMW_HVGUESTRPC_OPEN, VMW_HVGUESTRPC_OPEN_MAGIC,
p);
if (VMW_GUESTRPC_STATUS(p) != VMW_HVGUESTRPC_OPEN_SUCCESS)
return (-1);
return (VMW_GUESTRPC_EDXHI(p));
}
/*
* Send the length of the GuestRPC request
*
* In a GuestRPC request, the total length of the request must be sent
* before any data can be sent.
*/
static int
vmware_guestrpc_send_len(int channel, size_t len)
{
u_int p[4];
vmware_guestrpc(channel, VMW_HVGUESTRPC_SEND_LEN, len, p);
if (VMW_GUESTRPC_STATUS(p) != VMW_HVGUESTRPC_SEND_LEN_SUCCESS)
return (-1);
return (0);
}
/*
* Send the data for the GuestRPC request
*
* The total length of the GuestRPC request must be sent before any data.
* Data is sent 32-bit values at a time and therefore may require multiple
* calls to send all the data.
*/
static int
vmware_guestrpc_send_data(int channel, uint32_t data)
{
u_int p[4];
vmware_guestrpc(channel, VMW_HVGUESTRPC_SEND_DATA, data, p);
if (VMW_GUESTRPC_STATUS(p) != VMW_HVGUESTRPC_SEND_DATA_SUCCESS)
return (-1);
return (0);
}
/*
* Receive the length of the GuestRPC reply.
*
* Length of the reply data is returned in the EBX parameter.
* The reply identifier is returned in the EDXHI parameter.
*
* The reply identifier must be used as the GuestRPC parameter in calls
* to vmware_guestrpc_recv_data()
*/
static int
vmware_guestrpc_recv_len(int channel, size_t *lenp)
{
u_int p[4];
vmware_guestrpc(channel, VMW_HVGUESTRPC_RECV_LEN, 0, p);
if (VMW_GUESTRPC_STATUS(p) != VMW_HVGUESTRPC_RECV_LEN_SUCCESS)
return (-1);
*lenp = VMW_GUESTRPC_EBX(p);
return (VMW_GUESTRPC_EDXHI(p));
}
/*
* Receive the GuestRPC reply data.
*
* Data is received in 32-bit values at a time and therefore may
* require multiple requests to get all the data.
*/
static int
vmware_guestrpc_recv_data(int channel, int id, uint32_t *datap)
{
u_int p[4];
vmware_guestrpc(channel, VMW_HVGUESTRPC_RECV_DATA, id, p);
if (VMW_GUESTRPC_STATUS(p) != VMW_HVGUESTRPC_RECV_DATA_SUCCESS)
return (-1);
*datap = VMW_GUESTRPC_EBX(p);
return (0);
}
/*
* Close the GuestRPC channel.
*/
static int
vmware_guestrpc_close(int channel)
{
u_int p[4];
vmware_guestrpc(channel, VMW_HVGUESTRPC_CLOSE, 0, p);
if (VMW_GUESTRPC_STATUS(p) != VMW_HVGUESTRPC_CLOSE_SUCCESS)
return (-1);
return (0);
}
/*
* Send a GuestRPC command.
*/
int
vmware_guestrpc_cmd(struct sbuf *sbufp)
{
char *buf;
size_t cnt, len;
int chan, id, status;
uint32_t data;
/* Make sure we are running under VMware hypervisor */
if (vm_guest != VM_GUEST_VMWARE)
return (ENXIO);
/* Open the GuestRPC channel */
chan = vmware_guestrpc_open();
if (chan == -1)
return (EIO);
/* Send the length */
buf = sbuf_data(sbufp);
len = sbuf_len(sbufp);
status = vmware_guestrpc_send_len(chan, len);
if (status == -1)
goto done;
/* Send the data */
while (len > 0) {
data = 0;
cnt = min(4, len);
memcpy(&data, buf, cnt);
status = vmware_guestrpc_send_data(chan, data);
if (status == -1)
goto done;
buf += cnt;
len -= cnt;
}
/* Receive the length of the reply data */
id = vmware_guestrpc_recv_len(chan, &len);
if (id == -1)
goto done;
/* Receive the reply data */
sbuf_clear(sbufp);
while (len > 0) {
status = vmware_guestrpc_recv_data(chan, id, &data);
if (status == -1)
goto done;
sbuf_bcat(sbufp, &data, 4);
len -= min(4, len);
}
done:
/* Close the GuestRPC channel */
vmware_guestrpc_close(chan);
return (status == -1 ? EIO : 0);
}
/*
* Set guest information key/value pair
*/
int
vmware_guestrpc_set_guestinfo(const char *keyword, const char *val)
{
struct sbuf sb;
char *buf;
int error;
#ifdef DEBUG_VMGUESTRPC
printf("%s: %s=%s\n", __func__, keyword, val);
#endif
/* Send "info-set" GuestRPC command */
sbuf_new(&sb, NULL, 256, SBUF_AUTOEXTEND);
sbuf_printf(&sb, "info-set guestinfo.fbsd.%s %s", keyword, val);
sbuf_trim(&sb);
sbuf_finish(&sb);
error = vmware_guestrpc_cmd(&sb);
if (error)
return (error);
sbuf_finish(&sb);
buf = sbuf_data(&sb);
#ifdef DEBUG_VMGUESTRPC
printf("%s: result: %s\n", __func__, buf);
#endif
/* Buffer will contain 1 on sucess or 0 on failure */
return ((buf[0] == '0') ? EINVAL : 0);
}
/*
* Get guest information key/value pair.
*/
int
vmware_guestrpc_get_guestinfo(const char *keyword, struct sbuf *sbufp)
{
struct sbuf sb;
char *buf;
int error;
#ifdef DEBUG_VMGUESTRPC
printf("%s: %s\n", __func__, keyword);
#endif
/* Send "info-get" GuestRPC command */
sbuf_new(&sb, NULL, 256, SBUF_AUTOEXTEND);
sbuf_printf(&sb, "info-get guestinfo.fbsd.%s", keyword);
sbuf_trim(&sb);
sbuf_finish(&sb);
error = vmware_guestrpc_cmd(&sb);
if (error)
return (error);
sbuf_finish(&sb);
buf = sbuf_data(&sb);
#ifdef DEBUG_VMGUESTRPC
printf("%s: result: %s\n", __func__, buf);
#endif
/*
* Buffer will contain "1 <value>" on success or
* "0 No value found" on failure
*/
if (buf[0] == '0')
return (ENOENT);
/*
* Add value from buffer to the sbuf
*/
sbuf_cat(sbufp, buf + 2);
return (0);
}
MODULE_VERSION(vmware_guestrpc, 1);