Merge branch 'master' of ssh://code.systems.ethz.ch:8006/diffusion/BFI/barrelfish
[barrelfish] / usr / acpi / acpi_service.c
1 /**
2  * \file
3  * \brief ACPI daemon Flounder handler functions
4  */
5
6 /*
7  * Copyright (c) 2007, 2008, 2009, 2011, ETH Zurich.
8  * All rights reserved.
9  *
10  * This file is distributed under the terms in the attached LICENSE file.
11  * If you do not find this file, copies can be found by writing to:
12  * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13  */
14 #include <stdio.h>
15
16 #include <barrelfish/barrelfish.h>
17 #include <barrelfish/nameservice_client.h>
18 #include <if/acpi_defs.h>
19 #include <acpi.h>
20 #include <mm/mm.h>
21 #include "acpi_shared.h"
22 #include "acpi_debug.h"
23 #include "ioapic.h"
24
25 #ifdef ACPI_HAVE_VTD
26 #   include "intel_vtd.h"
27 #endif
28 extern bool mm_debug;
29
30 // XXX: proper cap handling (del etc.)
31 static void mm_alloc_range_proxy_handler(struct acpi_binding* b, uint8_t sizebits,
32                                                  genpaddr_t minbase, genpaddr_t maxlimit)
33 {
34     ACPI_DEBUG("mm_alloc_range_proxy_handler: sizebits: %d, minbase: 0x%lx maxlimit: 0x%lx\n",
35                sizebits, minbase, maxlimit);
36
37     struct capref devframe = NULL_CAP;
38     /* errval_t err = mm_alloc_range(&pci_mm_physaddr, sizebits, minbase, maxlimit, &devframe, NULL); */
39     errval_t err = mm_realloc_range(&pci_mm_physaddr, sizebits, minbase, &devframe);
40     if (err_is_fail(err)) {
41         DEBUG_ERR(err, "mm realloc range failed...\n");
42     }
43
44     err = b->tx_vtbl.mm_alloc_range_proxy_response(b, NOP_CONT, devframe, err);
45     assert(err_is_ok(err));
46 }
47
48 static void mm_realloc_range_proxy_handler(struct acpi_binding* b, uint8_t sizebits,
49                                            genpaddr_t minbase)
50 {
51     ACPI_DEBUG("mm_realloc_range_proxy_handler: sizebits: %d, "
52                "minbase: 0x%"PRIxGENPADDR"\n",
53                sizebits, minbase);
54
55     struct capref devframe = NULL_CAP;
56     errval_t err = mm_realloc_range(&pci_mm_physaddr, sizebits, minbase, &devframe);
57     if (err_is_fail(err)) {
58         DEBUG_ERR(err, "mm alloc range failed...\n");
59     }
60
61     err = b->tx_vtbl.mm_realloc_range_proxy_response(b, NOP_CONT, devframe, err);
62     assert(err_is_ok(err));
63 }
64
65 // XXX: proper cap handling
66 static void mm_free_proxy_handler(struct acpi_binding* b, struct capref devframe,
67                                           uint64_t base, uint8_t sizebits)
68 {
69     ACPI_DEBUG("mm_free_proxy_handler: base: 0x%"PRIx64", sizebits: %d\n", base, sizebits);
70
71     errval_t err = mm_free(&pci_mm_physaddr, devframe, base, sizebits);
72     if (err_is_fail(err)) {
73         DEBUG_ERR(err, "mm free failed...\n");
74     }
75
76     err = b->tx_vtbl.mm_free_proxy_response(b, NOP_CONT, err);
77     assert(err_is_ok(err));
78 }
79
80 static void enable_interrupt_handler(struct acpi_binding* b, uint32_t gsi,
81         coreid_t dest, uint32_t vector)
82 {
83     errval_t err = SYS_ERR_OK;
84     err = enable_and_route_interrupt(gsi, dest, vector);
85
86     err = b->tx_vtbl.enable_and_route_interrupt_response(b, NOP_CONT, err);
87     assert(err_is_ok(err));
88
89 }
90
91 static inline bool mcfg_correct_length(uint32_t header_len)
92 {
93     return header_len >=
94             sizeof(ACPI_TABLE_MCFG) + sizeof(ACPI_MCFG_ALLOCATION);
95 }
96
97 static void get_pcie_confspace(struct acpi_binding* b)
98 {
99     ACPI_DEBUG("get_pcie_confspace\n");
100
101     errval_t err;
102     ACPI_STATUS as;
103     ACPI_TABLE_HEADER *mcfg_header;
104
105     as = AcpiGetTable("MCFG", 1, &mcfg_header);
106     if (ACPI_SUCCESS(as) && mcfg_correct_length(mcfg_header->Length)) {
107
108         ACPI_MCFG_ALLOCATION *mcfg = (void*) mcfg_header
109                 + sizeof(ACPI_TABLE_MCFG);
110         ACPI_DEBUG(
111                 "PCIe enhanced configuration region at 0x%"PRIx64" "
112                 "(segment %u, buses %u-%u)\n", mcfg->Address,
113                 mcfg->PciSegment, mcfg->StartBusNumber, mcfg->EndBusNumber);
114
115         err = b->tx_vtbl.get_pcie_confspace_response(b, NOP_CONT, SYS_ERR_OK,
116                 mcfg->Address, mcfg->PciSegment, mcfg->StartBusNumber,
117                 mcfg->EndBusNumber);
118
119     } else {
120         ACPI_DEBUG("No MCFG table found -> no PCIe enhanced configuration\n");
121         err = b->tx_vtbl.get_pcie_confspace_response(b, NOP_CONT,
122                 ACPI_ERR_NO_MCFG_TABLE, 0, 0, 0, 0);
123     }
124
125     assert(err_is_ok(err));
126 }
127
128 static void get_path_name(ACPI_HANDLE handle, char* name, size_t len)
129 {
130     ACPI_BUFFER buf = { .Length = len, .Pointer = name };
131     ACPI_STATUS s;
132
133     s = AcpiGetName(handle, ACPI_FULL_PATHNAME, &buf);
134     assert(ACPI_SUCCESS(s));
135 }
136
137 static void read_irq_table(struct acpi_binding* b, char* pathname,
138         acpi_pci_address_t addr, uint8_t bus)
139 {
140     ACPI_DEBUG("read_irq_table: (parent)%s, (%"PRIu8",%"PRIu8",%"PRIu8"), %"PRIu8"\n",
141             pathname == NULL ? "NULL" : pathname, addr.bus, addr.device, addr.function, bus);
142
143     errval_t err;
144     ACPI_STATUS as;
145     ACPI_HANDLE handle;
146
147     as = AcpiGetHandle(NULL, pathname, &handle);
148     if (ACPI_SUCCESS(as)) {
149         ACPI_HANDLE child;
150         err = acpi_get_irqtable_device(handle, addr, &child, bus);
151
152         if(err_is_fail(err)){
153             ACPI_DEBUG("get_irq_table failed.\n");
154             err = b->tx_vtbl.read_irq_table_response(b, NOP_CONT, err, NULL);
155             assert(err_is_ok(err));
156         } else {
157             char name[128];
158             get_path_name(child, name, 128);
159             ACPI_DEBUG("Sending back path name: %s\n", name);
160
161             err = b->tx_vtbl.read_irq_table_response(b, NOP_CONT, SYS_ERR_OK, name);
162             assert(err_is_ok(err));
163         }
164     }
165     else {
166         ACPI_DEBUG("Unknown ACPI Handle for path: %s\n", pathname);
167         err = b->tx_vtbl.read_irq_table_response(b, NOP_CONT,
168                 ACPI_ERR_INVALID_PATH_NAME, NULL);
169         assert(err_is_ok(err));
170     }
171 }
172
173 static void set_device_irq(struct acpi_binding *b, char* device, uint32_t irq)
174 {
175     ACPI_DEBUG("Setting link device '%s' to GSI %"PRIu32"\n", device, irq);
176
177     errval_t err = SYS_ERR_OK;
178
179     ACPI_HANDLE source;
180     ACPI_STATUS as = AcpiGetHandle(NULL, device, &source);
181     if (ACPI_FAILURE(as)) {
182         ACPI_DEBUG("  failed lookup: %s\n", AcpiFormatException(as));
183         err = ACPI_ERR_INVALID_PATH_NAME;
184         goto reply;
185     }
186
187     uint8_t data[512];
188     ACPI_BUFFER buf = { .Length = sizeof(data), .Pointer = &data };
189     as = AcpiGetCurrentResources(source, &buf);
190     if (ACPI_FAILURE(as)) {
191         ACPI_DEBUG("  failed getting _CRS: %s\n", AcpiFormatException(as));
192         err = ACPI_ERR_GET_RESOURCES;
193         goto reply;
194     }
195
196     // set chosen IRQ in first IRQ resource type
197     ACPI_RESOURCE *res = buf.Pointer;
198     switch(res->Type) {
199     case ACPI_RESOURCE_TYPE_IRQ:
200         res->Data.Irq.Interrupts[0] = irq;
201         break;
202
203     case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
204         res->Data.ExtendedIrq.Interrupts[0] = irq;
205         break;
206
207     default:
208         printf("Unknown resource type: %"PRIu32"\n", res->Type);
209         ACPI_DEBUG("NYI");
210         break;
211     }
212
213     //pcie_enable(); // XXX
214     as = AcpiSetCurrentResources(source, &buf);
215     if (ACPI_FAILURE(as)) {
216         ACPI_DEBUG("  failed setting current IRQ: %s\n",
217                   AcpiFormatException(as));
218         err = ACPI_ERR_SET_IRQ;
219         goto reply;
220     }
221
222 reply:
223     err = b->tx_vtbl.set_device_irq_response(b, NOP_CONT, err);
224     assert(err_is_ok(err));
225 }
226
227 static void reset_handler(struct acpi_binding *b)
228 {
229     if (AcpiGbl_FADT.Flags & ACPI_FADT_RESET_REGISTER) {
230         printf("Resetting machine via ACPI...\n");
231         ACPI_STATUS as = AcpiReset();
232         if (ACPI_FAILURE(as)) {
233             printf("ACPI reset failed\n");
234         }
235     }
236
237     printf("Resetting machine via syscall...\n");
238     errval_t err = sys_reboot();
239     if (err_is_fail(err)) {
240         DEBUG_ERR(err, "reboot syscall failed");
241     }
242 }
243
244 static void sleep_handler(struct acpi_binding *b, uint32_t state)
245 {
246     printf("Entering S%"PRIu32" sleep state via ACPI...\n", state);
247     ACPI_STATUS as = AcpiEnterSleepStatePrep(state);
248     if (!ACPI_SUCCESS(as)) {
249         printf("AcpiEnterSleepStatePrep failed\n");
250         return;
251     }
252
253     as = AcpiEnterSleepState(state);
254     if (!ACPI_SUCCESS(as)) {
255         printf("AcpiEnterSleepState failed\n");
256     }
257 }
258
259 static void get_handle_handler(struct acpi_binding *b, char *dev_id)
260 {
261     errval_t err = SYS_ERR_OK;;
262
263     debug_printf("Looking up handle for device '%s'\n", dev_id);
264
265     ACPI_STATUS s;
266     ACPI_HANDLE handle;
267     s = AcpiGetHandle (NULL,dev_id,&handle);
268     if (ACPI_FAILURE(s)) {
269         if (s == AE_BAD_PATHNAME) {
270             err = ACPI_ERR_INVALID_PATH_NAME;
271         } else {
272             err = ACPI_ERR_INVALID_HANDLE;
273         }
274     }
275
276     //out uint64 handle, out errval err
277     err = b->tx_vtbl.get_handle_response(b, NOP_CONT, (uint64_t)handle, err);
278     assert(err_is_ok(err));
279
280     free(dev_id);
281 }
282
283 static void eval_integer_handler(struct acpi_binding *b,
284                                  uint64_t handle, char *path)
285 {
286     errval_t err = SYS_ERR_OK;
287
288     ACPI_STATUS s;
289     ACPI_INTEGER val = 0;
290     s = acpi_eval_integer((ACPI_HANDLE)handle, path, &val);
291     if (ACPI_FAILURE(s)) {
292         if (s == AE_BAD_PATHNAME) {
293             err = ACPI_ERR_INVALID_PATH_NAME;
294         } else {
295             err = ACPI_ERR_INVALID_HANDLE;
296         }
297         val = 0;
298     }
299
300     debug_printf("eval_integer_handler\n");
301     err = b->tx_vtbl.eval_integer_response(b, NOP_CONT, val, err);
302     free(path);
303 }
304
305
306 struct acpi_rx_vtbl acpi_rx_vtbl = {
307     .get_pcie_confspace_call = get_pcie_confspace,
308     .read_irq_table_call = read_irq_table,
309     .set_device_irq_call = set_device_irq,
310     .enable_and_route_interrupt_call = enable_interrupt_handler,
311
312     .get_handle_call = get_handle_handler,
313     .eval_integer_call = eval_integer_handler,
314
315     .mm_alloc_range_proxy_call = mm_alloc_range_proxy_handler,
316     .mm_realloc_range_proxy_call = mm_realloc_range_proxy_handler,
317     .mm_free_proxy_call = mm_free_proxy_handler,
318
319     .reset_call = reset_handler,
320     .sleep_call = sleep_handler,
321 };
322
323 static void export_callback(void *st, errval_t err, iref_t iref)
324 {
325     assert(err_is_ok(err));
326
327     err = nameservice_register("acpi", iref);
328     if (err_is_fail(err)) {
329         USER_PANIC_ERR(err, "nameservice_register failed");
330     }
331     ACPI_DEBUG("acpi service exported\n");
332 }
333
334 static errval_t connect_callback(void *cst, struct acpi_binding *b)
335 {
336     ACPI_DEBUG("acpi service get connection\n");
337     b->rx_vtbl = acpi_rx_vtbl;
338     b->st = NULL;
339
340     return SYS_ERR_OK;
341 }
342
343 void start_service(void)
344 {
345     ACPI_DEBUG("start_service\n");
346
347     acpi_service_arch_init(&acpi_rx_vtbl);
348
349     errval_t r = acpi_export(NULL, export_callback, connect_callback,
350                             get_default_waitset(), IDC_EXPORT_FLAGS_DEFAULT);
351     assert(err_is_ok(r));
352
353     ACPI_DEBUG("start_service: terminated\n");
354 }