diff --git a/sys/conf/files.x86 b/sys/conf/files.x86 --- a/sys/conf/files.x86 +++ b/sys/conf/files.x86 @@ -380,6 +380,7 @@ 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 --- a/sys/x86/acpica/madt.c +++ b/sys/x86/acpica/madt.c @@ -159,7 +159,8 @@ } 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 --- a/sys/x86/conf/NOTES +++ b/sys/x86/conf/NOTES @@ -550,6 +550,11 @@ 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 --- 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.h b/sys/x86/include/vmware_guestrpc.h copy from sys/x86/include/vmware.h copy to sys/x86/include/vmware_guestrpc.h --- a/sys/x86/include/vmware.h +++ b/sys/x86/include/vmware_guestrpc.h @@ -1,6 +1,7 @@ /*- - * Copyright (c) 2011-2014 Jung-uk Kim - * All rights reserved. + * 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 @@ -24,27 +25,13 @@ * SUCH DAMAGE. */ -#ifndef _X86_VMWARE_H_ -#define _X86_VMWARE_H_ - -#define VMW_HVMAGIC 0x564d5868 -#define VMW_HVPORT 0x5658 - -#define VMW_HVCMD_GETVERSION 10 -#define VMW_HVCMD_GETHZ 45 -#define VMW_HVCMD_GETVCPU_INFO 68 - -#define VMW_VCPUINFO_LEGACY_X2APIC (1 << 3) -#define VMW_VCPUINFO_VCPU_RESERVED (1 << 31) +#ifndef _X86_VMWARE_GUESTRPC_H_ +#define _X86_VMWARE_GUESTRPC_H_ -static __inline void -vmware_hvcall(u_int cmd, u_int *p) -{ +struct sbuf; - __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) - : "memory"); -} +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_H_ */ +#endif /* _X86_VMWARE_GUESTRPC_H_ */ diff --git a/sys/x86/x86/identcpu.c b/sys/x86/x86/identcpu.c --- a/sys/x86/x86/identcpu.c +++ b/sys/x86/x86/identcpu.c @@ -1470,7 +1470,8 @@ 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 --- a/sys/x86/x86/tsc.c +++ b/sys/x86/x86/tsc.c @@ -139,7 +139,7 @@ { 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 --- /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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* 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 " 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);