1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2022 Oxide Computer Company
14  */
15 
16 #include "payload_common.h"
17 #include "payload_utils.h"
18 #include "test_defs.h"
19 
20 #define	LAPIC_OFF_SVR	0xf0
21 #define	LAPIC_OFF_TIMER_ICR	0x380
22 #define	LAPIC_OFF_TIMER_CCR	0x390
23 #define	LAPIC_OFF_TIMER_DCR	0x3e0
24 
25 
26 #define	LAPIC_SVR_ENABLE	0x100
27 
28 static void
write_vlapic(uint_t reg,uint32_t value)29 write_vlapic(uint_t reg, uint32_t value)
30 {
31 	volatile uint32_t *ptr = (uint32_t *)(MMIO_LAPIC_BASE + reg);
32 	*ptr = value;
33 }
34 
35 static uint32_t
read_vlapic(uint_t reg)36 read_vlapic(uint_t reg)
37 {
38 	volatile uint32_t *ptr = (uint32_t *)(MMIO_LAPIC_BASE + reg);
39 	return (*ptr);
40 }
41 
42 static uint32_t
divisor_to_dcr(uint32_t inp)43 divisor_to_dcr(uint32_t inp)
44 {
45 	switch (inp) {
46 	case 1:
47 		return (0xb);
48 	case 2:
49 		return (0x0);
50 	case 4:
51 		return (0x1);
52 	case 8:
53 		return (0x2);
54 	case 16:
55 		return (0x3);
56 	case 32:
57 		return (0x8);
58 	case 64:
59 		return (0x9);
60 	case 128:
61 		return (0xa);
62 	default:
63 		/* fail immediate if divisor is out of range */
64 		outl(IOP_TEST_VALUE, 1);
65 		return (0xff);
66 	}
67 }
68 
69 
70 void
start(void)71 start(void)
72 {
73 	write_vlapic(LAPIC_OFF_SVR, LAPIC_SVR_ENABLE);
74 
75 	/* loop for as long as the host wants */
76 	for (;;) {
77 		uint32_t divisor;
78 		uint32_t start, end;
79 
80 		divisor = inl(IOP_TEST_PARAM);
81 		write_vlapic(LAPIC_OFF_TIMER_DCR, divisor_to_dcr(divisor));
82 		write_vlapic(LAPIC_OFF_TIMER_ICR, 0xffffffff);
83 
84 		start = read_vlapic(LAPIC_OFF_TIMER_CCR);
85 		outl(IOP_TEST_VALUE, start);
86 
87 		uint32_t target = start - LAPIC_TARGET_TICKS;
88 		do {
89 			end = read_vlapic(LAPIC_OFF_TIMER_CCR);
90 			/* wait for enough ticks to pass */
91 		} while (end > target);
92 		outl(IOP_TEST_VALUE, end);
93 	}
94 }
95