1/**
2 * \file
3 * \brief Code responsible for starting hpet
4 */
5
6/*
7 * Copyright (c) 2018 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, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19
20#include <barrelfish/barrelfish.h>
21#include <skb/skb.h>
22#include <if/octopus_defs.h>
23#include <octopus/trigger.h>
24#include <acpi_client/acpi_client.h>
25#include <hw_records.h>
26#include <if/acpi_defs.h>
27
28#include "kaluga.h"
29
30#define DRIVER_CORE 0
31#define HPET_INT_CAP 1 // should change it  to refer to the one in hpet.h
32
33errval_t start_hpet_driver(coreid_t where, struct module_info *driver,
34                           char *record ) {
35    errval_t err;
36    static struct domain_instance *inst = NULL;
37    struct driver_instance *drv = NULL;
38    int64_t address = 0, uid = 0;
39
40    KALUGA_DEBUG("start_hpet_driver: record=%s\n", record);
41    err = oct_read(record, "_ { " HW_HPET_RECORD_FIELDS_READ " }",
42                   &address, &uid);
43    if (err_is_fail(err)) {
44        DEBUG_ERR(err, "oct_read");
45        goto out;
46    }
47
48    // Fail early
49    assert(address != 0);
50
51    errval_t msgerr;
52
53    struct capref arg_caps;
54    struct cnoderef argnode_ref;
55    err = cnode_create_l2(&arg_caps, &argnode_ref);
56    struct capref devcap = {
57        .cnode = argnode_ref,
58        .slot = 0
59    };
60
61    struct capref devcap_tmp;
62    err = slot_alloc(&devcap_tmp);
63    assert(err_is_ok(err));
64
65    // store mem caps
66    err = connect_to_acpi();
67    if (err_is_fail(err)) {
68        DEBUG_ERR(err, "connect_to_acpi");
69        goto out;
70    }
71    struct acpi_binding *acpi = get_acpi_binding();
72    err = acpi->rpc_tx_vtbl.mm_alloc_range_proxy(acpi, BASE_PAGE_BITS, address,
73                                                 address + BASE_PAGE_SIZE,
74                                                 &devcap_tmp, &msgerr);
75    if (err_is_fail(err)) {
76        DEBUG_ERR(err, "mm_alloc_range_proxy\n");
77        goto out;
78    }
79    if (err_is_fail(msgerr)) {
80        DEBUG_ERR(msgerr, "mm_alloc_range_proxy msgerr\n");
81        err = msgerr;
82        goto out;
83    }
84    KALUGA_DEBUG("start_hpet_driver: got mem cap for hpet \n");
85
86    err = cap_copy(devcap, devcap_tmp);
87    assert(err_is_ok(err));
88
89    drv = ddomain_create_driver_instance("hpet_module", "key");
90    if (drv == NULL) {
91        err = DRIVERKIT_ERR_DRIVER_INIT;
92        goto out;
93    }
94
95    KALUGA_DEBUG("start_hpet_driver: created int cap for hpet \n");
96
97    // add mem cap to driver
98    drv->caps[0] = arg_caps;
99    //err = ddomain_driver_add_cap(drv, devcap);
100    if (err_is_fail(err)) {
101        USER_PANIC_ERR(err, "add_cap");
102        goto out;
103    }
104
105    drv->args[0] = malloc(50);
106    snprintf(drv->args[0], 50, "%ld", uid);
107
108    KALUGA_DEBUG("start_hpet_driver: Instantiating driver\n");
109
110    // create driver instance
111    if (driver->driverinstance == NULL) {
112        KALUGA_DEBUG("Driver instance not running, starting...\n");
113
114        // create driver domain
115        inst = instantiate_driver_domain(driver->binary, where);
116        driver->driverinstance = inst;
117
118        while (inst->b == NULL) {
119            event_dispatch(get_default_waitset());
120        }
121    }
122
123    ddomain_instantiate_driver(inst, drv);
124
125out:
126    free(drv->args[0]);
127    drv->args[0] = NULL;
128    return err;
129}
130
131/*
132 * For a hpet comp, instantiate the correct irq controller in the skb and return
133 * the input singleton range.
134 */
135static errval_t hpet_comp_get_irq_index(const char *record, char *ctrl_label,
136                                        uint64_t *irq_idx) {
137    int64_t hpet_uid, index;
138    errval_t err;
139    err = oct_read(record, "_ { " HW_HPET_COMP_RECORD_FIELDS_READ " }", &hpet_uid,
140                   &index);
141    if (err_is_fail(err))
142        return err;
143
144    err = skb_execute_query("Uid=%"PRIi64",Index=%"PRIi64",add_hpet_comp_controller(Lbl, Uid, "
145                            "Index),write('\n'),print_int_controller(Lbl)",
146                            hpet_uid, index);
147    if (err_is_fail(err)) {
148        DEBUG_SKB_ERR(err, "");
149        return err;
150    }
151
152    uint64_t end = 0;
153    err = skb_read_output("%*[^\n]\n%64[^,],%*[^,],%" SCNu64 ",%" SCNu64,
154                          ctrl_label, irq_idx, &end);
155
156    if (err_is_fail(err))
157        return err;
158
159    // we expect a range of only one element
160    assert(*irq_idx == end);
161
162    return SYS_ERR_OK;
163}
164
165
166static errval_t hpet_comp_store_irq_info(const char *device_record,
167                               struct driver_instance *drv) {
168    errval_t err;
169    struct capref *all_irq_cap = get_irq_cap();
170    struct capref intcap;
171
172    err = slot_alloc(&intcap);
173    if (err_is_fail(err)) {
174        DEBUG_ERR(err, "Could not retype int_src cap");
175        return err;
176    }
177    uint64_t irq_idx;
178    drv->args[0] = malloc(128);
179    assert(drv->args[0]);
180    err = hpet_comp_get_irq_index(device_record, drv->args[0], &irq_idx);
181    if (err_is_fail(err)) {
182        return err;
183    }
184    struct capref arg_caps;
185    struct cnoderef argnode_ref;
186    err = cnode_create_l2(&arg_caps, &argnode_ref);
187    struct capref irq_cap = {
188        .cnode = argnode_ref,
189        .slot = 0
190    };
191    err = cap_retype(irq_cap, *all_irq_cap, irq_idx, ObjType_IRQSrc, irq_idx, 1);
192    if (err_is_fail(err)) {
193        DEBUG_ERR(err, "Could not retype int_src cap");
194        return err;
195    }
196
197    return ddomain_driver_add_cap(drv, arg_caps);
198}
199
200static errval_t hpet_comp_store_index_arg(const char *record,
201                               struct driver_instance *drv) {
202    int64_t hpet_uid, index;
203    errval_t err;
204    err = oct_read(record, "_ { " HW_HPET_COMP_RECORD_FIELDS_READ " }", &hpet_uid,
205                   &index);
206    if (err_is_fail(err))
207        return err;
208
209    drv->args[1] = malloc(128);
210    assert(drv->args[1] != NULL);
211    snprintf(drv->args[1], 128, "%"PRIi64, index);
212    return err;
213}
214
215static errval_t start_hpet_comp_driver(const char *device_record){
216    errval_t err;
217
218    struct module_info *mi = find_module("hpet");
219    assert(mi != NULL);
220    assert(mi->driverinstance != NULL);
221
222    struct driver_instance *drv = NULL;
223    drv = ddomain_create_driver_instance("hpet_comp_module", "key");
224
225    err = hpet_comp_store_irq_info(device_record, drv);
226    if(err_is_fail(err)) return err;
227
228    err = hpet_comp_store_index_arg(device_record, drv);
229    if(err_is_fail(err)) return err;
230
231    KALUGA_DEBUG("start_hpet_comp_driver: Instantiating driver \n");
232    ddomain_instantiate_driver(mi->driverinstance, drv);
233
234    return SYS_ERR_OK;
235}
236
237static void hpet_comp_change_event(oct_mode_t mode, const char *device_record, void *st) {
238    if ((mode & OCT_ON_SET) > 0) {
239        KALUGA_DEBUG("HPET_comp change event: start\n");
240        errval_t err = start_hpet_comp_driver(device_record);
241        if(err_is_fail(err)){
242            DEBUG_ERR(err,"");
243        }
244    }
245}
246
247void hpet_change_event(oct_mode_t mode, const char *device_record, void *st) {
248    if ((mode & OCT_ON_SET) > 0) {
249        KALUGA_DEBUG("HPET change event: start \n");
250        errval_t err;
251
252        struct module_info *mi = find_module("hpet");
253        if (mi == NULL) {
254            printf("HPET driver not found or not declared as auto.");
255            return;
256        }
257
258        // Todo : change 0 is for core_0
259        err = start_hpet_driver(0, mi, (CONST_CAST)device_record);
260
261        switch (err_no(err)) {
262        case SYS_ERR_OK:
263            KALUGA_DEBUG("Spawned HPET driver: %s\n", mi->binary);
264            break;
265
266        case KALUGA_ERR_DRIVER_NOT_AUTO:
267            KALUGA_DEBUG("%s not declared as auto, ignore.\n", mi->binary);
268            break;
269
270        default:
271            DEBUG_ERR(err, "Unhandled error while starting hpet\n");
272            break;
273        }
274    }
275}
276
277/*
278 * Kaluga is notified (from acpi) of a new hpet. Kaluga obtains the page for reading
279 * the base registers of hpet starts hpet domain with hpet_module.
280 *
281 * The hpet_module will read the number of timers (we call this comparators)
282 * and insert for each comparator an skb hpet_comp and and octopus hpet_comp entry.
283 *
284 * Kaluga will react on this event, instantiate the hpet_comp interrupt controller driver.
285 * and start the hpet_comp_module with: the input interrupt cap and the label of
286 * the interrupt controller.
287 */
288
289static void irq_ready_event(oct_mode_t mode, const char *device_record,
290                            void *st) {
291    KALUGA_DEBUG("irq_ready_event: watching for HPET now\n");
292
293    errval_t err;
294    const char *hpet_device = HW_HPET_RECORD_REGEX;
295    err = oct_trigger_existing_and_watch(hpet_device, hpet_change_event, NULL,
296                                         NULL);
297
298    if (err_is_fail(err)) {
299        DEBUG_ERR(err, "oct_trigger... hpet_device");
300    }
301}
302
303errval_t watch_for_hpet(void) {
304    // We only start watching for HPETs once we get the base_irq_controller
305    // ready. Only then it's safe to call the add_hpet_controller method.
306
307    errval_t err;
308    const char *irq_ready = "base_irq_controller_ready {}";
309    err = oct_trigger_existing_and_watch(irq_ready, irq_ready_event, NULL, NULL);
310
311    if (err_is_fail(err)) {
312        DEBUG_ERR(err, "oct_trigger... base_irq_controller_ready");
313    }
314
315    err = oct_trigger_existing_and_watch(HW_HPET_COMP_RECORD_REGEX,
316            hpet_comp_change_event, NULL, NULL);
317
318    if (err_is_fail(err)) {
319        DEBUG_ERR(err, "oct_trigger... base_irq_controller_ready");
320    }
321    return err;
322}
323