Commit | Line | Data |
---|---|---|
7010adcd | 1 | // SPDX-License-Identifier: GPL-2.0 |
4a362601 JK |
2 | /* |
3 | * Jailhouse paravirt_ops implementation | |
4 | * | |
5 | * Copyright (c) Siemens AG, 2015-2017 | |
6 | * | |
7 | * Authors: | |
8 | * Jan Kiszka <jan.kiszka@siemens.com> | |
9 | */ | |
10 | ||
87e65d05 | 11 | #include <linux/acpi_pmtmr.h> |
4a362601 | 12 | #include <linux/kernel.h> |
fd498076 | 13 | #include <linux/reboot.h> |
7a56b81c | 14 | #include <linux/serial_8250.h> |
11c8dc41 | 15 | #include <asm/apic.h> |
13c01139 | 16 | #include <asm/io_apic.h> |
0cd39f46 | 17 | #include <asm/acpi.h> |
4a362601 JK |
18 | #include <asm/cpu.h> |
19 | #include <asm/hypervisor.h> | |
0d7c1e22 | 20 | #include <asm/i8259.h> |
cf878e16 | 21 | #include <asm/irqdomain.h> |
a0c01e4b | 22 | #include <asm/pci_x86.h> |
fd498076 | 23 | #include <asm/reboot.h> |
4a362601 | 24 | #include <asm/setup.h> |
89f579ce | 25 | #include <asm/jailhouse_para.h> |
4a362601 | 26 | |
7a56b81c | 27 | static struct jailhouse_setup_data setup_data; |
0935e5f7 | 28 | #define SETUP_DATA_V1_LEN (sizeof(setup_data.hdr) + sizeof(setup_data.v1)) |
7a56b81c | 29 | #define SETUP_DATA_V2_LEN (SETUP_DATA_V1_LEN + sizeof(setup_data.v2)) |
0935e5f7 | 30 | |
e85eb632 | 31 | static unsigned int precalibrated_tsc_khz; |
4a362601 | 32 | |
7a56b81c RR |
33 | static void jailhouse_setup_irq(unsigned int irq) |
34 | { | |
35 | struct mpc_intsrc mp_irq = { | |
36 | .type = MP_INTSRC, | |
37 | .irqtype = mp_INT, | |
38 | .irqflag = MP_IRQPOL_ACTIVE_HIGH | MP_IRQTRIG_EDGE, | |
39 | .srcbusirq = irq, | |
40 | .dstirq = irq, | |
41 | }; | |
42 | mp_save_irq(&mp_irq); | |
43 | } | |
44 | ||
4a362601 JK |
45 | static uint32_t jailhouse_cpuid_base(void) |
46 | { | |
47 | if (boot_cpu_data.cpuid_level < 0 || | |
48 | !boot_cpu_has(X86_FEATURE_HYPERVISOR)) | |
49 | return 0; | |
50 | ||
51 | return hypervisor_cpuid_base("Jailhouse\0\0\0", 0); | |
52 | } | |
53 | ||
54 | static uint32_t __init jailhouse_detect(void) | |
55 | { | |
56 | return jailhouse_cpuid_base(); | |
57 | } | |
58 | ||
e27c4929 | 59 | static void jailhouse_get_wallclock(struct timespec64 *now) |
0d7c1e22 JK |
60 | { |
61 | memset(now, 0, sizeof(*now)); | |
62 | } | |
63 | ||
e85eb632 JK |
64 | static void __init jailhouse_timer_init(void) |
65 | { | |
0935e5f7 | 66 | lapic_timer_period = setup_data.v1.apic_khz * (1000 / HZ); |
e85eb632 JK |
67 | } |
68 | ||
69 | static unsigned long jailhouse_get_tsc(void) | |
70 | { | |
71 | return precalibrated_tsc_khz; | |
72 | } | |
73 | ||
be6d447e TG |
74 | static void __init jailhouse_x2apic_init(void) |
75 | { | |
76 | #ifdef CONFIG_X86_X2APIC | |
77 | if (!x2apic_enabled()) | |
78 | return; | |
79 | /* | |
80 | * We do not have access to IR inside Jailhouse non-root cells. So | |
81 | * we have to run in physical mode. | |
82 | */ | |
83 | x2apic_phys = 1; | |
84 | /* | |
85 | * This will trigger the switch to apic_x2apic_phys. Empty OEM IDs | |
86 | * ensure that only this APIC driver picks up the call. | |
87 | */ | |
88 | default_acpi_madt_oem_check("", ""); | |
89 | #endif | |
90 | } | |
91 | ||
dcb76008 | 92 | static void __init jailhouse_parse_smp_config(void) |
11c8dc41 | 93 | { |
cf878e16 JK |
94 | struct ioapic_domain_cfg ioapic_cfg = { |
95 | .type = IOAPIC_DOMAIN_STRICT, | |
96 | .ops = &mp_ioapic_irqdomain_ops, | |
97 | }; | |
11c8dc41 JK |
98 | unsigned int cpu; |
99 | ||
be6d447e | 100 | jailhouse_x2apic_init(); |
11c8dc41 JK |
101 | |
102 | register_lapic_address(0xfee00000); | |
103 | ||
249ada2c | 104 | for (cpu = 0; cpu < setup_data.v1.num_cpus; cpu++) |
8cd01c8a | 105 | topology_register_apic(setup_data.v1.cpu_ids[cpu], CPU_ACPIID_INVALID, true); |
11c8dc41 JK |
106 | |
107 | smp_found_config = 1; | |
cf878e16 | 108 | |
0935e5f7 | 109 | if (setup_data.v1.standard_ioapic) { |
cf878e16 JK |
110 | mp_register_ioapic(0, 0xfec00000, gsi_top, &ioapic_cfg); |
111 | ||
7a56b81c RR |
112 | if (IS_ENABLED(CONFIG_SERIAL_8250) && |
113 | setup_data.hdr.version < 2) { | |
114 | /* Register 1:1 mapping for legacy UART IRQs 3 and 4 */ | |
115 | jailhouse_setup_irq(3); | |
116 | jailhouse_setup_irq(4); | |
117 | } | |
cf878e16 | 118 | } |
11c8dc41 JK |
119 | } |
120 | ||
fd498076 JK |
121 | static void jailhouse_no_restart(void) |
122 | { | |
123 | pr_notice("Jailhouse: Restart not supported, halting\n"); | |
124 | machine_halt(); | |
125 | } | |
126 | ||
a0c01e4b JK |
127 | static int __init jailhouse_pci_arch_init(void) |
128 | { | |
129 | pci_direct_init(1); | |
130 | ||
131 | /* | |
132 | * There are no bridges on the virtual PCI root bus under Jailhouse, | |
133 | * thus no other way to discover all devices than a full scan. | |
3b42349d | 134 | * Respect any overrides via the command line, though. |
a0c01e4b | 135 | */ |
3b42349d JK |
136 | if (pcibios_last_bus < 0) |
137 | pcibios_last_bus = 0xff; | |
a0c01e4b | 138 | |
6fa4a94e | 139 | #ifdef CONFIG_PCI_MMCONFIG |
0935e5f7 | 140 | if (setup_data.v1.pci_mmconfig_base) { |
6fa4a94e | 141 | pci_mmconfig_add(0, 0, pcibios_last_bus, |
0935e5f7 | 142 | setup_data.v1.pci_mmconfig_base); |
6fa4a94e OP |
143 | pci_mmcfg_arch_init(); |
144 | } | |
145 | #endif | |
146 | ||
a0c01e4b JK |
147 | return 0; |
148 | } | |
149 | ||
7a56b81c RR |
150 | #ifdef CONFIG_SERIAL_8250 |
151 | static inline bool jailhouse_uart_enabled(unsigned int uart_nr) | |
152 | { | |
153 | return setup_data.v2.flags & BIT(uart_nr); | |
154 | } | |
155 | ||
156 | static void jailhouse_serial_fixup(int port, struct uart_port *up, | |
157 | u32 *capabilities) | |
158 | { | |
159 | static const u16 pcuart_base[] = {0x3f8, 0x2f8, 0x3e8, 0x2e8}; | |
160 | unsigned int n; | |
161 | ||
162 | for (n = 0; n < ARRAY_SIZE(pcuart_base); n++) { | |
163 | if (pcuart_base[n] != up->iobase) | |
164 | continue; | |
165 | ||
166 | if (jailhouse_uart_enabled(n)) { | |
167 | pr_info("Enabling UART%u (port 0x%lx)\n", n, | |
168 | up->iobase); | |
169 | jailhouse_setup_irq(up->irq); | |
170 | } else { | |
171 | /* Deactivate UART if access isn't allowed */ | |
172 | up->iobase = 0; | |
173 | } | |
174 | break; | |
175 | } | |
176 | } | |
177 | ||
178 | static void __init jailhouse_serial_workaround(void) | |
179 | { | |
180 | /* | |
181 | * There are flags inside setup_data that indicate availability of | |
182 | * platform UARTs since setup data version 2. | |
183 | * | |
184 | * In case of version 1, we don't know which UARTs belong Linux. In | |
185 | * this case, unconditionally register 1:1 mapping for legacy UART IRQs | |
186 | * 3 and 4. | |
187 | */ | |
188 | if (setup_data.hdr.version > 1) | |
189 | serial8250_set_isa_configurator(jailhouse_serial_fixup); | |
190 | } | |
191 | #else /* !CONFIG_SERIAL_8250 */ | |
192 | static inline void jailhouse_serial_workaround(void) | |
193 | { | |
194 | } | |
195 | #endif /* CONFIG_SERIAL_8250 */ | |
196 | ||
4a362601 JK |
197 | static void __init jailhouse_init_platform(void) |
198 | { | |
199 | u64 pa_data = boot_params.hdr.setup_data; | |
0935e5f7 | 200 | unsigned long setup_data_len; |
4a362601 JK |
201 | struct setup_data header; |
202 | void *mapping; | |
203 | ||
30c92869 TG |
204 | x86_init.irqs.pre_vector_init = x86_init_noop; |
205 | x86_init.timers.timer_init = jailhouse_timer_init; | |
206 | x86_init.mpparse.find_mptable = x86_init_noop; | |
207 | x86_init.mpparse.early_parse_smp_cfg = x86_init_noop; | |
208 | x86_init.mpparse.parse_smp_cfg = jailhouse_parse_smp_config; | |
30c92869 | 209 | x86_init.pci.arch_init = jailhouse_pci_arch_init; |
11c8dc41 | 210 | |
30c92869 TG |
211 | x86_platform.calibrate_cpu = jailhouse_get_tsc; |
212 | x86_platform.calibrate_tsc = jailhouse_get_tsc; | |
213 | x86_platform.get_wallclock = jailhouse_get_wallclock; | |
214 | x86_platform.legacy.rtc = 0; | |
215 | x86_platform.legacy.warm_reset = 0; | |
216 | x86_platform.legacy.i8042 = X86_LEGACY_I8042_PLATFORM_ABSENT; | |
0d7c1e22 | 217 | |
30c92869 | 218 | legacy_pic = &null_legacy_pic; |
e85eb632 | 219 | |
30c92869 | 220 | machine_ops.emergency_restart = jailhouse_no_restart; |
fd498076 | 221 | |
4a362601 JK |
222 | while (pa_data) { |
223 | mapping = early_memremap(pa_data, sizeof(header)); | |
224 | memcpy(&header, mapping, sizeof(header)); | |
225 | early_memunmap(mapping, sizeof(header)); | |
226 | ||
0935e5f7 | 227 | if (header.type == SETUP_JAILHOUSE) |
4a362601 | 228 | break; |
4a362601 JK |
229 | |
230 | pa_data = header.next; | |
231 | } | |
232 | ||
233 | if (!pa_data) | |
234 | panic("Jailhouse: No valid setup data found"); | |
235 | ||
0935e5f7 RR |
236 | /* setup data must at least contain the header */ |
237 | if (header.len < sizeof(setup_data.hdr)) | |
238 | goto unsupported; | |
87e65d05 | 239 | |
0935e5f7 RR |
240 | pa_data += offsetof(struct setup_data, data); |
241 | setup_data_len = min_t(unsigned long, sizeof(setup_data), | |
242 | (unsigned long)header.len); | |
243 | mapping = early_memremap(pa_data, setup_data_len); | |
244 | memcpy(&setup_data, mapping, setup_data_len); | |
245 | early_memunmap(mapping, setup_data_len); | |
246 | ||
247 | if (setup_data.hdr.version == 0 || | |
248 | setup_data.hdr.compatible_version != | |
249 | JAILHOUSE_SETUP_REQUIRED_VERSION || | |
7a56b81c RR |
250 | (setup_data.hdr.version == 1 && header.len < SETUP_DATA_V1_LEN) || |
251 | (setup_data.hdr.version >= 2 && header.len < SETUP_DATA_V2_LEN)) | |
0935e5f7 RR |
252 | goto unsupported; |
253 | ||
254 | pmtmr_ioport = setup_data.v1.pm_timer_address; | |
87e65d05 | 255 | pr_debug("Jailhouse: PM-Timer IO Port: %#x\n", pmtmr_ioport); |
e85eb632 | 256 | |
0935e5f7 | 257 | precalibrated_tsc_khz = setup_data.v1.tsc_khz; |
11f19ec0 | 258 | setup_force_cpu_cap(X86_FEATURE_TSC_KNOWN_FREQ); |
5ae44430 | 259 | |
a0c01e4b JK |
260 | pci_probe = 0; |
261 | ||
5ae44430 JK |
262 | /* |
263 | * Avoid that the kernel complains about missing ACPI tables - there | |
264 | * are none in a non-root cell. | |
265 | */ | |
266 | disable_acpi(); | |
7a56b81c RR |
267 | |
268 | jailhouse_serial_workaround(); | |
0935e5f7 RR |
269 | return; |
270 | ||
271 | unsupported: | |
272 | panic("Jailhouse: Unsupported setup data structure"); | |
4a362601 JK |
273 | } |
274 | ||
275 | bool jailhouse_paravirt(void) | |
276 | { | |
277 | return jailhouse_cpuid_base() != 0; | |
278 | } | |
279 | ||
d97ee99b | 280 | static bool __init jailhouse_x2apic_available(void) |
11c8dc41 JK |
281 | { |
282 | /* | |
283 | * The x2APIC is only available if the root cell enabled it. Jailhouse | |
284 | * does not support switching between xAPIC and x2APIC. | |
285 | */ | |
286 | return x2apic_enabled(); | |
287 | } | |
288 | ||
4a362601 JK |
289 | const struct hypervisor_x86 x86_hyper_jailhouse __refconst = { |
290 | .name = "Jailhouse", | |
291 | .detect = jailhouse_detect, | |
292 | .init.init_platform = jailhouse_init_platform, | |
11c8dc41 | 293 | .init.x2apic_available = jailhouse_x2apic_available, |
30978346 | 294 | .ignore_nopv = true, |
4a362601 | 295 | }; |