Commit | Line | Data |
---|---|---|
4df7363e | 1 | // SPDX-License-Identifier: GPL-2.0 |
8cee3974 | 2 | #include <linux/acpi.h> |
64a66e2c RPM |
3 | #include <linux/cpufreq.h> |
4 | #include <linux/cpuidle.h> | |
9c11112c | 5 | #include <linux/export.h> |
38620fc4 | 6 | #include <linux/mm.h> |
8cee3974 | 7 | |
a43fb7da | 8 | #include <xen/hvc-console.h> |
b166b8ab | 9 | #include <xen/acpi.h> |
a43fb7da | 10 | |
103bf75f | 11 | #include <asm/bootparam.h> |
8cee3974 MW |
12 | #include <asm/io_apic.h> |
13 | #include <asm/hypervisor.h> | |
a43fb7da | 14 | #include <asm/e820/api.h> |
4c006734 | 15 | #include <asm/setup.h> |
8cee3974 MW |
16 | |
17 | #include <xen/xen.h> | |
18 | #include <asm/xen/interface.h> | |
19 | #include <asm/xen/hypercall.h> | |
4df7363e | 20 | |
a43fb7da MW |
21 | #include <xen/interface/memory.h> |
22 | ||
72813bfb RPM |
23 | #include "xen-ops.h" |
24 | ||
4df7363e MW |
25 | /* |
26 | * PVH variables. | |
27 | * | |
9c11112c | 28 | * The variable xen_pvh needs to live in a data segment since it is used |
4df7363e MW |
29 | * after startup_{32|64} is invoked, which will clear the .bss segment. |
30 | */ | |
9c11112c JB |
31 | bool __ro_after_init xen_pvh; |
32 | EXPORT_SYMBOL_GPL(xen_pvh); | |
8cee3974 | 33 | |
b166b8ab JC |
34 | #ifdef CONFIG_XEN_DOM0 |
35 | int xen_pvh_setup_gsi(int gsi, int trigger, int polarity) | |
36 | { | |
37 | int ret; | |
38 | struct physdev_setup_gsi setup_gsi; | |
39 | ||
40 | setup_gsi.gsi = gsi; | |
41 | setup_gsi.triggering = (trigger == ACPI_EDGE_SENSITIVE ? 0 : 1); | |
42 | setup_gsi.polarity = (polarity == ACPI_ACTIVE_HIGH ? 0 : 1); | |
43 | ||
44 | ret = HYPERVISOR_physdev_op(PHYSDEVOP_setup_gsi, &setup_gsi); | |
45 | if (ret == -EEXIST) { | |
46 | xen_raw_printk("Already setup the GSI :%d\n", gsi); | |
47 | ret = 0; | |
48 | } else if (ret) | |
49 | xen_raw_printk("Fail to setup GSI (%d)!\n", gsi); | |
50 | ||
51 | return ret; | |
52 | } | |
53 | EXPORT_SYMBOL_GPL(xen_pvh_setup_gsi); | |
54 | #endif | |
55 | ||
38620fc4 RPM |
56 | /* |
57 | * Reserve e820 UNUSABLE regions to inflate the memory balloon. | |
58 | * | |
59 | * On PVH dom0 the host memory map is used, RAM regions available to dom0 are | |
60 | * located as the same place as in the native memory map, but since dom0 gets | |
61 | * less memory than the total amount of host RAM the ranges that can't be | |
62 | * populated are converted from RAM -> UNUSABLE. Use such regions (up to the | |
63 | * ratio signaled in EXTRA_MEM_RATIO) in order to inflate the balloon driver at | |
64 | * boot. Doing so prevents the guest (even if just temporary) from using holes | |
65 | * in the memory map in order to map grants or foreign addresses, and | |
66 | * hopefully limits the risk of a clash with a device MMIO region. Ideally the | |
67 | * hypervisor should notify us which memory ranges are suitable for creating | |
68 | * foreign mappings, but that's not yet implemented. | |
69 | */ | |
4c006734 | 70 | static void __init pvh_reserve_extra_memory(void) |
38620fc4 | 71 | { |
4c006734 | 72 | struct boot_params *bootp = &boot_params; |
38620fc4 RPM |
73 | unsigned int i, ram_pages = 0, extra_pages; |
74 | ||
75 | for (i = 0; i < bootp->e820_entries; i++) { | |
76 | struct boot_e820_entry *e = &bootp->e820_table[i]; | |
77 | ||
78 | if (e->type != E820_TYPE_RAM) | |
79 | continue; | |
80 | ram_pages += PFN_DOWN(e->addr + e->size) - PFN_UP(e->addr); | |
81 | } | |
82 | ||
83 | /* Max amount of extra memory. */ | |
84 | extra_pages = EXTRA_MEM_RATIO * ram_pages; | |
85 | ||
86 | /* | |
87 | * Convert UNUSABLE ranges to RAM and reserve them for foreign mapping | |
88 | * purposes. | |
89 | */ | |
90 | for (i = 0; i < bootp->e820_entries && extra_pages; i++) { | |
91 | struct boot_e820_entry *e = &bootp->e820_table[i]; | |
92 | unsigned long pages; | |
93 | ||
94 | if (e->type != E820_TYPE_UNUSABLE) | |
95 | continue; | |
96 | ||
97 | pages = min(extra_pages, | |
98 | PFN_DOWN(e->addr + e->size) - PFN_UP(e->addr)); | |
99 | ||
100 | if (pages != (PFN_DOWN(e->addr + e->size) - PFN_UP(e->addr))) { | |
101 | struct boot_e820_entry *next; | |
102 | ||
103 | if (bootp->e820_entries == | |
104 | ARRAY_SIZE(bootp->e820_table)) | |
105 | /* No space left to split - skip region. */ | |
106 | continue; | |
107 | ||
108 | /* Split entry. */ | |
109 | next = e + 1; | |
110 | memmove(next, e, | |
111 | (bootp->e820_entries - i) * sizeof(*e)); | |
112 | bootp->e820_entries++; | |
113 | next->addr = PAGE_ALIGN(e->addr) + PFN_PHYS(pages); | |
114 | e->size = next->addr - e->addr; | |
115 | next->size -= e->size; | |
116 | } | |
117 | e->type = E820_TYPE_RAM; | |
118 | extra_pages -= pages; | |
119 | ||
120 | xen_add_extra_mem(PFN_UP(e->addr), pages); | |
121 | } | |
122 | } | |
fc05ea89 | 123 | |
4c006734 RPM |
124 | static void __init pvh_arch_setup(void) |
125 | { | |
126 | pvh_reserve_extra_memory(); | |
127 | ||
64a66e2c | 128 | if (xen_initial_domain()) { |
4c006734 | 129 | xen_add_preferred_consoles(); |
64a66e2c RPM |
130 | |
131 | /* | |
132 | * Disable usage of CPU idle and frequency drivers: when | |
133 | * running as hardware domain the exposed native ACPI tables | |
134 | * causes idle and/or frequency drivers to attach and | |
135 | * malfunction. It's Xen the entity that controls the idle and | |
136 | * frequency states. | |
137 | * | |
138 | * For unprivileged domains the exposed ACPI tables are | |
139 | * fabricated and don't contain such data. | |
140 | */ | |
141 | disable_cpuidle(); | |
142 | disable_cpufreq(); | |
143 | WARN_ON(xen_set_default_idle()); | |
144 | } | |
4c006734 RPM |
145 | } |
146 | ||
fc05ea89 RPM |
147 | void __init xen_pvh_init(struct boot_params *boot_params) |
148 | { | |
fc05ea89 RPM |
149 | xen_pvh = 1; |
150 | xen_domain_type = XEN_HVM_DOMAIN; | |
151 | xen_start_flags = pvh_start_info.flags; | |
152 | ||
4c006734 | 153 | x86_init.oem.arch_setup = pvh_arch_setup; |
fc05ea89 RPM |
154 | x86_init.oem.banner = xen_banner; |
155 | ||
156 | xen_efi_init(boot_params); | |
157 | ||
158 | if (xen_initial_domain()) { | |
159 | struct xen_platform_op op = { | |
160 | .cmd = XENPF_get_dom0_console, | |
161 | }; | |
162 | int ret = HYPERVISOR_platform_op(&op); | |
163 | ||
164 | if (ret > 0) | |
165 | xen_init_vga(&op.u.dom0_console, | |
166 | min(ret * sizeof(char), | |
167 | sizeof(op.u.dom0_console)), | |
168 | &boot_params->screen_info); | |
169 | } | |
170 | } | |
171 | ||
172 | void __init mem_map_via_hcall(struct boot_params *boot_params_p) | |
173 | { | |
174 | struct xen_memory_map memmap; | |
175 | int rc; | |
176 | ||
177 | memmap.nr_entries = ARRAY_SIZE(boot_params_p->e820_table); | |
178 | set_xen_guest_handle(memmap.buffer, boot_params_p->e820_table); | |
179 | rc = HYPERVISOR_memory_op(XENMEM_memory_map, &memmap); | |
180 | if (rc) { | |
181 | xen_raw_printk("XENMEM_memory_map failed (%d)\n", rc); | |
182 | BUG(); | |
183 | } | |
184 | boot_params_p->e820_entries = memmap.nr_entries; | |
185 | } |