#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>
#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
#define VMW_HVGUESTRPC_OPEN_MAGIC 0x49435052
#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
}
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));
}
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);
}
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);
}
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));
}
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);
}
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);
}
int
vmware_guestrpc_cmd(struct sbuf *sbufp)
{
char *buf;
size_t cnt, len;
int chan, id, status;
uint32_t data;
if (vm_guest != VM_GUEST_VMWARE)
return (ENXIO);
chan = vmware_guestrpc_open();
if (chan == -1)
return (EIO);
buf = sbuf_data(sbufp);
len = sbuf_len(sbufp);
status = vmware_guestrpc_send_len(chan, len);
if (status == -1)
goto done;
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;
}
id = vmware_guestrpc_recv_len(chan, &len);
if (id == -1)
goto done;
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:
vmware_guestrpc_close(chan);
return (status == -1 ? EIO : 0);
}
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
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
return ((buf[0] == '0') ? EINVAL : 0);
}
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
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
if (buf[0] == '0')
return (ENOENT);
sbuf_cat(sbufp, buf + 2);
return (0);
}
MODULE_VERSION(vmware_guestrpc, 1);