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_LVT_TIMER 0x320 22 #define LAPIC_OFF_TIMER_ICR 0x380 23 #define LAPIC_OFF_TIMER_CCR 0x390 24 #define LAPIC_OFF_TIMER_DCR 0x3e0 25 26 #define LAPIC_LVT_MASKED (1 << 16) 27 #define LAPIC_LVT_PERIODIC (1 << 17) 28 29 30 #define LAPIC_SVR_ENABLE 0x100 31 32 static void 33 write_vlapic(uint_t reg, uint32_t value) 34 { 35 volatile uint32_t *ptr = (uint32_t *)(MMIO_LAPIC_BASE + reg); 36 *ptr = value; 37 } 38 39 static uint32_t 40 read_vlapic(uint_t reg) 41 { 42 volatile uint32_t *ptr = (uint32_t *)(MMIO_LAPIC_BASE + reg); 43 return (*ptr); 44 } 45 46 static uint32_t 47 divisor_to_dcr(uint32_t inp) 48 { 49 switch (inp) { 50 case 1: 51 return (0xb); 52 case 2: 53 return (0x0); 54 case 4: 55 return (0x1); 56 case 8: 57 return (0x2); 58 case 16: 59 return (0x3); 60 case 32: 61 return (0x8); 62 case 64: 63 return (0x9); 64 case 128: 65 return (0xa); 66 default: 67 /* fail immediate if divisor is out of range */ 68 outl(IOP_TEST_VALUE, 1); 69 return (0xff); 70 } 71 } 72 73 74 void 75 start(void) 76 { 77 write_vlapic(LAPIC_OFF_SVR, LAPIC_SVR_ENABLE); 78 79 /* 80 * Configure the LAPIC timer for periodic operation, but leave the 81 * interrupt itself masked. 82 */ 83 write_vlapic(LAPIC_OFF_LVT_TIMER, 84 LAPIC_LVT_MASKED | LAPIC_LVT_PERIODIC); 85 86 /* loop for as long as the host wants */ 87 for (;;) { 88 const uint16_t divisor = inw(IOP_TEST_PARAM0); 89 const uint16_t loop_count = inw(IOP_TEST_PARAM1); 90 91 write_vlapic(LAPIC_OFF_TIMER_DCR, divisor_to_dcr(divisor)); 92 write_vlapic(LAPIC_OFF_TIMER_ICR, LAPIC_TARGET_TICKS); 93 94 uint32_t start, end, count = 0; 95 start = read_vlapic(LAPIC_OFF_TIMER_CCR); 96 outl(IOP_TEST_VALUE, start); 97 98 uint32_t prev = start; 99 do { 100 end = read_vlapic(LAPIC_OFF_TIMER_CCR); 101 102 /* timer period rolled over */ 103 if (end > prev) { 104 count++; 105 } 106 prev = end; 107 } while (count < loop_count); 108 outl(IOP_TEST_VALUE, end); 109 } 110 } 111