On Thu, May 30, 2019 at 12:09:01AM -0500, Katherine Rohl wrote: > Okay, here's the first pass of my 8042 device - I wasn't able to figure out > how to tie the reset line to the guest VM reset, so I was hoping someone > could give me a hand with that. Other than that, it attaches to i386 and > amd64 OpenBSD guests. There's no input yet as I mentioned, but the 8042 > commands execute according to specs.
Cool! :-) Some style(9) nits inline below. > diff --git a/sys/arch/amd64/amd64/vmm.c b/sys/arch/amd64/amd64/vmm.c > index 4ffb2ff899f..7de38facc78 100644 > --- a/sys/arch/amd64/amd64/vmm.c > +++ b/sys/arch/amd64/amd64/vmm.c > @@ -5359,6 +5359,8 @@ svm_handle_inout(struct vcpu *vcpu) > case IO_ICU1 ... IO_ICU1 + 1: > case 0x40 ... 0x43: > case PCKBC_AUX: > + case 0x60: > + case 0x64: > case IO_RTC ... IO_RTC + 1: > case IO_ICU2 ... IO_ICU2 + 1: > case 0x3f8 ... 0x3ff: > diff --git a/usr.sbin/vmd/Makefile b/usr.sbin/vmd/Makefile > index 8645df7aecf..f39d85b1b14 100644 > --- a/usr.sbin/vmd/Makefile > +++ b/usr.sbin/vmd/Makefile > @@ -4,7 +4,7 @@ > > PROG= vmd > SRCS= vmd.c control.c log.c priv.c proc.c config.c vmm.c > -SRCS+= vm.c loadfile_elf.c pci.c virtio.c i8259.c mc146818.c > +SRCS+= vm.c loadfile_elf.c pci.c virtio.c i8259.c mc146818.c > i8042.c > SRCS+= ns8250.c i8253.c vmboot.c ufs.c disklabel.c dhcp.c > packet.c > SRCS+= parse.y atomicio.c vioscsi.c vioraw.c vioqcow2.c > fw_cfg.c > > diff --git a/usr.sbin/vmd/i8042.c b/usr.sbin/vmd/i8042.c > new file mode 100644 > index 00000000000..b57139368cf > --- /dev/null > +++ b/usr.sbin/vmd/i8042.c > @@ -0,0 +1,439 @@ > +/* $OpenBSD: i8042.c,v 1.00 2019/05/25 18:18:00 rohl Exp $ */ This can be just: /* $OpenBSD$ */ It will be expanded automatically by cvs > +/* > + * Copyright (c) 2019 Katherine Rohl <luig...@gmail.com> > + * > + * Permission to use, copy, modify, and distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +#include <sys/types.h> > + > +#include <dev/ic/i8042reg.h> > + > +#include <machine/vmmvar.h> > + > +#include <event.h> > +#include <pthread.h> > +#include <string.h> > +#include <stddef.h> > +#include <unistd.h> > + > +#include "i8042.h" > +#include "proc.h" > +#include "vmm.h" > +#include "atomicio.h" > +#include "vmd.h" > + > +struct i8042 { > + uint8_t data; > + uint8_t command; > + uint8_t status; > + uint8_t internal_ram[16]; > + > + uint8_t input_port; > + uint8_t output_port; > + > + // Port 0 is keyboard, Port 1 is mouse C++style single-line comments should be avoided, use /* .. */ instead. > + uint8_t ps2_enabled[2]; > + > + uint32_t vm_id; > + > + // Is the 8042 awaiting a command argument byte? ... > + uint8_t awaiting_argbyte; > +}; > + > +struct i8042 kbc; > +pthread_mutex_t kbc_mtx; > + > +/* > + * i8042_init > + * > + * Initialize the emulated i8042 KBC. > + * > + * Parameters: > + * vm_id: vmm(4) assigned ID of the VM > + */ > +void > +i8042_init(uint32_t vm_id) > +{ > + memset(&kbc, 0, sizeof(kbc)); > + > + if(pthread_mutex_init(&kbc_mtx, NULL) != 0) > + fatalx("unable to create kbc mutex"); > + > + kbc.vm_id = vm_id; > + > + // Always initialize the reset bit to 1 for proper operation. .. > + kbc.output_port = KBC_DEFOUTPORT; > + kbc.input_port = KBC_DEFINPORT; > +} > + > +/* > + * i8042_set_output_port > + * > + * Set the output port of the KBC. > + * Many bits have side effects, so handle their on/off > + * transition effects as required. > + */ > +static void > +i8042_set_output_port(uint8_t byte) > +{ > + /* 0x01: reset line > + * 0x02: A20 gate (not implemented) > + * 0x04: aux port clock > + * 0x08: aux port data > + * 0x10: data in output buffer is from kb port (IRQ1) > + * 0x20: data in output buffer is from aux port (IRQ12) > + * 0x40: kb port clock > + * 0x80: kb port data > + */ > + > + uint8_t old_output_port = kbc.output_port; > + kbc.output_port = byte; > + > + // 0 -> 1 transition .. The braces should be on the same line, or in cases like this can be omitted. There should also be a space after keywords, if (..). > + if(((old_output_port & 0x10) == 0) && > + ((kbc.output_port & 0x10) == 1)) > + { > + vcpu_assert_pic_irq(kbc.vm_id, 0, KBC_KBDIRQ); > + } > + if(((old_output_port & 0x20) == 0) && > + ((kbc.output_port & 0x20) == 1)) > + { > + vcpu_assert_pic_irq(kbc.vm_id, 0, KBC_AUXIRQ); > + } > + > + // 1 -> 0 transition > + if(((old_output_port & 0x10) == 1) && > + ((kbc.output_port & 0x10) == 0)) > + { > + vcpu_deassert_pic_irq(kbc.vm_id, 0, KBC_KBDIRQ); > + } > + if(((old_output_port & 0x20) == 1) && > + ((kbc.output_port & 0x20) == 0)) > + { > + vcpu_deassert_pic_irq(kbc.vm_id, 0, KBC_AUXIRQ); > + } > +} > + > +/* > + * i8042_perform_twobyte_command > + * > + * Handles the two-byte command (one byte of command and > + * one byte of data) after the data has been received. > + */ > +static void > +i8042_perform_twobyte_command() ^ Functions that take no arguments should have void here. > +{ > + // The command is in kbc.command and the argument > + // is in kbc.data. > + > + switch(kbc.command) > + { > + case 0x60 ... 0x7F: // KBC_RAMWRITE > + kbc.internal_ram[kbc.command - 0x60] = kbc.data; > + break; > + case KBC_CMDWOUT: > + i8042_set_output_port(kbc.data); > + break; > + case KBC_KBDECHO: > + i8042_set_output_port(kbc.output_port|0x10); > + break; > + case KBC_AUXECHO: > + i8042_set_output_port(kbc.output_port|0x20); > + break; > + case KBC_AUXWRITE: > + // No auxiliary device for this to go to. > + break; > + } > + > + kbc.awaiting_argbyte = 0; > +} > + > +/* > + * i8042_process_command > + * > + * Handles the command byte currently in the KBC's command > + * register, setting the data register accordingly. > + */ > +static void > +i8042_process_command() Same. > +{ > +#define SET_DIB_BIT (kbc.status |= KBS_DIB) > + kbc.awaiting_argbyte = 0; > + > + switch(kbc.command) > + { > + case 0x20 ... 0x3F: // KBC_RAMREAD > + kbc.data = kbc.internal_ram[kbc.command - 0x20]; > + SET_DIB_BIT; > + break; > + case 0x60 ... 0x7F: // KBC_RAMWRITE > + case KBC_CMDWOUT: > + case KBC_KBDECHO: > + case KBC_AUXECHO: > + case KBC_AUXWRITE: > + kbc.awaiting_argbyte = 1; > + break; > + case KBC_AUXENABLE: > + kbc.ps2_enabled[KBC_AUXPORT] = 1; > + break; > + case KBC_AUXDISABLE: > + kbc.ps2_enabled[KBC_AUXPORT] = 0; > + break; > + case KBC_AUXTEST: > + kbc.data = 0; > + SET_DIB_BIT; > + break; > + case KBC_KBDTEST: > + kbc.data = 0; > + SET_DIB_BIT; > + break; > + case KBC_KBDENABLE: > + kbc.ps2_enabled[KBC_KBDPORT] = 1; > + break; > + case KBC_KBDDISABLE: > + kbc.ps2_enabled[KBC_KBDPORT] = 0; > + break; > + case KBC_READINPORT: > + kbc.data = kbc.input_port; > + SET_DIB_BIT; > + break; > + case KBC_POLLINLO: > + kbc.status &= 0x0F; > + kbc.status |= (kbc.input_port << 4); > + break; > + case KBC_POLLINHI: > + kbc.status &= 0x0F; > + kbc.status |= (kbc.input_port & 0xF0); > + break; > + case KBC_CMDROUT: > + kbc.data = kbc.output_port; > + SET_DIB_BIT; > + break; > + case KBC_SELFTEST: > + kbc.data = 0x55; > + SET_DIB_BIT; > + break; > + case KBC_PULSE0: > + kbc.output_port &= 0xFE; > + break; > + case KBC_PULSE1: > + case KBC_PULSE2: > + case KBC_PULSE3: > + // Ignore these commands. > + break; > + } > +} > + > +/* > + * i8042_read_status_register > + * > + * Handles status register reads. > + * > + * Parameters: > + * > + * Return value: > + * Value of the KBC status register. > + */ > +static uint8_t > +i8042_read_status_register() > +{ Same. > + return kbc.status; > +} > + > +/* > + * i8042_read_data_register > + * > + * Reads the data register of the KBC. > + * > + * Parameters: > + * > + * Return value: > + * Value of the KBC data register. > + */ > +static uint8_t > +i8042_read_data_register() > +{ And here. > + // Clear the output buffer full bit. > + kbc.status &= 0xFE; > + > + return kbc.data; > +} > + > +/* > + * i8042_write_command_register > + * > + * Writes a byte to the 8042 command register. > + * > + * Parameters: > + * data: The byte to write. > + */ > +static void > +i8042_write_command_register(uint8_t data) > +{ > + kbc.command = data; > + i8042_process_command(); > +} > + > +/* > + * i8042_write_data_register > + * > + * Write a byte to the KBC data register. > + * > + * Parameters: > + * data: The byte to write. > + */ > +static void > +i8042_write_data_register(uint8_t data) > +{ > + kbc.data = data; > + > + if(kbc.awaiting_argbyte) > + { > + i8042_perform_twobyte_command(); > + } > +} > + > +/* > + * i8042_io_read > + * > + * Handles emulated reads of the KBC's I/O ports. > + * > + * Parameters: > + * vei: > + * > + * Return value: > + * The byte read from the addressed port. > + * > + */ > +static uint8_t > +i8042_io_read(struct vm_exit *vei) > +{ > + uint16_t port = vei->vei.vei_port; > + uint8_t rv; > + > + mutex_lock(&kbc_mtx); > + > + switch(port) > + { > + case KBC_CMD: > + rv = i8042_read_status_register();; > + break; > + case KBC_DATA: > + rv = i8042_read_data_register(); > + break; > + default: > + fatal("%s: invalid port 0x%x", __func__, port); > + } > + > + mutex_unlock(&kbc_mtx); > + return(rv); > +} > + > +/* > + * i8042_io_write > + * > + * Handles writes to the emulated KBC device, setting the > + * value of the selected register. > + * > + * Parameters: > + * vei: > + * > + */ > +static void > +i8042_io_write(struct vm_exit *vei) > +{ > + uint16_t port = vei->vei.vei_port; > + uint32_t data; > + > + get_input_data(vei, &data); > + > + mutex_lock(&kbc_mtx); > + switch(port) > + { > + case KBC_DATA: > + i8042_write_data_register(data); > + break; > + case KBC_CMD: > + i8042_write_command_register(data); > + break; > + default: > + fatal("%s: invalid port 0x%x", __func__, port); > + } > + > + // Have we deasserted the reset line? > + if((kbc.output_port & 0x01) == 0) > + { > + // TODO: Reset the CPU. > + } > + > + mutex_unlock(&kbc_mtx); > +} > + > +/* > + * vcpu_exit_i8042 > + * > + * Handles the 0x60 and 0x64 i8042 KBC in/out exits. > + * > + * Parameters: > + * vrp: vm run parameters containing exit information for the I/O > + * instruction being performed. > + * > + * Return value: > + * 0xFF (?) > + * > + */ > +uint8_t > +vcpu_exit_i8042(struct vm_run_params *vrp) > +{ > + struct vm_exit *vei = vrp->vrp_exit; > + > + if(vei->vei.vei_dir == VEI_DIR_OUT) > + { > + i8042_io_write(vei); > + } > + else > + { > + set_return_data(vei, i8042_io_read(vei)); > + } > + > + return (0xFF); > +} > + > +int > +i8042_restore(int fd) > +{ > + log_debug("%s: restoring KBC", __func__); > + if(atomicio(read, fd, &kbc, sizeof(kbc)) != sizeof(kbc)) { > + log_warnx("%s: error reading KBC from fd", __func__); > + return (-1); > + } > + > + if(pthread_mutex_init(&kbc_mtx, NULL) != 0) > + fatalx("unable to create kbc mutex"); > + > + return (0); > +} > + > +int > +i8042_dump(int fd) > +{ > + log_debug("%s: sending KBC", __func__); > + if(atomicio(vwrite, fd, &kbc, sizeof(kbc)) != sizeof(kbc)) { > + log_warnx("%s: error writing KBC to fd", __func__); > + return (-1); > + } > + > + return (0); > + > +} > diff --git a/usr.sbin/vmd/i8042.h b/usr.sbin/vmd/i8042.h > new file mode 100644 > index 00000000000..67af8f54f2a > --- /dev/null > +++ b/usr.sbin/vmd/i8042.h > @@ -0,0 +1,45 @@ > +/* $OpenBSD: i8042.c,v 1.00 2019/05/25 18:18:00 rohl Exp $ */ :-) > +/* > + * Copyright (c) 2019 Katherine Rohl <luig...@gmail.com> > + * > + * Permission to use, copy, modify, and distribute this software for any > + * purpose with or without fee is hereby granted, provided that the above > + * copyright notice and this permission notice appear in all copies. > + * > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. > + */ > + > +/* > + * Emulated i8042 (keyboard controller, based on 8048) > + */ > + > +#define KBC_DEFINPORT 0xB0 /* Default input port value. */ > +#define KBC_DEFOUTPORT 0x01 /* Default output port value. */ > + > +#define KBC_KBDPORT 0 /* Keyboard is port 0. */ > +#define KBC_AUXPORT 1 /* Auxiliary is port 1. */ > + > +#define KBC_KBDIRQ 1 /* Keyboard IRQ. */ > +#define KBC_AUXIRQ 12 /* Secondary IRQ. */ > + > +#define KBC_DATA 0x60 /* Data is I/O port 60h. */ > +#define KBC_CMD 0x64 /* Command is I/O port 64h. */ > + > +/* Commands that aren't useful for public includes. */ > +#define KBC_READINPORT 0xC0 /* Read the input port, place in data. > */ > +#define KBC_POLLINLO 0xC1 /* Poll low 4 bits of input, place in status. */ > +#define KBC_POLLINHI 0xC2 /* Poll high 4 bits of input, place in status. > */ > +#define KBC_CMDROUT 0xD0 /* Read the output port. */ > + > +uint8_t vcpu_exit_i8042(struct vm_run_params *); > + > +void i8042_init(uint32_t); > + > +int i8042_restore(int); > +int i8042_dump(int); > diff --git a/usr.sbin/vmd/vm.c b/usr.sbin/vmd/vm.c > index 72b2e379ac3..35adf25c1cf 100644 > --- a/usr.sbin/vmd/vm.c > +++ b/usr.sbin/vmd/vm.c > @@ -61,6 +61,7 @@ > #include "i8259.h" > #include "ns8250.h" > #include "mc146818.h" > +#include "i8042.h" > #include "fw_cfg.h" > #include "atomicio.h" > > @@ -949,6 +950,11 @@ init_emulated_hw(struct vmop_create_params *vmc, int > child_cdrom, > ioports_map[ELCR0] = vcpu_exit_elcr; > ioports_map[ELCR1] = vcpu_exit_elcr; > > + /* Init i8042 keyboard controller */ > + i8042_init(vcp->vcp_id); > + ioports_map[0x64] = vcpu_exit_i8042; > + ioports_map[0x60] = vcpu_exit_i8042; > + > /* Init ns8250 UART */ > ns8250_init(con_fd, vcp->vcp_id); > for (i = COM1_DATA; i <= COM1_SCR; i++) > @@ -1002,6 +1008,11 @@ restore_emulated_hw(struct vm_create_params *vcp, int > fd, > ioports_map[IO_ICU2] = vcpu_exit_i8259; > ioports_map[IO_ICU2 + 1] = vcpu_exit_i8259; > > + /* Init i8042 keyboard controller */ > + i8042_restore(fd); > + ioports_map[0x64] = vcpu_exit_i8042; > + ioports_map[0x60]= vcpu_exit_i8042; > + > /* Init ns8250 UART */ > ns8250_restore(fd, con_fd, vcp->vcp_id); > for (i = COM1_DATA; i <= COM1_SCR; i++) > @@ -1291,7 +1302,7 @@ vcpu_run_loop(void *arg) > > for (;;) { > ret = pthread_mutex_lock(&vcpu_run_mtx[n]); > - > + > if (ret) { > log_warnx("%s: can't lock vcpu run mtx (%d)", > __func__, (int)ret); >