1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/acpi.h> |
3 | #include <linux/export.h> |
4 | #include <linux/mm.h> |
5 | |
6 | #include <xen/hvc-console.h> |
7 | |
8 | #include <asm/bootparam.h> |
9 | #include <asm/io_apic.h> |
10 | #include <asm/hypervisor.h> |
11 | #include <asm/e820/api.h> |
12 | |
13 | #include <xen/xen.h> |
14 | #include <asm/xen/interface.h> |
15 | #include <asm/xen/hypercall.h> |
16 | |
17 | #include <xen/interface/memory.h> |
18 | |
19 | #include "xen-ops.h" |
20 | |
21 | /* |
22 | * PVH variables. |
23 | * |
24 | * The variable xen_pvh needs to live in a data segment since it is used |
25 | * after startup_{32|64} is invoked, which will clear the .bss segment. |
26 | */ |
27 | bool __ro_after_init xen_pvh; |
28 | EXPORT_SYMBOL_GPL(xen_pvh); |
29 | |
30 | void __init xen_pvh_init(struct boot_params *boot_params) |
31 | { |
32 | u32 msr; |
33 | u64 pfn; |
34 | |
35 | xen_pvh = 1; |
36 | xen_domain_type = XEN_HVM_DOMAIN; |
37 | xen_start_flags = pvh_start_info.flags; |
38 | |
39 | msr = cpuid_ebx(op: xen_cpuid_base() + 2); |
40 | pfn = __pa(hypercall_page); |
41 | wrmsr_safe(msr, (u32)pfn, (u32)(pfn >> 32)); |
42 | |
43 | if (xen_initial_domain()) |
44 | x86_init.oem.arch_setup = xen_add_preferred_consoles; |
45 | x86_init.oem.banner = xen_banner; |
46 | |
47 | xen_efi_init(boot_params); |
48 | |
49 | if (xen_initial_domain()) { |
50 | struct xen_platform_op op = { |
51 | .cmd = XENPF_get_dom0_console, |
52 | }; |
53 | int ret = HYPERVISOR_platform_op(op: &op); |
54 | |
55 | if (ret > 0) |
56 | xen_init_vga(&op.u.dom0_console, |
57 | min(ret * sizeof(char), |
58 | sizeof(op.u.dom0_console)), |
59 | &boot_params->screen_info); |
60 | } |
61 | } |
62 | |
63 | void __init mem_map_via_hcall(struct boot_params *boot_params_p) |
64 | { |
65 | struct xen_memory_map memmap; |
66 | int rc; |
67 | |
68 | memmap.nr_entries = ARRAY_SIZE(boot_params_p->e820_table); |
69 | set_xen_guest_handle(memmap.buffer, boot_params_p->e820_table); |
70 | rc = HYPERVISOR_memory_op(XENMEM_memory_map, arg: &memmap); |
71 | if (rc) { |
72 | xen_raw_printk(fmt: "XENMEM_memory_map failed (%d)\n" , rc); |
73 | BUG(); |
74 | } |
75 | boot_params_p->e820_entries = memmap.nr_entries; |
76 | } |
77 | |
78 | /* |
79 | * Reserve e820 UNUSABLE regions to inflate the memory balloon. |
80 | * |
81 | * On PVH dom0 the host memory map is used, RAM regions available to dom0 are |
82 | * located as the same place as in the native memory map, but since dom0 gets |
83 | * less memory than the total amount of host RAM the ranges that can't be |
84 | * populated are converted from RAM -> UNUSABLE. Use such regions (up to the |
85 | * ratio signaled in EXTRA_MEM_RATIO) in order to inflate the balloon driver at |
86 | * boot. Doing so prevents the guest (even if just temporary) from using holes |
87 | * in the memory map in order to map grants or foreign addresses, and |
88 | * hopefully limits the risk of a clash with a device MMIO region. Ideally the |
89 | * hypervisor should notify us which memory ranges are suitable for creating |
90 | * foreign mappings, but that's not yet implemented. |
91 | */ |
92 | void __init (struct boot_params *bootp) |
93 | { |
94 | unsigned int i, ram_pages = 0, ; |
95 | |
96 | for (i = 0; i < bootp->e820_entries; i++) { |
97 | struct boot_e820_entry *e = &bootp->e820_table[i]; |
98 | |
99 | if (e->type != E820_TYPE_RAM) |
100 | continue; |
101 | ram_pages += PFN_DOWN(e->addr + e->size) - PFN_UP(e->addr); |
102 | } |
103 | |
104 | /* Max amount of extra memory. */ |
105 | extra_pages = EXTRA_MEM_RATIO * ram_pages; |
106 | |
107 | /* |
108 | * Convert UNUSABLE ranges to RAM and reserve them for foreign mapping |
109 | * purposes. |
110 | */ |
111 | for (i = 0; i < bootp->e820_entries && extra_pages; i++) { |
112 | struct boot_e820_entry *e = &bootp->e820_table[i]; |
113 | unsigned long pages; |
114 | |
115 | if (e->type != E820_TYPE_UNUSABLE) |
116 | continue; |
117 | |
118 | pages = min(extra_pages, |
119 | PFN_DOWN(e->addr + e->size) - PFN_UP(e->addr)); |
120 | |
121 | if (pages != (PFN_DOWN(e->addr + e->size) - PFN_UP(e->addr))) { |
122 | struct boot_e820_entry *next; |
123 | |
124 | if (bootp->e820_entries == |
125 | ARRAY_SIZE(bootp->e820_table)) |
126 | /* No space left to split - skip region. */ |
127 | continue; |
128 | |
129 | /* Split entry. */ |
130 | next = e + 1; |
131 | memmove(next, e, |
132 | (bootp->e820_entries - i) * sizeof(*e)); |
133 | bootp->e820_entries++; |
134 | next->addr = PAGE_ALIGN(e->addr) + PFN_PHYS(pages); |
135 | e->size = next->addr - e->addr; |
136 | next->size -= e->size; |
137 | } |
138 | e->type = E820_TYPE_RAM; |
139 | extra_pages -= pages; |
140 | |
141 | xen_add_extra_mem(PFN_UP(e->addr), n_pfns: pages); |
142 | } |
143 | } |
144 | |