pvcpu_enum.c revision 367457
1/*- 2 * Copyright (c) 2013 Roger Pau Monn�� <roger.pau@citrix.com> 3 * All rights reserved. 4 * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: stable/11/sys/x86/xen/pvcpu_enum.c 367457 2020-11-07 18:10:59Z dim $"); 30 31#include <sys/param.h> 32#include <sys/systm.h> 33#include <sys/bus.h> 34#include <sys/kernel.h> 35#include <sys/smp.h> 36#include <sys/pcpu.h> 37#include <vm/vm.h> 38#include <vm/pmap.h> 39 40#include <machine/intr_machdep.h> 41#include <x86/apicvar.h> 42 43#include <machine/cpu.h> 44#include <machine/smp.h> 45 46#include <xen/xen-os.h> 47#include <xen/xen_intr.h> 48#include <xen/hypervisor.h> 49 50#include <xen/interface/vcpu.h> 51 52#include <contrib/dev/acpica/include/acpi.h> 53#include <contrib/dev/acpica/include/aclocal.h> 54#include <contrib/dev/acpica/include/actables.h> 55 56#include <dev/acpica/acpivar.h> 57 58static int xenpv_probe(void); 59static int xenpv_probe_cpus(void); 60static int xenpv_setup_local(void); 61static int xenpv_setup_io(void); 62 63static ACPI_TABLE_MADT *madt; 64static vm_paddr_t madt_physaddr; 65static vm_offset_t madt_length; 66 67static struct apic_enumerator xenpv_enumerator = { 68 "Xen PV", 69 xenpv_probe, 70 xenpv_probe_cpus, 71 xenpv_setup_local, 72 xenpv_setup_io 73}; 74 75/*--------------------- Helper functions to parse MADT -----------------------*/ 76 77/* 78 * Parse an interrupt source override for an ISA interrupt. 79 */ 80static void 81madt_parse_interrupt_override(ACPI_MADT_INTERRUPT_OVERRIDE *intr) 82{ 83 enum intr_trigger trig; 84 enum intr_polarity pol; 85 int ret; 86 87 if (acpi_quirks & ACPI_Q_MADT_IRQ0 && intr->SourceIrq == 0 && 88 intr->GlobalIrq == 2) { 89 if (bootverbose) 90 printf("MADT: Skipping timer override\n"); 91 return; 92 } 93 94 madt_parse_interrupt_values(intr, &trig, &pol); 95 96 /* Remap the IRQ if it is mapped to a different interrupt vector. */ 97 if (intr->SourceIrq != intr->GlobalIrq && intr->GlobalIrq > 15 && 98 intr->SourceIrq == AcpiGbl_FADT.SciInterrupt) 99 /* 100 * If the SCI is remapped to a non-ISA global interrupt, 101 * then override the vector we use to setup. 102 */ 103 acpi_OverrideInterruptLevel(intr->GlobalIrq); 104 105 /* Register the IRQ with the polarity and trigger mode found. */ 106 ret = xen_register_pirq(intr->GlobalIrq, trig, pol); 107 if (ret != 0) 108 panic("Unable to register interrupt override"); 109} 110 111/* 112 * Call the handler routine for each entry in the MADT table. 113 */ 114static void 115madt_walk_table(acpi_subtable_handler *handler, void *arg) 116{ 117 118 acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length, 119 handler, arg); 120} 121 122/* 123 * Parse interrupt entries. 124 */ 125static void 126madt_parse_ints(ACPI_SUBTABLE_HEADER *entry, void *arg __unused) 127{ 128 129 if (entry->Type == ACPI_MADT_TYPE_INTERRUPT_OVERRIDE) 130 madt_parse_interrupt_override( 131 (ACPI_MADT_INTERRUPT_OVERRIDE *)entry); 132} 133 134/*---------------------------- Xen PV enumerator -----------------------------*/ 135 136/* 137 * This enumerator will only be registered on PVH 138 */ 139static int 140xenpv_probe(void) 141{ 142 return (0); 143} 144 145/* 146 * Test each possible vCPU in order to find the number of vCPUs 147 */ 148static int 149xenpv_probe_cpus(void) 150{ 151#ifdef SMP 152 int i, ret; 153 154 for (i = 0; i < MAXCPU; i++) { 155 ret = HYPERVISOR_vcpu_op(VCPUOP_is_up, i, NULL); 156 if (ret >= 0) 157 lapic_create((i * 2), (i == 0)); 158 } 159#endif 160 return (0); 161} 162 163/* 164 * Initialize the vCPU id of the BSP 165 */ 166static int 167xenpv_setup_local(void) 168{ 169 PCPU_SET(vcpu_id, 0); 170 lapic_init(0); 171 return (0); 172} 173 174/* 175 * On PVH guests there's no IO APIC 176 */ 177static int 178xenpv_setup_io(void) 179{ 180 181 if (xen_initial_domain()) { 182 /* 183 * NB: we could iterate over the MADT IOAPIC entries in order 184 * to figure out the exact number of IOAPIC interrupts, but 185 * this is legacy code so just keep using the previous 186 * behaviour and assume a maximum of 256 interrupts. 187 */ 188 num_io_irqs = max(MINIMUM_MSI_INT - 1, num_io_irqs); 189 190 acpi_SetDefaultIntrModel(ACPI_INTR_APIC); 191 } 192 return (0); 193} 194 195void 196xenpv_register_pirqs(struct pic *pic __unused) 197{ 198 unsigned int i; 199 int ret; 200 201 /* Map MADT */ 202 madt_physaddr = acpi_find_table(ACPI_SIG_MADT); 203 madt = acpi_map_table(madt_physaddr, ACPI_SIG_MADT); 204 madt_length = madt->Header.Length; 205 206 /* Try to initialize ACPI so that we can access the FADT. */ 207 ret = acpi_Startup(); 208 if (ACPI_FAILURE(ret)) { 209 printf("MADT: ACPI Startup failed with %s\n", 210 AcpiFormatException(ret)); 211 printf("Try disabling either ACPI or apic support.\n"); 212 panic("Using MADT but ACPI doesn't work"); 213 } 214 215 /* Run through the table to see if there are any overrides. */ 216 madt_walk_table(madt_parse_ints, NULL); 217 218 /* 219 * If there was not an explicit override entry for the SCI, 220 * force it to use level trigger and active-low polarity. 221 */ 222 if (!madt_found_sci_override) { 223 printf( 224"MADT: Forcing active-low polarity and level trigger for SCI\n"); 225 ret = xen_register_pirq(AcpiGbl_FADT.SciInterrupt, 226 INTR_TRIGGER_LEVEL, INTR_POLARITY_LOW); 227 if (ret != 0) 228 panic("Unable to register SCI IRQ"); 229 } 230 231 /* Register legacy ISA IRQs */ 232 for (i = 1; i < 16; i++) { 233 if (intr_lookup_source(i) != NULL) 234 continue; 235 ret = xen_register_pirq(i, INTR_TRIGGER_EDGE, 236 INTR_POLARITY_LOW); 237 if (ret != 0 && bootverbose) 238 printf("Unable to register legacy IRQ#%u: %d\n", i, 239 ret); 240 } 241} 242 243static void 244xenpv_register(void *dummy __unused) 245{ 246 if (xen_pv_domain()) { 247 apic_register_enumerator(&xenpv_enumerator); 248 } 249} 250SYSINIT(xenpv_register, SI_SUB_TUNABLES - 1, SI_ORDER_FIRST, xenpv_register, NULL); 251 252/* 253 * Setup per-CPU vCPU IDs 254 */ 255static void 256xenpv_set_ids(void *dummy) 257{ 258 struct pcpu *pc; 259 int i; 260 261 CPU_FOREACH(i) { 262 pc = pcpu_find(i); 263 pc->pc_vcpu_id = i; 264 } 265} 266SYSINIT(xenpv_set_ids, SI_SUB_CPU, SI_ORDER_MIDDLE, xenpv_set_ids, NULL); 267