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