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.h"
20 
21  /* Number of pages holding ACPI tables */
22 #define NUM_ACPI_PAGES 16
23 /* Hard-coded size of RSDP */
24 #define RSDP_LEN 64
25 #define ALIGN(p, a) (((p) + ((a) - 1)) & ~((a) - 1))
26 
27 struct libxl_acpi_ctxt {
28     struct acpi_ctxt c;
29 
30     unsigned int page_size;
31     unsigned int page_shift;
32 
33     /* Memory allocator */
34     unsigned long guest_start;
35     unsigned long guest_curr;
36     unsigned long guest_end;
37     void *buf;
38 };
39 
40 extern const unsigned char dsdt_pvh[];
41 extern const unsigned int dsdt_pvh_len;
42 
43 /* Assumes contiguous physical space */
virt_to_phys(struct acpi_ctxt * ctxt,void * v)44 static unsigned long virt_to_phys(struct acpi_ctxt *ctxt, void *v)
45 {
46     struct libxl_acpi_ctxt *libxl_ctxt =
47         CONTAINER_OF(ctxt, struct libxl_acpi_ctxt, c);
48 
49     return libxl_ctxt->guest_start + (v - libxl_ctxt->buf);
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 = ALIGN(libxl_ctxt->guest_curr, align);
64     e = s + size - 1;
65 
66     /* TODO: Reallocate memory */
67     if ((e < s) || (e >= libxl_ctxt->guest_end))
68         return NULL;
69 
70     libxl_ctxt->guest_curr = e;
71 
72     return libxl_ctxt->buf + (s - libxl_ctxt->guest_start);
73 }
74 
acpi_mem_free(struct acpi_ctxt * ctxt,void * v,uint32_t size)75 static void acpi_mem_free(struct acpi_ctxt *ctxt,
76                           void *v, uint32_t size)
77 {
78 }
79 
acpi_lapic_id(unsigned cpu)80 static uint32_t acpi_lapic_id(unsigned cpu)
81 {
82     return cpu * 2;
83 }
84 
init_acpi_config(libxl__gc * gc,struct xc_dom_image * dom,const libxl_domain_build_info * b_info,struct acpi_config * config)85 static int init_acpi_config(libxl__gc *gc,
86                             struct xc_dom_image *dom,
87                             const libxl_domain_build_info *b_info,
88                             struct acpi_config *config)
89 {
90     xc_interface *xch = dom->xch;
91     uint32_t domid = dom->guest_domid;
92     xc_domaininfo_t info;
93     struct hvm_info_table *hvminfo;
94     int r, rc;
95 
96     config->dsdt_anycpu = config->dsdt_15cpu = dsdt_pvh;
97     config->dsdt_anycpu_len = config->dsdt_15cpu_len = dsdt_pvh_len;
98 
99     r = xc_domain_getinfo_single(xch, domid, &info);
100     if (r < 0) {
101         LOGED(ERROR, domid, "getdomaininfo failed");
102         rc = ERROR_FAIL;
103         goto out;
104     }
105 
106     hvminfo = libxl__zalloc(gc, sizeof(*hvminfo));
107 
108     hvminfo->apic_mode = libxl_defbool_val(b_info->apic);
109 
110     if (dom->nr_vnodes) {
111         unsigned int *vcpu_to_vnode, *vdistance;
112         struct xen_vmemrange *vmemrange;
113         struct acpi_numa *numa = &config->numa;
114 
115         r = xc_domain_getvnuma(xch, domid, &numa->nr_vnodes,
116                                &numa->nr_vmemranges,
117                                &hvminfo->nr_vcpus, NULL, NULL, NULL);
118         if (r) {
119             LOG(ERROR, "xc_domain_getvnuma failed (rc=%d)", r);
120             rc = ERROR_FAIL;
121             goto out;
122         }
123 
124         vmemrange = libxl__zalloc(gc, dom->nr_vmemranges * sizeof(*vmemrange));
125         vdistance = libxl__zalloc(gc, dom->nr_vnodes * sizeof(*vdistance));
126         vcpu_to_vnode = libxl__zalloc(gc, hvminfo->nr_vcpus *
127                                       sizeof(*vcpu_to_vnode));
128         r = xc_domain_getvnuma(xch, domid, &numa->nr_vnodes,
129                                &numa->nr_vmemranges, &hvminfo->nr_vcpus,
130                                vmemrange, vdistance, vcpu_to_vnode);
131         if (r) {
132             LOG(ERROR, "xc_domain_getvnuma failed (rc=%d)", r);
133             rc = ERROR_FAIL;
134             goto out;
135         }
136         numa->vmemrange = vmemrange;
137         numa->vdistance = vdistance;
138         numa->vcpu_to_vnode = vcpu_to_vnode;
139     } else {
140         hvminfo->nr_vcpus = info.max_vcpu_id + 1;
141     }
142 
143     memcpy(hvminfo->vcpu_online, b_info->avail_vcpus.map,
144            b_info->avail_vcpus.size);
145 
146     config->hvminfo = hvminfo;
147 
148     config->lapic_base_address = LAPIC_BASE_ADDRESS;
149     config->lapic_id = acpi_lapic_id;
150     config->acpi_revision = 5;
151 
152     rc = 0;
153 out:
154     return rc;
155 }
156 
libxl__dom_load_acpi(libxl__gc * gc,const libxl_domain_build_info * b_info,struct xc_dom_image * dom)157 int libxl__dom_load_acpi(libxl__gc *gc,
158                          const libxl_domain_build_info *b_info,
159                          struct xc_dom_image *dom)
160 {
161     struct acpi_config config = {0};
162     struct libxl_acpi_ctxt libxl_ctxt;
163     int rc = 0, acpi_pages_num;
164 
165     if (b_info->type != LIBXL_DOMAIN_TYPE_PVH)
166         goto out;
167 
168     libxl_ctxt.page_size = XC_DOM_PAGE_SIZE(dom);
169     libxl_ctxt.page_shift =  XC_DOM_PAGE_SHIFT(dom);
170 
171     libxl_ctxt.c.mem_ops.alloc = mem_alloc;
172     libxl_ctxt.c.mem_ops.v2p = virt_to_phys;
173     libxl_ctxt.c.mem_ops.free = acpi_mem_free;
174 
175     rc = init_acpi_config(gc, dom, b_info, &config);
176     if (rc) {
177         LOG(ERROR, "init_acpi_config failed (rc=%d)", rc);
178         goto out;
179     }
180 
181     /* These are all copied into guest memory, so use zero-ed memory. */
182     config.rsdp = (unsigned long)libxl__zalloc(gc, RSDP_LEN);
183     config.infop = (unsigned long)libxl__zalloc(gc, libxl_ctxt.page_size);
184     /* Pages to hold ACPI tables */
185     libxl_ctxt.buf = libxl__zalloc(gc, NUM_ACPI_PAGES *
186                                    libxl_ctxt.page_size);
187 
188     /*
189      * Set up allocator memory.
190      * Start next to acpi_info page to avoid fracturing e820.
191      */
192     libxl_ctxt.guest_start = libxl_ctxt.guest_curr = libxl_ctxt.guest_end =
193         ACPI_INFO_PHYSICAL_ADDRESS + libxl_ctxt.page_size;
194 
195     libxl_ctxt.guest_end += NUM_ACPI_PAGES * libxl_ctxt.page_size;
196 
197     /* Build the tables. */
198     rc = acpi_build_tables(&libxl_ctxt.c, &config);
199     if (rc) {
200         LOG(ERROR, "acpi_build_tables failed with %d", rc);
201         goto out;
202     }
203 
204     /* Calculate how many pages are needed for the tables. */
205     acpi_pages_num = (ALIGN(libxl_ctxt.guest_curr, libxl_ctxt.page_size) -
206                       libxl_ctxt.guest_start) >> libxl_ctxt.page_shift;
207 
208     dom->acpi_modules[0].data = (void *)config.rsdp;
209     dom->acpi_modules[0].length = RSDP_LEN;
210     /*
211      * Some Linux versions cannot properly process hvm_start_info.rsdp_paddr
212      * and so we need to put RSDP in location that can be discovered by ACPI's
213      * standard search method, in R-O BIOS memory (we chose last RSDP_LEN bytes)
214      */
215     if (strcmp(xc_dom_guest_os(dom), "linux") ||
216         xc_dom_feature_get(dom, XENFEAT_linux_rsdp_unrestricted))
217         dom->acpi_modules[0].guest_addr_out = ACPI_INFO_PHYSICAL_ADDRESS +
218             (1 + acpi_pages_num) * libxl_ctxt.page_size;
219     else
220         dom->acpi_modules[0].guest_addr_out = 0x100000 - RSDP_LEN;
221 
222     dom->acpi_modules[1].data = (void *)config.infop;
223     dom->acpi_modules[1].length = libxl_ctxt.page_size;
224     dom->acpi_modules[1].guest_addr_out = ACPI_INFO_PHYSICAL_ADDRESS;
225 
226     dom->acpi_modules[2].data = libxl_ctxt.buf;
227     dom->acpi_modules[2].length = acpi_pages_num  << libxl_ctxt.page_shift;
228     dom->acpi_modules[2].guest_addr_out = ACPI_INFO_PHYSICAL_ADDRESS +
229         libxl_ctxt.page_size;
230 
231 out:
232     return rc;
233 }
234 
235 /*
236  * Local variables:
237  * mode: C
238  * c-basic-offset: 4
239  * indent-tabs-mode: nil
240  * End:
241  */
242