1 /*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU Lesser General Public License as published
4 * by the Free Software Foundation; version 2.1 only. with the special
5 * exception on linking described in file LICENSE.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU Lesser General Public License for more details.
11 *
12 * Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved.
13 */
14
15 #include "libxl_internal.h"
16 #include "libxl_arch.h"
17 #include <xen/hvm/hvm_info_table.h>
18 #include <xen/hvm/e820.h>
19 #include "libacpi/libacpi.h"
20
21 #include <xc_dom.h>
22
23 /* Number of pages holding ACPI tables */
24 #define NUM_ACPI_PAGES 16
25
26 struct libxl_acpi_ctxt {
27 struct acpi_ctxt c;
28
29 unsigned int page_size;
30 unsigned int page_shift;
31
32 /* Memory allocator */
33 unsigned long alloc_base_paddr;
34 unsigned long alloc_base_vaddr;
35 unsigned long alloc_currp;
36 unsigned long alloc_end;
37 };
38
39 extern const unsigned char dsdt_pvh[];
40 extern const unsigned int dsdt_pvh_len;
41
42 /* Assumes contiguous physical space */
virt_to_phys(struct acpi_ctxt * ctxt,void * v)43 static unsigned long virt_to_phys(struct acpi_ctxt *ctxt, void *v)
44 {
45 struct libxl_acpi_ctxt *libxl_ctxt =
46 CONTAINER_OF(ctxt, struct libxl_acpi_ctxt, c);
47
48 return (((unsigned long)v - libxl_ctxt->alloc_base_vaddr) +
49 libxl_ctxt->alloc_base_paddr);
50 }
51
mem_alloc(struct acpi_ctxt * ctxt,uint32_t size,uint32_t align)52 static void *mem_alloc(struct acpi_ctxt *ctxt,
53 uint32_t size, uint32_t align)
54 {
55 struct libxl_acpi_ctxt *libxl_ctxt =
56 CONTAINER_OF(ctxt, struct libxl_acpi_ctxt, c);
57 unsigned long s, e;
58
59 /* Align to at least 16 bytes. */
60 if (align < 16)
61 align = 16;
62
63 s = (libxl_ctxt->alloc_currp + align) & ~((unsigned long)align - 1);
64 e = s + size - 1;
65
66 /* TODO: Reallocate memory */
67 if ((e < s) || (e >= libxl_ctxt->alloc_end))
68 return NULL;
69
70 while (libxl_ctxt->alloc_currp >> libxl_ctxt->page_shift !=
71 e >> libxl_ctxt->page_shift)
72 libxl_ctxt->alloc_currp += libxl_ctxt->page_size;
73
74 libxl_ctxt->alloc_currp = e;
75
76 return (void *)s;
77 }
78
acpi_mem_free(struct acpi_ctxt * ctxt,void * v,uint32_t size)79 static void acpi_mem_free(struct acpi_ctxt *ctxt,
80 void *v, uint32_t size)
81 {
82 }
83
acpi_lapic_id(unsigned cpu)84 static uint32_t acpi_lapic_id(unsigned cpu)
85 {
86 return cpu * 2;
87 }
88
init_acpi_config(libxl__gc * gc,struct xc_dom_image * dom,const libxl_domain_build_info * b_info,struct acpi_config * config)89 static int init_acpi_config(libxl__gc *gc,
90 struct xc_dom_image *dom,
91 const libxl_domain_build_info *b_info,
92 struct acpi_config *config)
93 {
94 xc_interface *xch = dom->xch;
95 uint32_t domid = dom->guest_domid;
96 xc_dominfo_t info;
97 struct hvm_info_table *hvminfo;
98 int i, r, rc;
99
100 config->dsdt_anycpu = config->dsdt_15cpu = dsdt_pvh;
101 config->dsdt_anycpu_len = config->dsdt_15cpu_len = dsdt_pvh_len;
102
103 r = xc_domain_getinfo(xch, domid, 1, &info);
104 if (r < 0) {
105 LOG(ERROR, "getdomaininfo failed (rc=%d)", r);
106 rc = ERROR_FAIL;
107 goto out;
108 }
109
110 hvminfo = libxl__zalloc(gc, sizeof(*hvminfo));
111
112 hvminfo->apic_mode = libxl_defbool_val(b_info->apic);
113
114 if (dom->nr_vnodes) {
115 unsigned int *vcpu_to_vnode, *vdistance;
116 struct xen_vmemrange *vmemrange;
117 struct acpi_numa *numa = &config->numa;
118
119 r = xc_domain_getvnuma(xch, domid, &numa->nr_vnodes,
120 &numa->nr_vmemranges,
121 &hvminfo->nr_vcpus, NULL, NULL, NULL);
122 if (r) {
123 LOG(ERROR, "xc_domain_getvnuma failed (rc=%d)", r);
124 rc = ERROR_FAIL;
125 goto out;
126 }
127
128 vmemrange = libxl__zalloc(gc, dom->nr_vmemranges * sizeof(*vmemrange));
129 vdistance = libxl__zalloc(gc, dom->nr_vnodes * sizeof(*vdistance));
130 vcpu_to_vnode = libxl__zalloc(gc, hvminfo->nr_vcpus *
131 sizeof(*vcpu_to_vnode));
132 r = xc_domain_getvnuma(xch, domid, &numa->nr_vnodes,
133 &numa->nr_vmemranges, &hvminfo->nr_vcpus,
134 vmemrange, vdistance, vcpu_to_vnode);
135 if (r) {
136 LOG(ERROR, "xc_domain_getvnuma failed (rc=%d)", r);
137 rc = ERROR_FAIL;
138 goto out;
139 }
140 numa->vmemrange = vmemrange;
141 numa->vdistance = vdistance;
142 numa->vcpu_to_vnode = vcpu_to_vnode;
143 } else {
144 hvminfo->nr_vcpus = info.max_vcpu_id + 1;
145 }
146
147 for (i = 0; i < hvminfo->nr_vcpus; i++)
148 hvminfo->vcpu_online[i / 8] |= 1 << (i & 7);
149
150 config->hvminfo = hvminfo;
151
152 config->lapic_base_address = LAPIC_BASE_ADDRESS;
153 config->lapic_id = acpi_lapic_id;
154 config->acpi_revision = 5;
155
156 rc = 0;
157 out:
158 return rc;
159 }
160
libxl__dom_load_acpi(libxl__gc * gc,const libxl_domain_build_info * b_info,struct xc_dom_image * dom)161 int libxl__dom_load_acpi(libxl__gc *gc,
162 const libxl_domain_build_info *b_info,
163 struct xc_dom_image *dom)
164 {
165 struct acpi_config config = {0};
166 struct libxl_acpi_ctxt libxl_ctxt;
167 int rc = 0, acpi_pages_num;
168 void *acpi_pages;
169 unsigned long page_mask;
170
171 if (b_info->type != LIBXL_DOMAIN_TYPE_PVH)
172 goto out;
173
174 libxl_ctxt.page_size = XC_DOM_PAGE_SIZE(dom);
175 libxl_ctxt.page_shift = XC_DOM_PAGE_SHIFT(dom);
176 page_mask = (1UL << libxl_ctxt.page_shift) - 1;
177
178 libxl_ctxt.c.mem_ops.alloc = mem_alloc;
179 libxl_ctxt.c.mem_ops.v2p = virt_to_phys;
180 libxl_ctxt.c.mem_ops.free = acpi_mem_free;
181
182 rc = init_acpi_config(gc, dom, b_info, &config);
183 if (rc) {
184 LOG(ERROR, "init_acpi_config failed (rc=%d)", rc);
185 goto out;
186 }
187
188 config.rsdp = (unsigned long)libxl__malloc(gc, libxl_ctxt.page_size);
189 config.infop = (unsigned long)libxl__malloc(gc, libxl_ctxt.page_size);
190 /* Pages to hold ACPI tables */
191 acpi_pages = libxl__malloc(gc, (NUM_ACPI_PAGES + 1) *
192 libxl_ctxt.page_size);
193
194 /*
195 * Set up allocator memory.
196 * Start next to acpi_info page to avoid fracturing e820.
197 */
198 libxl_ctxt.alloc_base_paddr = ACPI_INFO_PHYSICAL_ADDRESS +
199 libxl_ctxt.page_size;
200 libxl_ctxt.alloc_base_vaddr = libxl_ctxt.alloc_currp =
201 (unsigned long)acpi_pages;
202 libxl_ctxt.alloc_end = (unsigned long)acpi_pages +
203 (NUM_ACPI_PAGES * libxl_ctxt.page_size);
204
205 /* Build the tables. */
206 rc = acpi_build_tables(&libxl_ctxt.c, &config);
207 if (rc) {
208 LOG(ERROR, "acpi_build_tables failed with %d", rc);
209 goto out;
210 }
211
212 /* Calculate how many pages are needed for the tables. */
213 acpi_pages_num =
214 ((libxl_ctxt.alloc_currp - (unsigned long)acpi_pages)
215 >> libxl_ctxt.page_shift) +
216 ((libxl_ctxt.alloc_currp & page_mask) ? 1 : 0);
217
218 dom->acpi_modules[0].data = (void *)config.rsdp;
219 dom->acpi_modules[0].length = 64;
220 /*
221 * Some Linux versions cannot properly process hvm_start_info.rsdp_paddr
222 * and so we need to put RSDP in location that can be discovered by ACPI's
223 * standard search method, in R-O BIOS memory (we chose last 64 bytes)
224 */
225 if (strcmp(dom->parms.guest_os, "linux") ||
226 elf_xen_feature_get(XENFEAT_linux_rsdp_unrestricted,
227 dom->parms.f_supported))
228 dom->acpi_modules[0].guest_addr_out = ACPI_INFO_PHYSICAL_ADDRESS +
229 (1 + acpi_pages_num) * libxl_ctxt.page_size;
230 else
231 dom->acpi_modules[0].guest_addr_out = 0x100000 - 64;
232
233 dom->acpi_modules[1].data = (void *)config.infop;
234 dom->acpi_modules[1].length = 4096;
235 dom->acpi_modules[1].guest_addr_out = ACPI_INFO_PHYSICAL_ADDRESS;
236
237 dom->acpi_modules[2].data = acpi_pages;
238 dom->acpi_modules[2].length = acpi_pages_num << libxl_ctxt.page_shift;
239 dom->acpi_modules[2].guest_addr_out = ACPI_INFO_PHYSICAL_ADDRESS +
240 libxl_ctxt.page_size;
241
242 out:
243 return rc;
244 }
245
246 /*
247 * Local variables:
248 * mode: C
249 * c-basic-offset: 4
250 * indent-tabs-mode: nil
251 * End:
252 */
253