vmd(8) i8042 device implementation questions

classic Classic list List threaded Threaded
13 messages Options
Reply | Threaded
Open this post in threaded view
|

vmd(8) i8042 device implementation questions

Katherine Rohl
I have my i8042 device for vmd(8) mostly implemented. It’s only missing a few commands, but since there are no PS/2 input devices yet, there isn’t very much in the way of testing I can do beyond ensuring that commands act as they should.

I have a couple questions about expected behavior, mostly related to the 8042 output port.

* The 8042 controls the CPU reset line, but I’m not sure what the behavior should be in terms of vmm/vmd. Send a reset command if the reset line gets pulled low?
* The 8042 controls Gate A20, which doesn’t seem to exist in the VM. I just have the output line not hooked up to anything. That OK?
* There is nothing hooked up to the PS/2 ports since adding input devices is out of scope for my current diff.
* Commands F0-F3 pulse 8042 output port bits low for 6uS. How should such timing be implemented in a vmd device?

Once I get these cleared up, I should have a diff ready by the weekend. The 8042 already gets detected in the OpenBSD boot process!

Katherine

Reply | Threaded
Open this post in threaded view
|

Re: vmd(8) i8042 device implementation questions

Mike Larkin-2
On Tue, May 28, 2019 at 09:57:02PM -0500, Katherine Rohl wrote:
> I have my i8042 device for vmd(8) mostly implemented. It’s only missing a few commands, but since there are no PS/2 input devices yet, there isn’t very much in the way of testing I can do beyond ensuring that commands act as they should.
>
> I have a couple questions about expected behavior, mostly related to the 8042 output port.
>
> * The 8042 controls the CPU reset line, but I’m not sure what the behavior should be in terms of vmm/vmd. Send a reset command if the reset line gets pulled low?

The reset line being asserted (deasserted?) can be shunted to the same code in
vmd that processes triple faults, thus resetting the CPU.

> * The 8042 controls Gate A20, which doesn’t seem to exist in the VM. I just have the output line not hooked up to anything. That OK?

Ignore for now.

> * There is nothing hooked up to the PS/2 ports since adding input devices is out of scope for my current diff.
> * Commands F0-F3 pulse 8042 output port bits low for 6uS. How should such timing be implemented in a vmd device?
>

We don't have very precise timing, so I'm not sure how you're going to get a
6uS pulse. What does an attached device do with such a pulse in a normal
scenario?

> Once I get these cleared up, I should have a diff ready by the weekend. The 8042 already gets detected in the OpenBSD boot process!
>
> Katherine
>

Reply | Threaded
Open this post in threaded view
|

Re: vmd(8) i8042 device implementation questions

Katherine Rohl
I did a little more looking and the only practical use is $F0 to pulse the reset line rather than setting it directly (and thus locking up the system forever). I’ll treat it as a reset.  $F1 would pulse Gate A20 (which is a useless operation in a PC) and $F2 and $F3 pulse reserved bits.

> On May 29, 2019, at 3:16 AM, Mike Larkin <[hidden email]> wrote:

> We don't have very precise timing, so I'm not sure how you're going to get a
> 6uS pulse. What does an attached device do with such a pulse in a normal
> scenario?

Reply | Threaded
Open this post in threaded view
|

Re: vmd(8) i8042 device implementation questions

Katherine Rohl
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.

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 $ */
+/*
+ * Copyright (c) 2019 Katherine Rohl <[hidden email]>
+ *
+ * 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
+ 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
+ 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()
+{
+ // 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()
+{
+#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()
+{
+ 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()
+{
+ // 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 <[hidden email]>
+ *
+ * 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);


> On May 29, 2019, at 3:31 AM, Katherine Rohl <[hidden email]> wrote:
>
> I did a little more looking and the only practical use is $F0 to pulse the reset line rather than setting it directly (and thus locking up the system forever). I’ll treat it as a reset.  $F1 would pulse Gate A20 (which is a useless operation in a PC) and $F2 and $F3 pulse reserved bits.
>
>> On May 29, 2019, at 3:16 AM, Mike Larkin <[hidden email]> wrote:
>
>> We don't have very precise timing, so I'm not sure how you're going to get a
>> 6uS pulse. What does an attached device do with such a pulse in a normal
>> scenario?

Reply | Threaded
Open this post in threaded view
|

Re: vmd(8) i8042 device implementation questions

Bryan Steele-2
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 <[hidden email]>
> + *
> + * 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 <[hidden email]>
> + *
> + * 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);
>

Reply | Threaded
Open this post in threaded view
|

Re: vmd(8) i8042 device implementation questions

Katherine Rohl
Ah, OK. This should follow style(9):



Ah, OK. This should follow style(9):

---

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..a62386b79ff
--- /dev/null
+++ b/usr.sbin/vmd/i8042.c
@@ -0,0 +1,421 @@
+/* $OpenBSD$ */
+/*
+ * Copyright (c) 2019 Katherine Rohl <[hidden email]>
+ *
+ * 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 */
+ 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
+ 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(void)
+{
+ // 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(void)
+{
+#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(void)
+{
+ 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(void)
+{
+ // 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);
+ }
+
+ if ((kbc.output_port & 0x01) == 0) /* Deasserted the reset line? */
+ {
+ /* 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 <[hidden email]>
+ *
+ * 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);

Reply | Threaded
Open this post in threaded view
|

Re: vmd(8) i8042 device implementation questions

Katherine Rohl
Apologies.

---

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..ced3b43da1c
--- /dev/null
+++ b/usr.sbin/vmd/i8042.c
@@ -0,0 +1,423 @@
+/* $OpenBSD$ */
+/*
+ * Copyright (c) 2019 Katherine Rohl <[hidden email]>
+ *
+ * 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 */
+ 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 */
+ 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(void)
+{
+ /*
+ * 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(void)
+{
+#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(void)
+{
+ 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(void)
+{
+ /* 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);
+ }
+
+ if ((kbc.output_port & 0x01) == 0) /* Deasserted the reset line? */
+ {
+ /* 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 <[hidden email]>
+ *
+ * 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);

Reply | Threaded
Open this post in threaded view
|

Re: vmd(8) i8042 device implementation questions

Mike Larkin-2
On Thu, May 30, 2019 at 11:06:57PM -0500, Katherine Rohl wrote:
> Apologies.
>
> ---
>

Hi Katherine,

 Thanks for the diff. I think we are getting close! A few comments below.

-ml

> 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..ced3b43da1c
> --- /dev/null
> +++ b/usr.sbin/vmd/i8042.c
> @@ -0,0 +1,423 @@
> +/* $OpenBSD$ */
> +/*
> + * Copyright (c) 2019 Katherine Rohl <[hidden email]>
> + *
> + * 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 */
> + 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 */
> + if (((old_output_port & 0x10) == 0) &&
> +    ((kbc.output_port & 0x10) == 1))
> + vcpu_assert_pic_irq(kbc.vm_id, 0, KBC_KBDIRQ);

For this IRQ, if it's edge triggered, please assert then deassert the line.
The i8259 code should handle that properly. What you have here is a level
triggered interrupt (eg, the line will stay asserted until someone
does a 1 -> 0 transition below). Same goes for the next few cases.

> + 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(void)
> +{
> + /*
> + * The command is in kbc.command and the argument
> + *  is in kbc.data.
> + **/

Extra *

> +
> + 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(void)
> +{
> +#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(void)
> +{
> + 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(void)
> +{
> + /* 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:

vei: exit information for this i8042 command

> + *
> + * 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:

vei: exit information for this i8042 command

> + *
> + */
> +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);
> + }
> +
> + if ((kbc.output_port & 0x01) == 0) /* Deasserted the reset line? */
> + {
> + /* TODO: Reset the CPU. */

I would recommend changing vcpu_exit_inout to return a value, and if we
return EAGAIN here it should reset the VM (look at vm.c for how we handle
triple faults in the same way).

If you would rather get this in later, that's fine. It's probably a different
change, to be honest.

> + }
> +
> + 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 (?)

This means no interrupt will be injected. I'm not sure if that's what you want.
See vm.c: vcpu_exit_inout(..). It looks like you may have manually asserted the
IRQ in this file, which is a bit different than what we do in other devices. That
may be okay, though.

> + *
> + */
> +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);
> +

Previous line has a trailing tab.

Also, please bump the revision in the vcpu struct for send/receive
as we will be sending a new struct layout now.

> +}
> 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 <[hidden email]>
> + *
> + * 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]);
> -
> +

Previous line has extra whitespace.

>   if (ret) {
>   log_warnx("%s: can't lock vcpu run mtx (%d)",
>      __func__, (int)ret);
>

Reply | Threaded
Open this post in threaded view
|

Re: vmd(8) i8042 device implementation questions

Katherine Rohl
Couple questions:

> This means no interrupt will be injected. I'm not sure if that's what you want.
> See vm.c: vcpu_exit_inout(..). It looks like you may have manually asserted the
> IRQ in this file, which is a bit different than what we do in other devices. That
> may be okay, though.

The device can assert zero, one, or two IRQs depending on the state of the input ports. Are we capable of asserting two IRQs at once through vcpu_exit_i8042?

> For this IRQ, if it's edge triggered, please assert then deassert the line.
> The i8259 code should handle that properly. What you have here is a level
> triggered interrupt (eg, the line will stay asserted until someone
> does a 1 -> 0 transition below). Same goes for the next few cases.

Would asserting the IRQs through the exit function handle this for me if that’s possible?

> Also, please bump the revision in the vcpu struct for send/receive
> as we will be sending a new struct layout now.

Where exactly? The file revision?

Reply | Threaded
Open this post in threaded view
|

Re: vmd(8) i8042 device implementation questions

Jasper Lievisse Adriaanse-2
On Sat, Jun 01, 2019 at 06:12:16PM -0500, Katherine Rohl wrote:

> Couple questions:
>
> > This means no interrupt will be injected. I'm not sure if that's what you want.
> > See vm.c: vcpu_exit_inout(..). It looks like you may have manually asserted the
> > IRQ in this file, which is a bit different than what we do in other devices. That
> > may be okay, though.
>
> The device can assert zero, one, or two IRQs depending on the state of the input ports. Are we capable of asserting two IRQs at once through vcpu_exit_i8042?
>
> > For this IRQ, if it's edge triggered, please assert then deassert the line.
> > The i8259 code should handle that properly. What you have here is a level
> > triggered interrupt (eg, the line will stay asserted until someone
> > does a 1 -> 0 transition below). Same goes for the next few cases.
>
> Would asserting the IRQs through the exit function handle this for me if that???s possible?
>
> > Also, please bump the revision in the vcpu struct for send/receive
> > as we will be sending a new struct layout now.
>
> Where exactly? The file revision?
That would be VM_DUMP_VERSION in vmd.h I reckon.

--
jasper

Reply | Threaded
Open this post in threaded view
|

Re: vmd(8) i8042 device implementation questions

Mike Larkin-2
On Sun, Jun 02, 2019 at 03:21:34PM +0200, Jasper Lievisse Adriaanse wrote:

> On Sat, Jun 01, 2019 at 06:12:16PM -0500, Katherine Rohl wrote:
> > Couple questions:
> >
> > > This means no interrupt will be injected. I'm not sure if that's what you want.
> > > See vm.c: vcpu_exit_inout(..). It looks like you may have manually asserted the
> > > IRQ in this file, which is a bit different than what we do in other devices. That
> > > may be okay, though.
> >
> > The device can assert zero, one, or two IRQs depending on the state of the input ports. Are we capable of asserting two IRQs at once through vcpu_exit_i8042?
> >
> > > For this IRQ, if it's edge triggered, please assert then deassert the line.
> > > The i8259 code should handle that properly. What you have here is a level
> > > triggered interrupt (eg, the line will stay asserted until someone
> > > does a 1 -> 0 transition below). Same goes for the next few cases.
> >
> > Would asserting the IRQs through the exit function handle this for me if that???s possible?
> >
> > > Also, please bump the revision in the vcpu struct for send/receive
> > > as we will be sending a new struct layout now.
> >
> > Where exactly? The file revision?
> That would be VM_DUMP_VERSION in vmd.h I reckon.
>

Yes, that's what I meant.

> --
> jasper
>

Reply | Threaded
Open this post in threaded view
|

Re: vmd(8) i8042 device implementation questions

Mike Larkin-2
In reply to this post by Katherine Rohl
On Sat, Jun 01, 2019 at 06:12:16PM -0500, Katherine Rohl wrote:
> Couple questions:
>
> > This means no interrupt will be injected. I'm not sure if that's what you want.
> > See vm.c: vcpu_exit_inout(..). It looks like you may have manually asserted the
> > IRQ in this file, which is a bit different than what we do in other devices. That
> > may be okay, though.
>
> The device can assert zero, one, or two IRQs depending on the state of the input ports. Are we capable of asserting two IRQs at once through vcpu_exit_i8042?
>

Yes, just assert/deassert what you want and it should drive the PIC state
machine accordingly. LMK if that doesn't work. Assert followed by immediate
deassert for edge triggered, and assert followed by deassert after ack for
level triggered. I think all these are edge triggered though.

> > For this IRQ, if it's edge triggered, please assert then deassert the line.
> > The i8259 code should handle that properly. What you have here is a level
> > triggered interrupt (eg, the line will stay asserted until someone
> > does a 1 -> 0 transition below). Same goes for the next few cases.
>
> Would asserting the IRQs through the exit function handle this for me if that’s possible?

The return-to-vmm path can assert one single interrupt via returning something
other than 0xFF from the exit handler in vmd. But if you need two, you'll need
to either rework that logic (and fix all the other devices) or just
assert/deassert one in the exit handler function and have the return-to-vmm
path handle the other. Your call.

Note - it is probably a good idea to rework the logic at some point because the
current model can only handle "inject this edge triggered interrupt", and not
other interesting scenarios we may need later like "assert this level triggered
interrupt" or being able to flexibly configure a number of different interrupts
(edge or level triggered). There is also no provision to handle deasserting
something in the return-to-vmm path presently (that has to be handled in each
device's I/O routine in vmd - yuck).

>
> > Also, please bump the revision in the vcpu struct for send/receive
> > as we will be sending a new struct layout now.
>
> Where exactly? The file revision?

Reply | Threaded
Open this post in threaded view
|

Re: vmd(8) i8042 device implementation questions

Katherine Rohl
OK, here’s an updated diff. Sorry for the delay, I’ve been having network problems. Boots up and gets detected immediately by OpenBSD, no delays.

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..927edf6e60a
--- /dev/null
+++ b/usr.sbin/vmd/i8042.c
@@ -0,0 +1,417 @@
+/* $OpenBSD$ */
+/*
+ * Copyright (c) 2019 Katherine Rohl <[hidden email]>
+ *
+ * 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 */
+ 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 */
+ if (((old_output_port & 0x10) == 0) &&
+    ((kbc.output_port & 0x10) == 1))
+ {
+ vcpu_assert_pic_irq(kbc.vm_id, 0, 1);
+ vcpu_deassert_pic_irq(kbc.vm_id, 0, 1);
+ }
+ if (((old_output_port & 0x20) == 0) &&
+    ((kbc.output_port & 0x20) == 1))
+ {
+ vcpu_assert_pic_irq(kbc.vm_id, 0, 1);
+ vcpu_deassert_pic_irq(kbc.vm_id, 0, 1);
+ }
+}
+
+/*
+ * 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(void)
+{
+ /*
+ * 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(void)
+{
+#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(void)
+{
+ 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(void)
+{
+ /* 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: exit information for this i8042 command
+ *
+ * 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: exit information for this i8042 command
+ *
+ */
+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);
+ }
+
+ if ((kbc.output_port & 0x01) == 0) /* Deasserted the reset line? */
+ {
+ /* 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.
+ *
+ */
+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 <[hidden email]>
+ *
+ * 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);
diff --git a/usr.sbin/vmd/vmd.h b/usr.sbin/vmd/vmd.h
index 7e40b38f51d..a9807de2188 100644
--- a/usr.sbin/vmd/vmd.h
+++ b/usr.sbin/vmd/vmd.h
@@ -215,7 +215,7 @@ struct vm_dump_header {
 #define VM_DUMP_SIGNATURE VMM_HV_SIGNATURE
  uint8_t vmh_pad[3];
  uint8_t vmh_version;
-#define VM_DUMP_VERSION 6
+#define VM_DUMP_VERSION 7
  struct vm_dump_header_cpuid
     vmh_cpuids[VM_DUMP_HEADER_CPUID_COUNT];
 } __packed;