1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/init.h> |
3 | #include <linux/thread_info.h> |
4 | |
5 | #include <asm/x86_init.h> |
6 | #include <asm/apic.h> |
7 | #include <asm/io_apic.h> |
8 | #include <asm/xen/hypercall.h> |
9 | |
10 | #include <xen/xen.h> |
11 | #include <xen/interface/physdev.h> |
12 | #include "xen-ops.h" |
13 | #include "pmu.h" |
14 | #include "smp.h" |
15 | |
16 | static unsigned int xen_io_apic_read(unsigned apic, unsigned reg) |
17 | { |
18 | struct physdev_apic apic_op; |
19 | int ret; |
20 | |
21 | apic_op.apic_physbase = mpc_ioapic_addr(ioapic: apic); |
22 | apic_op.reg = reg; |
23 | ret = HYPERVISOR_physdev_op(PHYSDEVOP_apic_read, arg: &apic_op); |
24 | if (!ret) |
25 | return apic_op.value; |
26 | |
27 | /* fallback to return an emulated IO_APIC values */ |
28 | if (reg == 0x1) |
29 | return 0x00170020; |
30 | else if (reg == 0x0) |
31 | return apic << 24; |
32 | |
33 | return 0xfd; |
34 | } |
35 | |
36 | static u32 xen_get_apic_id(u32 x) |
37 | { |
38 | return ((x)>>24) & 0xFFu; |
39 | } |
40 | |
41 | static u32 xen_apic_read(u32 reg) |
42 | { |
43 | struct xen_platform_op op = { |
44 | .cmd = XENPF_get_cpuinfo, |
45 | .interface_version = XENPF_INTERFACE_VERSION, |
46 | }; |
47 | int ret, cpu; |
48 | |
49 | if (reg == APIC_LVR) |
50 | return 0x14; |
51 | if (reg != APIC_ID) |
52 | return 0; |
53 | |
54 | cpu = smp_processor_id(); |
55 | if (!xen_initial_domain()) |
56 | return cpu ? cpuid_to_apicid[cpu] << 24 : 0; |
57 | |
58 | op.u.pcpu_info.xen_cpuid = cpu; |
59 | |
60 | ret = HYPERVISOR_platform_op(op: &op); |
61 | if (ret) |
62 | op.u.pcpu_info.apic_id = BAD_APICID; |
63 | |
64 | return op.u.pcpu_info.apic_id << 24; |
65 | } |
66 | |
67 | static void xen_apic_write(u32 reg, u32 val) |
68 | { |
69 | if (reg == APIC_LVTPC) { |
70 | (void)pmu_apic_update(reg); |
71 | return; |
72 | } |
73 | |
74 | /* Warn to see if there's any stray references */ |
75 | WARN(1,"register: %x, value: %x\n" , reg, val); |
76 | } |
77 | |
78 | static void xen_apic_eoi(void) |
79 | { |
80 | WARN_ON_ONCE(1); |
81 | } |
82 | |
83 | static u64 xen_apic_icr_read(void) |
84 | { |
85 | return 0; |
86 | } |
87 | |
88 | static void xen_apic_icr_write(u32 low, u32 id) |
89 | { |
90 | /* Warn to see if there's any stray references */ |
91 | WARN_ON(1); |
92 | } |
93 | |
94 | static int xen_apic_probe_pv(void) |
95 | { |
96 | if (xen_pv_domain()) |
97 | return 1; |
98 | |
99 | return 0; |
100 | } |
101 | |
102 | static int xen_madt_oem_check(char *oem_id, char *oem_table_id) |
103 | { |
104 | return xen_pv_domain(); |
105 | } |
106 | |
107 | static u32 xen_cpu_present_to_apicid(int cpu) |
108 | { |
109 | if (cpu_present(cpu)) |
110 | return cpu_data(cpu).topo.apicid; |
111 | else |
112 | return BAD_APICID; |
113 | } |
114 | |
115 | static struct apic xen_pv_apic __ro_after_init = { |
116 | .name = "Xen PV" , |
117 | .probe = xen_apic_probe_pv, |
118 | .acpi_madt_oem_check = xen_madt_oem_check, |
119 | |
120 | /* .delivery_mode and .dest_mode_logical not used by XENPV */ |
121 | |
122 | .disable_esr = 0, |
123 | |
124 | .cpu_present_to_apicid = xen_cpu_present_to_apicid, |
125 | |
126 | .max_apic_id = UINT_MAX, |
127 | .get_apic_id = xen_get_apic_id, |
128 | |
129 | .calc_dest_apicid = apic_flat_calc_apicid, |
130 | |
131 | #ifdef CONFIG_SMP |
132 | .send_IPI_mask = xen_send_IPI_mask, |
133 | .send_IPI_mask_allbutself = xen_send_IPI_mask_allbutself, |
134 | .send_IPI_allbutself = xen_send_IPI_allbutself, |
135 | .send_IPI_all = xen_send_IPI_all, |
136 | .send_IPI_self = xen_send_IPI_self, |
137 | #endif |
138 | .read = xen_apic_read, |
139 | .write = xen_apic_write, |
140 | .eoi = xen_apic_eoi, |
141 | |
142 | .icr_read = xen_apic_icr_read, |
143 | .icr_write = xen_apic_icr_write, |
144 | }; |
145 | apic_driver(xen_pv_apic); |
146 | |
147 | void __init xen_init_apic(void) |
148 | { |
149 | x86_apic_ops.io_apic_read = xen_io_apic_read; |
150 | } |
151 | |