1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2013 Roger Pau Monn�� <roger.pau@citrix.com> 5 * All rights reserved. 6 * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org> 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 AND CONTRIBUTORS ``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/cdefs.h> 31__FBSDID("$FreeBSD$"); 32 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/bus.h> 36#include <sys/kernel.h> 37#include <sys/smp.h> 38#include <sys/pcpu.h> 39#include <vm/vm.h> 40#include <vm/pmap.h> 41 42#include <machine/intr_machdep.h> 43#include <x86/apicvar.h> 44 45#include <machine/cpu.h> 46#include <machine/smp.h> 47#include <machine/md_var.h> 48 49#include <xen/xen-os.h> 50#include <xen/xen_intr.h> 51#include <xen/hypervisor.h> 52 53#include <xen/interface/vcpu.h> 54 55#include <contrib/dev/acpica/include/acpi.h> 56#include <contrib/dev/acpica/include/aclocal.h> 57#include <contrib/dev/acpica/include/actables.h> 58 59#include <dev/acpica/acpivar.h> 60 61static int xenpv_probe(void); 62static int xenpv_probe_cpus(void); 63static int xenpv_setup_local(void); 64static int xenpv_setup_io(void); 65 66static ACPI_TABLE_MADT *madt; 67static vm_paddr_t madt_physaddr; 68static vm_offset_t madt_length; 69 70static struct apic_enumerator xenpv_enumerator = { 71 .apic_name = "Xen PV", 72 .apic_probe = xenpv_probe, 73 .apic_probe_cpus = xenpv_probe_cpus, 74 .apic_setup_local = xenpv_setup_local, 75 .apic_setup_io = xenpv_setup_io 76}; 77 78/*--------------------- Helper functions to parse MADT -----------------------*/ 79 80/* 81 * Parse an interrupt source override for an ISA interrupt. 82 */ 83static void 84madt_parse_interrupt_override(ACPI_MADT_INTERRUPT_OVERRIDE *intr) 85{ 86 enum intr_trigger trig; 87 enum intr_polarity pol; 88 int ret; 89 90 if (acpi_quirks & ACPI_Q_MADT_IRQ0 && intr->SourceIrq == 0 && 91 intr->GlobalIrq == 2) { 92 if (bootverbose) 93 printf("MADT: Skipping timer override\n"); 94 return; 95 } 96 97 madt_parse_interrupt_values(intr, &trig, &pol); 98 99 /* Remap the IRQ if it is mapped to a different interrupt vector. */ 100 if (intr->SourceIrq != intr->GlobalIrq && intr->GlobalIrq > 15 && 101 intr->SourceIrq == AcpiGbl_FADT.SciInterrupt) 102 /* 103 * If the SCI is remapped to a non-ISA global interrupt, 104 * then override the vector we use to setup. 105 */ 106 acpi_OverrideInterruptLevel(intr->GlobalIrq); 107 108 /* Register the IRQ with the polarity and trigger mode found. */ 109 ret = xen_register_pirq(intr->GlobalIrq, trig, pol); 110 if (ret != 0) 111 panic("Unable to register interrupt override"); 112} 113 114/* 115 * Call the handler routine for each entry in the MADT table. 116 */ 117static void 118madt_walk_table(acpi_subtable_handler *handler, void *arg) 119{ 120 121 acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length, 122 handler, arg); 123} 124 125/* 126 * Parse interrupt entries. 127 */ 128static void 129madt_parse_ints(ACPI_SUBTABLE_HEADER *entry, void *arg __unused) 130{ 131 132 if (entry->Type == ACPI_MADT_TYPE_INTERRUPT_OVERRIDE) 133 madt_parse_interrupt_override( 134 (ACPI_MADT_INTERRUPT_OVERRIDE *)entry); 135} 136 137/*---------------------------- Xen PV enumerator -----------------------------*/ 138 139/* 140 * This enumerator will only be registered on PVH 141 */ 142static int 143xenpv_probe(void) 144{ 145 return (0); 146} 147 148/* 149 * Test each possible vCPU in order to find the number of vCPUs 150 */ 151static int 152xenpv_probe_cpus(void) 153{ 154#ifdef SMP 155 int i, ret; 156 157 for (i = 0; i < MAXCPU && (i * 2) < MAX_APIC_ID; i++) { 158 ret = HYPERVISOR_vcpu_op(VCPUOP_is_up, i, NULL); 159 mp_ncpus = min(mp_ncpus + 1, MAXCPU); 160 } 161 mp_maxid = mp_ncpus - 1; 162 max_apic_id = mp_ncpus * 2; 163#endif 164 return (0); 165} 166 167/* 168 * Initialize the vCPU id of the BSP 169 */ 170static int 171xenpv_setup_local(void) 172{ 173#ifdef SMP 174 int i, ret; 175 176 for (i = 0; i < MAXCPU && (i * 2) < MAX_APIC_ID; i++) { 177 ret = HYPERVISOR_vcpu_op(VCPUOP_is_up, i, NULL); 178 if (ret >= 0) 179 lapic_create((i * 2), (i == 0)); 180 } 181#endif 182 183 PCPU_SET(vcpu_id, 0); 184 lapic_init(0); 185 return (0); 186} 187 188/* 189 * On PVH guests there's no IO APIC 190 */ 191static int 192xenpv_setup_io(void) 193{ 194 195 if (xen_initial_domain()) { 196 /* 197 * NB: we could iterate over the MADT IOAPIC entries in order 198 * to figure out the exact number of IOAPIC interrupts, but 199 * this is legacy code so just keep using the previous 200 * behaviour and assume a maximum of 256 interrupts. 201 */ 202 num_io_irqs = max(MINIMUM_MSI_INT - 1, num_io_irqs); 203 204 acpi_SetDefaultIntrModel(ACPI_INTR_APIC); 205 } 206 return (0); 207} 208 209void 210xenpv_register_pirqs(struct pic *pic __unused) 211{ 212 unsigned int i; 213 int ret; 214 215 /* Map MADT */ 216 madt_physaddr = acpi_find_table(ACPI_SIG_MADT); 217 madt = acpi_map_table(madt_physaddr, ACPI_SIG_MADT); 218 madt_length = madt->Header.Length; 219 220 /* Try to initialize ACPI so that we can access the FADT. */ 221 ret = acpi_Startup(); 222 if (ACPI_FAILURE(ret)) { 223 printf("MADT: ACPI Startup failed with %s\n", 224 AcpiFormatException(ret)); 225 printf("Try disabling either ACPI or apic support.\n"); 226 panic("Using MADT but ACPI doesn't work"); 227 } 228 229 /* Run through the table to see if there are any overrides. */ 230 madt_walk_table(madt_parse_ints, NULL); 231 232 /* 233 * If there was not an explicit override entry for the SCI, 234 * force it to use level trigger and active-low polarity. 235 */ 236 if (!madt_found_sci_override) { 237 printf( 238"MADT: Forcing active-low polarity and level trigger for SCI\n"); 239 ret = xen_register_pirq(AcpiGbl_FADT.SciInterrupt, 240 INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW); 241 if (ret != 0) 242 panic("Unable to register SCI IRQ"); 243 } 244 245 /* Register legacy ISA IRQs */ 246 for (i = 1; i < 16; i++) { 247 if (intr_lookup_source(i) != NULL) 248 continue; 249 ret = xen_register_pirq(i, INTR_TRIGGER_EDGE, 250 INTR_POLARITY_LOW); 251 if (ret != 0 && bootverbose) 252 printf("Unable to register legacy IRQ#%u: %d\n", i, 253 ret); 254 } 255} 256 257static void 258xenpv_register(void *dummy __unused) 259{ 260 if (xen_pv_domain()) { 261 apic_register_enumerator(&xenpv_enumerator); 262 } 263} 264SYSINIT(xenpv_register, SI_SUB_TUNABLES - 1, SI_ORDER_FIRST, xenpv_register, NULL); 265