git: ecaab0fb5da4 - main - guestrpc module to handle VMware backdoor port GuestRPC functionality
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 01 May 2024 19:46:02 UTC
The branch main has been updated by stevek: URL: https://cgit.FreeBSD.org/src/commit/?id=ecaab0fb5da4cd7340c62b77bcb19efcfa1b69df commit ecaab0fb5da4cd7340c62b77bcb19efcfa1b69df Author: Stephen J. Kiernan <stevek@FreeBSD.org> AuthorDate: 2024-05-01 19:45:45 +0000 Commit: Stephen J. Kiernan <stevek@FreeBSD.org> CommitDate: 2024-05-01 19:45:45 +0000 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 --- sys/conf/files.x86 | 1 + sys/x86/acpica/madt.c | 3 +- sys/x86/conf/NOTES | 5 + sys/x86/include/vmware.h | 8 +- sys/x86/include/vmware_guestrpc.h | 37 +++++ sys/x86/x86/identcpu.c | 3 +- sys/x86/x86/tsc.c | 2 +- sys/x86/x86/vmware_guestrpc.c | 337 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 391 insertions(+), 5 deletions(-) diff --git a/sys/conf/files.x86 b/sys/conf/files.x86 index ce31c42215be..9439a46ce347 100644 --- a/sys/conf/files.x86 +++ b/sys/conf/files.x86 @@ -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 diff --git a/sys/x86/acpica/madt.c b/sys/x86/acpica/madt.c index adfeed70c5c6..c6358ad7e847 100644 --- a/sys/x86/acpica/madt.c +++ b/sys/x86/acpica/madt.c @@ -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"); diff --git a/sys/x86/conf/NOTES b/sys/x86/conf/NOTES index 5042585da310..87a8f8924d12 100644 --- a/sys/x86/conf/NOTES +++ b/sys/x86/conf/NOTES @@ -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: # diff --git a/sys/x86/include/vmware.h b/sys/x86/include/vmware.h index 38bca8c146a7..5ab6462b862d 100644 --- a/sys/x86/include/vmware.h +++ b/sys/x86/include/vmware.h @@ -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"); } diff --git a/sys/x86/include/vmware_guestrpc.h b/sys/x86/include/vmware_guestrpc.h new file mode 100644 index 000000000000..24055b6c249e --- /dev/null +++ b/sys/x86/include/vmware_guestrpc.h @@ -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_ */ diff --git a/sys/x86/x86/identcpu.c b/sys/x86/x86/identcpu.c index 919dda722d71..953736d6b25c 100644 --- a/sys/x86/x86/identcpu.c +++ b/sys/x86/x86/identcpu.c @@ -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); diff --git a/sys/x86/x86/tsc.c b/sys/x86/x86/tsc.c index 7c11a1f5f300..4edaa37d9b54 100644 --- a/sys/x86/x86/tsc.c +++ b/sys/x86/x86/tsc.c @@ -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; diff --git a/sys/x86/x86/vmware_guestrpc.c b/sys/x86/x86/vmware_guestrpc.c new file mode 100644 index 000000000000..b62f40c5a9ef --- /dev/null +++ b/sys/x86/x86/vmware_guestrpc.c @@ -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);