On Thu, May 30, 2019 at 12:09:01AM -0500, Katherine Rohl wrote:
> Okay, here's the first pass of my 8042 device - I wasn't able to figure out 
> how to tie the reset line to the guest VM reset, so I was hoping someone 
> could give me a hand with that. Other than that, it attaches to i386 and 
> amd64 OpenBSD guests. There's no input yet as I mentioned, but the 8042 
> commands execute according to specs.

Cool! :-)

Some style(9) nits inline below.

> diff --git a/sys/arch/amd64/amd64/vmm.c b/sys/arch/amd64/amd64/vmm.c
> index 4ffb2ff899f..7de38facc78 100644
> --- a/sys/arch/amd64/amd64/vmm.c
> +++ b/sys/arch/amd64/amd64/vmm.c
> @@ -5359,6 +5359,8 @@ svm_handle_inout(struct vcpu *vcpu)
>       case IO_ICU1 ... IO_ICU1 + 1:
>       case 0x40 ... 0x43:
>       case PCKBC_AUX:
> +     case 0x60:
> +     case 0x64:
>       case IO_RTC ... IO_RTC + 1:
>       case IO_ICU2 ... IO_ICU2 + 1:
>       case 0x3f8 ... 0x3ff:
> diff --git a/usr.sbin/vmd/Makefile b/usr.sbin/vmd/Makefile
> index 8645df7aecf..f39d85b1b14 100644
> --- a/usr.sbin/vmd/Makefile
> +++ b/usr.sbin/vmd/Makefile
> @@ -4,7 +4,7 @@
>  
>  PROG=                vmd
>  SRCS=                vmd.c control.c log.c priv.c proc.c config.c vmm.c
> -SRCS+=               vm.c loadfile_elf.c pci.c virtio.c i8259.c mc146818.c
> +SRCS+=               vm.c loadfile_elf.c pci.c virtio.c i8259.c mc146818.c 
> i8042.c
>  SRCS+=               ns8250.c i8253.c vmboot.c ufs.c disklabel.c dhcp.c 
> packet.c
>  SRCS+=               parse.y atomicio.c vioscsi.c vioraw.c vioqcow2.c 
> fw_cfg.c
>  
> diff --git a/usr.sbin/vmd/i8042.c b/usr.sbin/vmd/i8042.c
> new file mode 100644
> index 00000000000..b57139368cf
> --- /dev/null
> +++ b/usr.sbin/vmd/i8042.c
> @@ -0,0 +1,439 @@
> +/* $OpenBSD: i8042.c,v 1.00 2019/05/25 18:18:00 rohl Exp $ */

This can be just:

/* $OpenBSD$ */

It will be expanded automatically by cvs

> +/*
> + * Copyright (c) 2019 Katherine Rohl <luig...@gmail.com>
> + *
> + * Permission to use, copy, modify, and distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +#include <sys/types.h>
> +
> +#include <dev/ic/i8042reg.h>
> +
> +#include <machine/vmmvar.h>
> +
> +#include <event.h>
> +#include <pthread.h>
> +#include <string.h>
> +#include <stddef.h>
> +#include <unistd.h>
> +
> +#include "i8042.h"
> +#include "proc.h"
> +#include "vmm.h"
> +#include "atomicio.h"
> +#include "vmd.h"
> +
> +struct i8042 {
> +     uint8_t data;
> +     uint8_t command;
> +     uint8_t status;
> +     uint8_t internal_ram[16];
> +
> +     uint8_t input_port;
> +     uint8_t output_port;
> +     
> +     // Port 0 is keyboard, Port 1 is mouse

C++style single-line comments should be avoided, use /* .. */ instead.

> +     uint8_t ps2_enabled[2];
> +     
> +     uint32_t vm_id;
> +
> +     // Is the 8042 awaiting a command argument byte?

...

> +     uint8_t awaiting_argbyte;
> +};
> +
> +struct i8042 kbc;
> +pthread_mutex_t kbc_mtx;
> +
> +/*
> + * i8042_init
> + *
> + * Initialize the emulated i8042 KBC.
> + *
> + * Parameters:
> + *  vm_id: vmm(4) assigned ID of the VM
> + */
> +void
> +i8042_init(uint32_t vm_id)
> +{
> +     memset(&kbc, 0, sizeof(kbc));
> +
> +     if(pthread_mutex_init(&kbc_mtx, NULL) != 0)
> +             fatalx("unable to create kbc mutex");
> +
> +     kbc.vm_id = vm_id;
> +
> +     // Always initialize the reset bit to 1 for proper operation.

..

> +     kbc.output_port = KBC_DEFOUTPORT;
> +     kbc.input_port = KBC_DEFINPORT;
> +}
> +
> +/*
> + * i8042_set_output_port
> + *
> + * Set the output port of the KBC.
> + * Many bits have side effects, so handle their on/off
> + *  transition effects as required.
> + */
> +static void
> +i8042_set_output_port(uint8_t byte)
> +{
> +     /* 0x01: reset line
> +      * 0x02: A20 gate (not implemented)
> +      * 0x04: aux port clock
> +      * 0x08: aux port data
> +      * 0x10: data in output buffer is from kb port (IRQ1)
> +      * 0x20: data in output buffer is from aux port (IRQ12)
> +      * 0x40: kb port clock
> +      * 0x80: kb port data
> +      */
> +
> +     uint8_t old_output_port = kbc.output_port;
> +     kbc.output_port = byte;
> +     
> +     // 0 -> 1 transition

..

The braces should be on the same line, or in cases like this can
be omitted. There should also be a space after keywords, if (..).

> +     if(((old_output_port & 0x10) == 0) &&
> +        ((kbc.output_port & 0x10) == 1))
> +        {
> +                vcpu_assert_pic_irq(kbc.vm_id, 0, KBC_KBDIRQ);
> +        }
> +     if(((old_output_port & 0x20) == 0) &&
> +        ((kbc.output_port & 0x20) == 1))
> +        {
> +                vcpu_assert_pic_irq(kbc.vm_id, 0, KBC_AUXIRQ);
> +        }
> +
> +     // 1 -> 0 transition
> +     if(((old_output_port & 0x10) == 1) &&
> +        ((kbc.output_port & 0x10) == 0))
> +        {
> +                vcpu_deassert_pic_irq(kbc.vm_id, 0, KBC_KBDIRQ);
> +        }
> +     if(((old_output_port & 0x20) == 1) &&
> +        ((kbc.output_port & 0x20) == 0))
> +        {
> +                vcpu_deassert_pic_irq(kbc.vm_id, 0, KBC_AUXIRQ);
> +        }
> +}
> +
> +/*
> + * i8042_perform_twobyte_command
> + *
> + * Handles the two-byte command (one byte of command and
> + *  one byte of data) after the data has been received.
> + */
> +static void
> +i8042_perform_twobyte_command()
                                ^
Functions that take no arguments should have void here.

> +{
> +     // The command is in kbc.command and the argument
> +     //  is in kbc.data.
> +
> +     switch(kbc.command)
> +     {
> +     case 0x60 ... 0x7F: // KBC_RAMWRITE
> +             kbc.internal_ram[kbc.command - 0x60] = kbc.data;
> +             break;
> +     case KBC_CMDWOUT:
> +             i8042_set_output_port(kbc.data);
> +             break;
> +     case KBC_KBDECHO:
> +             i8042_set_output_port(kbc.output_port|0x10);
> +             break;
> +     case KBC_AUXECHO:
> +             i8042_set_output_port(kbc.output_port|0x20);
> +             break;
> +     case KBC_AUXWRITE:
> +             // No auxiliary device for this to go to.
> +             break;
> +             }
> +
> +     kbc.awaiting_argbyte = 0;
> +}
> +
> +/*
> + * i8042_process_command
> + *
> + * Handles the command byte currently in the KBC's command
> + *  register, setting the data register accordingly.
> + */
> +static void
> +i8042_process_command()

Same.

> +{
> +#define SET_DIB_BIT (kbc.status |= KBS_DIB)
> +     kbc.awaiting_argbyte = 0;
> +     
> +     switch(kbc.command)
> +     {
> +     case 0x20 ... 0x3F: // KBC_RAMREAD
> +             kbc.data = kbc.internal_ram[kbc.command - 0x20];
> +             SET_DIB_BIT;
> +             break;
> +     case 0x60 ... 0x7F: // KBC_RAMWRITE
> +     case KBC_CMDWOUT:
> +     case KBC_KBDECHO:
> +     case KBC_AUXECHO:
> +     case KBC_AUXWRITE:
> +             kbc.awaiting_argbyte = 1;
> +             break;
> +     case KBC_AUXENABLE:
> +             kbc.ps2_enabled[KBC_AUXPORT] = 1;
> +             break;
> +     case KBC_AUXDISABLE:
> +             kbc.ps2_enabled[KBC_AUXPORT] = 0;
> +             break;
> +     case KBC_AUXTEST:
> +             kbc.data = 0;
> +             SET_DIB_BIT;
> +             break;
> +     case KBC_KBDTEST:
> +             kbc.data = 0;
> +             SET_DIB_BIT;
> +             break;
> +     case KBC_KBDENABLE:
> +             kbc.ps2_enabled[KBC_KBDPORT] = 1;
> +             break;
> +     case KBC_KBDDISABLE:
> +             kbc.ps2_enabled[KBC_KBDPORT] = 0;
> +             break;  
> +     case KBC_READINPORT:
> +             kbc.data = kbc.input_port;
> +             SET_DIB_BIT;
> +             break;
> +     case KBC_POLLINLO:
> +             kbc.status &= 0x0F;
> +             kbc.status |= (kbc.input_port << 4);
> +             break;
> +     case KBC_POLLINHI:
> +             kbc.status &= 0x0F;
> +             kbc.status |= (kbc.input_port & 0xF0);
> +             break;
> +     case KBC_CMDROUT:
> +             kbc.data = kbc.output_port;
> +             SET_DIB_BIT;
> +             break;
> +     case KBC_SELFTEST:
> +             kbc.data = 0x55;
> +             SET_DIB_BIT;
> +             break;
> +     case KBC_PULSE0:
> +             kbc.output_port &= 0xFE;
> +             break;
> +     case KBC_PULSE1:
> +     case KBC_PULSE2:
> +     case KBC_PULSE3:
> +             // Ignore these commands.
> +             break;
> +     }
> +}
> +
> +/*
> + * i8042_read_status_register 
> + *
> + * Handles status register reads.
> + * 
> + * Parameters:
> + *
> + * Return value:
> + *  Value of the KBC status register.
> + */
> +static uint8_t
> +i8042_read_status_register()
> +{

Same.

> +     return kbc.status;
> +}
> +
> +/*
> + * i8042_read_data_register
> + *
> + * Reads the data register of the KBC.
> + *
> + * Parameters:
> + *
> + * Return value:
> + *  Value of the KBC data register.
> + */
> +static uint8_t
> +i8042_read_data_register()
> +{

And here.

> +     // Clear the output buffer full bit.
> +     kbc.status &= 0xFE;
> +
> +     return kbc.data;
> +}
> +
> +/*
> + * i8042_write_command_register
> + *
> + * Writes a byte to the 8042 command register.
> + *
> + * Parameters:
> + *  data: The byte to write.
> + */
> +static void
> +i8042_write_command_register(uint8_t data)
> +{
> +     kbc.command = data;
> +     i8042_process_command();
> +}
> +
> +/*
> + * i8042_write_data_register
> + *
> + * Write a byte to the KBC data register.
> + *
> + * Parameters:
> + *  data: The byte to write.
> + */
> +static void
> +i8042_write_data_register(uint8_t data)
> +{    
> +     kbc.data = data;
> +
> +     if(kbc.awaiting_argbyte)
> +     {
> +             i8042_perform_twobyte_command();
> +     }
> +}
> +
> +/*
> + * i8042_io_read
> + *
> + * Handles emulated reads of the KBC's I/O ports.
> + *
> + * Parameters:
> + *  vei:
> + *
> + * Return value:
> + *  The byte read from the addressed port.
> + *
> + */
> +static uint8_t
> +i8042_io_read(struct vm_exit *vei)
> +{            
> +     uint16_t port = vei->vei.vei_port;
> +     uint8_t rv;
> +             
> +     mutex_lock(&kbc_mtx);
> +     
> +     switch(port)
> +     {
> +     case KBC_CMD:
> +             rv = i8042_read_status_register();;
> +             break;
> +     case KBC_DATA:
> +             rv = i8042_read_data_register();
> +             break;
> +     default:
> +             fatal("%s: invalid port 0x%x", __func__, port);
> +     }
> +
> +     mutex_unlock(&kbc_mtx);
> +     return(rv);
> +}
> +
> +/*
> + * i8042_io_write
> + *
> + * Handles writes to the emulated KBC device, setting the
> + *  value of the selected register.
> + *
> + * Parameters:
> + *  vei:
> + *
> + */
> +static void
> +i8042_io_write(struct vm_exit *vei)
> +{
> +     uint16_t port = vei->vei.vei_port;
> +     uint32_t data;
> +
> +     get_input_data(vei, &data);
> +
> +     mutex_lock(&kbc_mtx);
> +     switch(port)
> +     {
> +     case KBC_DATA:
> +             i8042_write_data_register(data);
> +             break;
> +     case KBC_CMD:
> +             i8042_write_command_register(data);
> +             break;
> +     default:
> +             fatal("%s: invalid port 0x%x", __func__, port);
> +     }
> +
> +     // Have we deasserted the reset line?
> +     if((kbc.output_port & 0x01) == 0)
> +     {
> +             // TODO: Reset the CPU.
> +     }
> +     
> +     mutex_unlock(&kbc_mtx);
> +}
> +
> +/*
> + * vcpu_exit_i8042
> + *
> + * Handles the 0x60 and 0x64 i8042 KBC in/out exits.
> + *
> + * Parameters:
> + *  vrp: vm run parameters containing exit information for the I/O
> + *   instruction being performed.
> + *
> + * Return value:
> + *  0xFF (?)
> + *
> + */
> +uint8_t
> +vcpu_exit_i8042(struct vm_run_params *vrp)
> +{
> +     struct vm_exit *vei = vrp->vrp_exit;
> +
> +     if(vei->vei.vei_dir == VEI_DIR_OUT)
> +     {
> +             i8042_io_write(vei);
> +             }
> +     else
> +     {
> +             set_return_data(vei, i8042_io_read(vei));
> +     }
> +
> +     return (0xFF);
> +}
> +
> +int
> +i8042_restore(int fd)
> +{
> +     log_debug("%s: restoring KBC", __func__);
> +     if(atomicio(read, fd, &kbc, sizeof(kbc)) != sizeof(kbc)) {
> +             log_warnx("%s: error reading KBC from fd", __func__);
> +             return (-1);
> +     }
> +
> +     if(pthread_mutex_init(&kbc_mtx, NULL) != 0)
> +             fatalx("unable to create kbc mutex");
> +
> +     return (0);
> +}
> +
> +int
> +i8042_dump(int fd)
> +{
> +     log_debug("%s: sending KBC", __func__);
> +     if(atomicio(vwrite, fd, &kbc, sizeof(kbc)) != sizeof(kbc)) {
> +             log_warnx("%s: error writing KBC to fd", __func__);
> +             return (-1);
> +     }
> +
> +     return (0);
> +     
> +}
> diff --git a/usr.sbin/vmd/i8042.h b/usr.sbin/vmd/i8042.h
> new file mode 100644
> index 00000000000..67af8f54f2a
> --- /dev/null
> +++ b/usr.sbin/vmd/i8042.h
> @@ -0,0 +1,45 @@
> +/* $OpenBSD: i8042.c,v 1.00 2019/05/25 18:18:00 rohl Exp $ */

:-)

> +/*
> + * Copyright (c) 2019 Katherine Rohl <luig...@gmail.com>
> + *
> + * Permission to use, copy, modify, and distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + */
> +
> +/*
> + * Emulated i8042 (keyboard controller, based on 8048)
> + */
> +
> +#define KBC_DEFINPORT  0xB0  /* Default input port value. */
> +#define KBC_DEFOUTPORT 0x01  /* Default output port value. */
> +
> +#define KBC_KBDPORT 0                /* Keyboard is port 0. */
> +#define KBC_AUXPORT 1                /* Auxiliary is port 1. */
> +
> +#define KBC_KBDIRQ   1               /* Keyboard IRQ.  */
> +#define KBC_AUXIRQ  12               /* Secondary IRQ. */
> +
> +#define KBC_DATA 0x60                /* Data is I/O port 60h. */
> +#define KBC_CMD  0x64                /* Command is I/O port 64h. */
> +
> +/* Commands that aren't useful for public includes. */
> +#define KBC_READINPORT       0xC0    /* Read the input port, place in data. 
> */
> +#define KBC_POLLINLO 0xC1    /* Poll low 4 bits of input, place in status. */
> +#define KBC_POLLINHI 0xC2    /* Poll high 4 bits of input, place in status. 
> */
> +#define KBC_CMDROUT  0xD0    /* Read the output port. */
> +
> +uint8_t vcpu_exit_i8042(struct vm_run_params *);
> +
> +void i8042_init(uint32_t);
> +
> +int i8042_restore(int);
> +int i8042_dump(int);
> diff --git a/usr.sbin/vmd/vm.c b/usr.sbin/vmd/vm.c
> index 72b2e379ac3..35adf25c1cf 100644
> --- a/usr.sbin/vmd/vm.c
> +++ b/usr.sbin/vmd/vm.c
> @@ -61,6 +61,7 @@
>  #include "i8259.h"
>  #include "ns8250.h"
>  #include "mc146818.h"
> +#include "i8042.h"
>  #include "fw_cfg.h"
>  #include "atomicio.h"
>  
> @@ -949,6 +950,11 @@ init_emulated_hw(struct vmop_create_params *vmc, int 
> child_cdrom,
>       ioports_map[ELCR0] = vcpu_exit_elcr;
>       ioports_map[ELCR1] = vcpu_exit_elcr;
>  
> +     /* Init i8042 keyboard controller */
> +     i8042_init(vcp->vcp_id);
> +     ioports_map[0x64] = vcpu_exit_i8042;
> +     ioports_map[0x60] = vcpu_exit_i8042;
> +     
>       /* Init ns8250 UART */
>       ns8250_init(con_fd, vcp->vcp_id);
>       for (i = COM1_DATA; i <= COM1_SCR; i++)
> @@ -1002,6 +1008,11 @@ restore_emulated_hw(struct vm_create_params *vcp, int 
> fd,
>       ioports_map[IO_ICU2] = vcpu_exit_i8259;
>       ioports_map[IO_ICU2 + 1] = vcpu_exit_i8259;
>  
> +     /* Init i8042 keyboard controller */
> +     i8042_restore(fd);
> +     ioports_map[0x64] = vcpu_exit_i8042;
> +     ioports_map[0x60]= vcpu_exit_i8042;
> +     
>       /* Init ns8250 UART */
>       ns8250_restore(fd, con_fd, vcp->vcp_id);
>       for (i = COM1_DATA; i <= COM1_SCR; i++)
> @@ -1291,7 +1302,7 @@ vcpu_run_loop(void *arg)
>  
>       for (;;) {
>               ret = pthread_mutex_lock(&vcpu_run_mtx[n]);
> -
> +             
>               if (ret) {
>                       log_warnx("%s: can't lock vcpu run mtx (%d)",
>                           __func__, (int)ret);
> 

Reply via email to