xref: /freebsd/usr.sbin/bhyve/amd64/atkbdc.c (revision 02e9120893770924227138ba49df1edb3896112a)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
5  * Copyright (c) 2015 Nahanni Systems Inc.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/types.h>
31 
32 #include <machine/vmm.h>
33 #include <machine/vmm_snapshot.h>
34 
35 #include <vmmapi.h>
36 
37 #include <assert.h>
38 #include <errno.h>
39 #include <stdbool.h>
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <pthread.h>
45 #include <pthread_np.h>
46 
47 #include "acpi.h"
48 #include "atkbdc.h"
49 #include "inout.h"
50 #include "pci_emul.h"
51 #include "pci_irq.h"
52 #include "pci_lpc.h"
53 #include "ps2kbd.h"
54 #include "ps2mouse.h"
55 
56 #define	KBD_DATA_PORT		0x60
57 
58 #define	KBD_STS_CTL_PORT	0x64
59 
60 #define	KBDC_RESET		0xfe
61 
62 #define	KBD_DEV_IRQ		1
63 #define	AUX_DEV_IRQ		12
64 
65 /* controller commands */
66 #define	KBDC_SET_COMMAND_BYTE	0x60
67 #define	KBDC_GET_COMMAND_BYTE	0x20
68 #define	KBDC_DISABLE_AUX_PORT	0xa7
69 #define	KBDC_ENABLE_AUX_PORT	0xa8
70 #define	KBDC_TEST_AUX_PORT	0xa9
71 #define	KBDC_TEST_CTRL		0xaa
72 #define	KBDC_TEST_KBD_PORT	0xab
73 #define	KBDC_DISABLE_KBD_PORT	0xad
74 #define	KBDC_ENABLE_KBD_PORT	0xae
75 #define	KBDC_READ_INPORT	0xc0
76 #define	KBDC_READ_OUTPORT	0xd0
77 #define	KBDC_WRITE_OUTPORT	0xd1
78 #define	KBDC_WRITE_KBD_OUTBUF	0xd2
79 #define	KBDC_WRITE_AUX_OUTBUF	0xd3
80 #define	KBDC_WRITE_TO_AUX	0xd4
81 
82 /* controller command byte (set by KBDC_SET_COMMAND_BYTE) */
83 #define	KBD_TRANSLATION		0x40
84 #define	KBD_SYS_FLAG_BIT	0x04
85 #define	KBD_DISABLE_KBD_PORT	0x10
86 #define	KBD_DISABLE_AUX_PORT	0x20
87 #define	KBD_ENABLE_AUX_INT	0x02
88 #define	KBD_ENABLE_KBD_INT	0x01
89 #define	KBD_KBD_CONTROL_BITS	(KBD_DISABLE_KBD_PORT | KBD_ENABLE_KBD_INT)
90 #define	KBD_AUX_CONTROL_BITS	(KBD_DISABLE_AUX_PORT | KBD_ENABLE_AUX_INT)
91 
92 /* controller status bits */
93 #define	KBDS_KBD_BUFFER_FULL	0x01
94 #define KBDS_SYS_FLAG		0x04
95 #define KBDS_CTRL_FLAG		0x08
96 #define	KBDS_AUX_BUFFER_FULL	0x20
97 
98 /* controller output port */
99 #define	KBDO_KBD_OUTFULL	0x10
100 #define	KBDO_AUX_OUTFULL	0x20
101 
102 #define	RAMSZ			32
103 #define	FIFOSZ			15
104 #define	CTRL_CMD_FLAG		0x8000
105 
106 struct kbd_dev {
107 	bool	irq_active;
108 	int	irq;
109 
110 	uint8_t	buffer[FIFOSZ];
111 	int	brd, bwr;
112 	int	bcnt;
113 };
114 
115 struct aux_dev {
116 	bool	irq_active;
117 	int	irq;
118 };
119 
120 struct atkbdc_softc {
121 	struct vmctx *ctx;
122 	pthread_mutex_t mtx;
123 
124 	struct ps2kbd_softc	*ps2kbd_sc;
125 	struct ps2mouse_softc	*ps2mouse_sc;
126 
127 	uint8_t	status;		/* status register */
128 	uint8_t	outport;	/* controller output port */
129 	uint8_t	ram[RAMSZ];	/* byte0 = controller config */
130 
131 	uint32_t curcmd;	/* current command for next byte */
132 	uint32_t  ctrlbyte;
133 
134 	struct kbd_dev kbd;
135 	struct aux_dev aux;
136 };
137 
138 #ifdef BHYVE_SNAPSHOT
139 static struct atkbdc_softc *atkbdc_sc = NULL;
140 #endif
141 
142 static void
143 atkbdc_assert_kbd_intr(struct atkbdc_softc *sc)
144 {
145 	if ((sc->ram[0] & KBD_ENABLE_KBD_INT) != 0) {
146 		sc->kbd.irq_active = true;
147 		vm_isa_pulse_irq(sc->ctx, sc->kbd.irq, sc->kbd.irq);
148 	}
149 }
150 
151 static void
152 atkbdc_assert_aux_intr(struct atkbdc_softc *sc)
153 {
154 	if ((sc->ram[0] & KBD_ENABLE_AUX_INT) != 0) {
155 		sc->aux.irq_active = true;
156 		vm_isa_pulse_irq(sc->ctx, sc->aux.irq, sc->aux.irq);
157 	}
158 }
159 
160 static int
161 atkbdc_kbd_queue_data(struct atkbdc_softc *sc, uint8_t val)
162 {
163 	assert(pthread_mutex_isowned_np(&sc->mtx));
164 
165 	if (sc->kbd.bcnt < FIFOSZ) {
166 		sc->kbd.buffer[sc->kbd.bwr] = val;
167 		sc->kbd.bwr = (sc->kbd.bwr + 1) % FIFOSZ;
168 		sc->kbd.bcnt++;
169 		sc->status |= KBDS_KBD_BUFFER_FULL;
170 		sc->outport |= KBDO_KBD_OUTFULL;
171 	} else {
172 		printf("atkbd data buffer full\n");
173 	}
174 
175 	return (sc->kbd.bcnt < FIFOSZ);
176 }
177 
178 static void
179 atkbdc_kbd_read(struct atkbdc_softc *sc)
180 {
181 	const uint8_t translation[256] = {
182 		0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58,
183 		0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59,
184 		0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a,
185 		0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b,
186 		0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c,
187 		0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d,
188 		0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e,
189 		0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f,
190 		0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60,
191 		0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61,
192 		0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e,
193 		0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76,
194 		0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b,
195 		0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f,
196 		0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45,
197 		0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54,
198 		0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87,
199 		0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
200 		0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
201 		0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
202 		0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
203 		0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
204 		0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
205 		0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
206 		0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
207 		0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
208 		0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
209 		0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
210 		0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
211 		0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
212 		0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
213 		0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
214 	};
215 	uint8_t val;
216 	uint8_t release = 0;
217 
218 	assert(pthread_mutex_isowned_np(&sc->mtx));
219 
220 	if (sc->ram[0] & KBD_TRANSLATION) {
221 		while (ps2kbd_read(sc->ps2kbd_sc, &val) != -1) {
222 			if (val == 0xf0) {
223 				release = 0x80;
224 				continue;
225 			} else {
226 				val = translation[val] | release;
227 			}
228 			atkbdc_kbd_queue_data(sc, val);
229 			break;
230 		}
231 	} else {
232 		while (sc->kbd.bcnt < FIFOSZ) {
233 			if (ps2kbd_read(sc->ps2kbd_sc, &val) != -1)
234 				atkbdc_kbd_queue_data(sc, val);
235 			else
236 				break;
237 		}
238 	}
239 
240 	if (((sc->ram[0] & KBD_DISABLE_AUX_PORT) ||
241 	    ps2mouse_fifocnt(sc->ps2mouse_sc) == 0) && sc->kbd.bcnt > 0)
242 		atkbdc_assert_kbd_intr(sc);
243 }
244 
245 static void
246 atkbdc_aux_poll(struct atkbdc_softc *sc)
247 {
248 	if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0) {
249 		sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
250 		sc->outport |= KBDO_AUX_OUTFULL;
251 		atkbdc_assert_aux_intr(sc);
252 	}
253 }
254 
255 static void
256 atkbdc_kbd_poll(struct atkbdc_softc *sc)
257 {
258 	assert(pthread_mutex_isowned_np(&sc->mtx));
259 
260 	atkbdc_kbd_read(sc);
261 }
262 
263 static void
264 atkbdc_poll(struct atkbdc_softc *sc)
265 {
266 	atkbdc_aux_poll(sc);
267 	atkbdc_kbd_poll(sc);
268 }
269 
270 static void
271 atkbdc_dequeue_data(struct atkbdc_softc *sc, uint8_t *buf)
272 {
273 	assert(pthread_mutex_isowned_np(&sc->mtx));
274 
275 	if (ps2mouse_read(sc->ps2mouse_sc, buf) == 0) {
276 		if (ps2mouse_fifocnt(sc->ps2mouse_sc) == 0) {
277 			if (sc->kbd.bcnt == 0)
278 				sc->status &= ~(KBDS_AUX_BUFFER_FULL |
279 				                KBDS_KBD_BUFFER_FULL);
280 			else
281 				sc->status &= ~(KBDS_AUX_BUFFER_FULL);
282 			sc->outport &= ~KBDO_AUX_OUTFULL;
283 		}
284 
285 		atkbdc_poll(sc);
286 		return;
287 	}
288 
289 	if (sc->kbd.bcnt > 0) {
290 		*buf = sc->kbd.buffer[sc->kbd.brd];
291 		sc->kbd.brd = (sc->kbd.brd + 1) % FIFOSZ;
292 		sc->kbd.bcnt--;
293 		if (sc->kbd.bcnt == 0) {
294 			sc->status &= ~KBDS_KBD_BUFFER_FULL;
295 			sc->outport &= ~KBDO_KBD_OUTFULL;
296 		}
297 
298 		atkbdc_poll(sc);
299 	}
300 
301 	if (ps2mouse_fifocnt(sc->ps2mouse_sc) == 0 && sc->kbd.bcnt == 0) {
302 		sc->status &= ~(KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
303 	}
304 }
305 
306 static int
307 atkbdc_data_handler(struct vmctx *ctx __unused, int in,
308     int port __unused, int bytes, uint32_t *eax, void *arg)
309 {
310 	struct atkbdc_softc *sc;
311 	uint8_t buf;
312 	int retval;
313 
314 	if (bytes != 1)
315 		return (-1);
316 	sc = arg;
317 	retval = 0;
318 
319 	pthread_mutex_lock(&sc->mtx);
320 	if (in) {
321 		sc->curcmd = 0;
322 		if (sc->ctrlbyte != 0) {
323 			*eax = sc->ctrlbyte & 0xff;
324 			sc->ctrlbyte = 0;
325 		} else {
326 			/* read device buffer; includes kbd cmd responses */
327 			atkbdc_dequeue_data(sc, &buf);
328 			*eax = buf;
329 		}
330 
331 		sc->status &= ~KBDS_CTRL_FLAG;
332 		pthread_mutex_unlock(&sc->mtx);
333 		return (retval);
334 	}
335 
336 	if (sc->status & KBDS_CTRL_FLAG) {
337 		/*
338 		 * Command byte for the controller.
339 		 */
340 		switch (sc->curcmd) {
341 		case KBDC_SET_COMMAND_BYTE:
342 			sc->ram[0] = *eax;
343 			if (sc->ram[0] & KBD_SYS_FLAG_BIT)
344 				sc->status |= KBDS_SYS_FLAG;
345 			else
346 				sc->status &= ~KBDS_SYS_FLAG;
347 			break;
348 		case KBDC_WRITE_OUTPORT:
349 			sc->outport = *eax;
350 			break;
351 		case KBDC_WRITE_TO_AUX:
352 			ps2mouse_write(sc->ps2mouse_sc, *eax, 0);
353 			atkbdc_poll(sc);
354 			break;
355 		case KBDC_WRITE_KBD_OUTBUF:
356 			atkbdc_kbd_queue_data(sc, *eax);
357 			break;
358 		case KBDC_WRITE_AUX_OUTBUF:
359 			ps2mouse_write(sc->ps2mouse_sc, *eax, 1);
360 			sc->status |= (KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
361 			atkbdc_aux_poll(sc);
362 			break;
363 		default:
364 			/* write to particular RAM byte */
365 			if (sc->curcmd >= 0x61 && sc->curcmd <= 0x7f) {
366 				int byten;
367 
368 				byten = (sc->curcmd - 0x60) & 0x1f;
369 				sc->ram[byten] = *eax & 0xff;
370 			}
371 			break;
372 		}
373 
374 		sc->curcmd = 0;
375 		sc->status &= ~KBDS_CTRL_FLAG;
376 
377 		pthread_mutex_unlock(&sc->mtx);
378 		return (retval);
379 	}
380 
381 	/*
382 	 * Data byte for the device.
383 	 */
384 	ps2kbd_write(sc->ps2kbd_sc, *eax);
385 	atkbdc_poll(sc);
386 
387 	pthread_mutex_unlock(&sc->mtx);
388 
389 	return (retval);
390 }
391 
392 static int
393 atkbdc_sts_ctl_handler(struct vmctx *ctx, int in,
394     int port __unused, int bytes, uint32_t *eax, void *arg)
395 {
396 	struct atkbdc_softc *sc;
397 	int	error, retval;
398 
399 	if (bytes != 1)
400 		return (-1);
401 
402 	sc = arg;
403 	retval = 0;
404 
405 	pthread_mutex_lock(&sc->mtx);
406 
407 	if (in) {
408 		/* read status register */
409 		*eax = sc->status;
410 		pthread_mutex_unlock(&sc->mtx);
411 		return (retval);
412 	}
413 
414 
415 	sc->curcmd = 0;
416 	sc->status |= KBDS_CTRL_FLAG;
417 	sc->ctrlbyte = 0;
418 
419 	switch (*eax) {
420 	case KBDC_GET_COMMAND_BYTE:
421 		sc->ctrlbyte = CTRL_CMD_FLAG | sc->ram[0];
422 		break;
423 	case KBDC_TEST_CTRL:
424 		sc->ctrlbyte = CTRL_CMD_FLAG | 0x55;
425 		break;
426 	case KBDC_TEST_AUX_PORT:
427 	case KBDC_TEST_KBD_PORT:
428 		sc->ctrlbyte = CTRL_CMD_FLAG | 0;
429 		break;
430 	case KBDC_READ_INPORT:
431 		sc->ctrlbyte = CTRL_CMD_FLAG | 0;
432 		break;
433 	case KBDC_READ_OUTPORT:
434 		sc->ctrlbyte = CTRL_CMD_FLAG | sc->outport;
435 		break;
436 	case KBDC_SET_COMMAND_BYTE:
437 	case KBDC_WRITE_OUTPORT:
438 	case KBDC_WRITE_KBD_OUTBUF:
439 	case KBDC_WRITE_AUX_OUTBUF:
440 		sc->curcmd = *eax;
441 		break;
442 	case KBDC_DISABLE_KBD_PORT:
443 		sc->ram[0] |= KBD_DISABLE_KBD_PORT;
444 		break;
445 	case KBDC_ENABLE_KBD_PORT:
446 		sc->ram[0] &= ~KBD_DISABLE_KBD_PORT;
447 		if (sc->kbd.bcnt > 0)
448 			sc->status |= KBDS_KBD_BUFFER_FULL;
449 		atkbdc_poll(sc);
450 		break;
451 	case KBDC_WRITE_TO_AUX:
452 		sc->curcmd = *eax;
453 		break;
454 	case KBDC_DISABLE_AUX_PORT:
455 		sc->ram[0] |= KBD_DISABLE_AUX_PORT;
456 		ps2mouse_toggle(sc->ps2mouse_sc, 0);
457 		sc->status &= ~(KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL);
458 		sc->outport &= ~KBDS_AUX_BUFFER_FULL;
459 		break;
460 	case KBDC_ENABLE_AUX_PORT:
461 		sc->ram[0] &= ~KBD_DISABLE_AUX_PORT;
462 		ps2mouse_toggle(sc->ps2mouse_sc, 1);
463 		if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0)
464 			sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
465 		break;
466 	case KBDC_RESET:		/* Pulse "reset" line */
467 		error = vm_suspend(ctx, VM_SUSPEND_RESET);
468 		assert(error == 0 || errno == EALREADY);
469 		break;
470 	default:
471 		if (*eax >= 0x21 && *eax <= 0x3f) {
472 			/* read "byte N" from RAM */
473 			int	byten;
474 
475 			byten = (*eax - 0x20) & 0x1f;
476 			sc->ctrlbyte = CTRL_CMD_FLAG | sc->ram[byten];
477 		}
478 		break;
479 	}
480 
481 	pthread_mutex_unlock(&sc->mtx);
482 
483 	if (sc->ctrlbyte != 0) {
484 		sc->status |= KBDS_KBD_BUFFER_FULL;
485 		sc->status &= ~KBDS_AUX_BUFFER_FULL;
486 		atkbdc_assert_kbd_intr(sc);
487 	} else if (ps2mouse_fifocnt(sc->ps2mouse_sc) > 0 &&
488 	           (sc->ram[0] & KBD_DISABLE_AUX_PORT) == 0) {
489 		sc->status |= KBDS_AUX_BUFFER_FULL | KBDS_KBD_BUFFER_FULL;
490 		atkbdc_assert_aux_intr(sc);
491 	} else if (sc->kbd.bcnt > 0 && (sc->ram[0] & KBD_DISABLE_KBD_PORT) == 0) {
492 		sc->status |= KBDS_KBD_BUFFER_FULL;
493 		atkbdc_assert_kbd_intr(sc);
494 	}
495 
496 	return (retval);
497 }
498 
499 void
500 atkbdc_event(struct atkbdc_softc *sc, int iskbd)
501 {
502 	pthread_mutex_lock(&sc->mtx);
503 
504 	if (iskbd)
505 		atkbdc_kbd_poll(sc);
506 	else
507 		atkbdc_aux_poll(sc);
508 	pthread_mutex_unlock(&sc->mtx);
509 }
510 
511 void
512 atkbdc_init(struct vmctx *ctx)
513 {
514 	struct inout_port iop;
515 	struct atkbdc_softc *sc;
516 	int error;
517 
518 	sc = calloc(1, sizeof(struct atkbdc_softc));
519 	sc->ctx = ctx;
520 
521 	pthread_mutex_init(&sc->mtx, NULL);
522 
523 	bzero(&iop, sizeof(struct inout_port));
524 	iop.name = "atkdbc";
525 	iop.port = KBD_STS_CTL_PORT;
526 	iop.size = 1;
527 	iop.flags = IOPORT_F_INOUT;
528 	iop.handler = atkbdc_sts_ctl_handler;
529 	iop.arg = sc;
530 
531 	error = register_inout(&iop);
532 	assert(error == 0);
533 
534 	bzero(&iop, sizeof(struct inout_port));
535 	iop.name = "atkdbc";
536 	iop.port = KBD_DATA_PORT;
537 	iop.size = 1;
538 	iop.flags = IOPORT_F_INOUT;
539 	iop.handler = atkbdc_data_handler;
540 	iop.arg = sc;
541 
542 	error = register_inout(&iop);
543 	assert(error == 0);
544 
545 	pci_irq_reserve(KBD_DEV_IRQ);
546 	sc->kbd.irq = KBD_DEV_IRQ;
547 
548 	pci_irq_reserve(AUX_DEV_IRQ);
549 	sc->aux.irq = AUX_DEV_IRQ;
550 
551 	sc->ps2kbd_sc = ps2kbd_init(sc);
552 	sc->ps2mouse_sc = ps2mouse_init(sc);
553 
554 #ifdef BHYVE_SNAPSHOT
555 	assert(atkbdc_sc == NULL);
556 	atkbdc_sc = sc;
557 #endif
558 }
559 
560 #ifdef BHYVE_SNAPSHOT
561 int
562 atkbdc_snapshot(struct vm_snapshot_meta *meta)
563 {
564 	int ret;
565 
566 	SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->status, meta, ret, done);
567 	SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->outport, meta, ret, done);
568 	SNAPSHOT_BUF_OR_LEAVE(atkbdc_sc->ram,
569 			      sizeof(atkbdc_sc->ram), meta, ret, done);
570 	SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->curcmd, meta, ret, done);
571 	SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->ctrlbyte, meta, ret, done);
572 	SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd, meta, ret, done);
573 
574 	SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd.irq_active, meta, ret, done);
575 	SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd.irq, meta, ret, done);
576 	SNAPSHOT_BUF_OR_LEAVE(atkbdc_sc->kbd.buffer,
577 			      sizeof(atkbdc_sc->kbd.buffer), meta, ret, done);
578 	SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd.brd, meta, ret, done);
579 	SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd.bwr, meta, ret, done);
580 	SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->kbd.bcnt, meta, ret, done);
581 
582 	SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->aux.irq_active, meta, ret, done);
583 	SNAPSHOT_VAR_OR_LEAVE(atkbdc_sc->aux.irq, meta, ret, done);
584 
585 	ret = ps2kbd_snapshot(atkbdc_sc->ps2kbd_sc, meta);
586 	if (ret != 0)
587 		goto done;
588 
589 	ret = ps2mouse_snapshot(atkbdc_sc->ps2mouse_sc, meta);
590 
591 done:
592 	return (ret);
593 }
594 #endif
595 
596 static void
597 atkbdc_dsdt(void)
598 {
599 
600 	dsdt_line("");
601 	dsdt_line("Device (KBD)");
602 	dsdt_line("{");
603 	dsdt_line("  Name (_HID, EisaId (\"PNP0303\"))");
604 	dsdt_line("  Name (_CRS, ResourceTemplate ()");
605 	dsdt_line("  {");
606 	dsdt_indent(2);
607 	dsdt_fixed_ioport(KBD_DATA_PORT, 1);
608 	dsdt_fixed_ioport(KBD_STS_CTL_PORT, 1);
609 	dsdt_fixed_irq(1);
610 	dsdt_unindent(2);
611 	dsdt_line("  })");
612 	dsdt_line("}");
613 
614 	dsdt_line("");
615 	dsdt_line("Device (MOU)");
616 	dsdt_line("{");
617 	dsdt_line("  Name (_HID, EisaId (\"PNP0F13\"))");
618 	dsdt_line("  Name (_CRS, ResourceTemplate ()");
619 	dsdt_line("  {");
620 	dsdt_indent(2);
621 	dsdt_fixed_ioport(KBD_DATA_PORT, 1);
622 	dsdt_fixed_ioport(KBD_STS_CTL_PORT, 1);
623 	dsdt_fixed_irq(12);
624 	dsdt_unindent(2);
625 	dsdt_line("  })");
626 	dsdt_line("}");
627 }
628 LPC_DSDT(atkbdc_dsdt);
629 
630