1/**
2 * \file
3 * \brief Driver module example template.
4 *
5 * In summary, every driver (struct bfdriver) shall implement the five
6 * life-cycle functions init/set_sleep_level/attach/detach/destroy
7 * (along with additional IRQ handler functions etc.).
8 *
9 * A driver module is linked with a driver domain (see main.c in this directory).
10 * At runtime, a driver domain will instantiate a driver instance (struct bfdriver_instance)
11 * of a given module (or class if you want) using the `init` function. During the lifetime
12 * of a driver instance it may be `detached` from and re-`attach`-ed to the
13 * device, set in different sleep levels, and finally it can be `destroy`-ed.
14 *
15 * For every driver instance (i.e., struct bfdriver_instance), a corresponding
16 * control interface created and exported. The interface is defined in dcontrol.if,
17 * the corresponding code is located in the driverkit library (dcontrol_service.c).
18 *
19 * \note For more information about driver domains check the main.c file in this
20 * directory.
21 */
22/*
23 * Copyright (c) 2016, ETH Zurich.
24 * All rights reserved.
25 *
26 * This file is distributed under the terms in the attached LICENSE file.
27 * If you do not find this file, copies can be found by writing to:
28 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
29 */
30
31#include <stdlib.h>
32#include <stdio.h>
33#include <string.h>
34#include <assert.h>
35
36#include <barrelfish/barrelfish.h>
37#include <driverkit/driverkit.h>
38#include <driverkit/iommu.h>
39#include <driverkit/hwmodel.h>
40
41#include <xeon_phi/xeon_phi.h>
42#include <xeon_phi/xeon_phi_manager_client.h>
43
44#include <skb/skb.h>
45
46#include "xeon_phi_internal.h"
47#include "smpt.h"
48#include "dma_service.h"
49#include "service.h"
50#include "xphi_service.h"
51#include "interphi.h"
52#include "domain.h"
53#include "sysmem_caps.h"
54
55
56
57extern uint8_t xeon_phi_dma_enabled;
58extern char *xeon_phi_mod_uri;
59extern char *xeon_phi_mod_list;
60extern struct xeon_phi *phis;
61
62bool started = false;
63uint8_t num_xphi = 0;
64
65
66static errval_t add_xeon_phi_model_nodes(int32_t nodeid, int32_t *newnodeid) {
67    HWMODEL_QUERY_DEBUG(
68            "state_get(S),"
69            "replace_with_xeon_phi(S, %"PRIi32", E1, NewS),"
70            "writeln(E1),"
71            "state_set(NewS)",
72            nodeid);
73    errval_t err = skb_execute_query(
74            "state_get(S),"
75            "replace_with_xeon_phi(S, %"PRIi32", E1, NewS),"
76            "writeln(E1),"
77            "state_set(NewS)",
78            nodeid);
79    if(err_is_fail(err)){
80        DEBUG_SKB_ERR(err, "add_pci");
81        return err;
82    }
83    debug_printf("[knc] Allocated model node=%s, for Xeon Phi device.\n",
84            skb_get_output());
85
86    if(newnodeid != NULL){
87        skb_read_output("%d", newnodeid);
88    }
89    return err;
90}
91
92
93/**
94 * Driver initialization function. This function is called by the driver domain
95 * (see also 'create_handler' in ddomain_service.c).
96 * Typically through a request from the device manager.
97 *
98 * The init function is supposed to set `dev` to the exported service iref.
99 * The init function may use `bfi->dstate` to store additional state about the device.
100 *
101 * \param[in]   bfi   The instance of this driver.
102 * \param[in]   name  The name of this driver instance.
103 * \param[in]   flags Additional flags (The exact flags supported is device/driver specific).
104 * \param[in]   c     Capabilities (for registers etc.) as provided by the device manager.
105 *                    The exact layout of the `c` is device specific.
106 * \param[out]  dev   The service iref over which the device can be contacted.
107 *
108 * \retval SYS_ERR_OK Device initialized successfully.
109 * \retval LIB_ERR_MALLOC_FAIL Unable to allocate memory for the driver.
110 */
111static errval_t init(struct bfdriver_instance *bfi, uint64_t flags, iref_t* dev) {
112
113    errval_t err;
114    // 1. Initialize the device:
115
116    debug_printf("[knc] attaching new co-processor\n");
117    if (started) {
118        debug_printf("[knc] skipping initialization of second knc\n");
119        return SYS_ERR_OK;
120    }
121#if defined(XEON_PHI_USE_HW_MODEL)
122    // TODO HW model
123    started = true;
124#endif
125
126    /* allocate the Xeon Phi state */
127    struct xeon_phi *xphi = calloc(1, sizeof(*xphi));
128    if (xphi == NULL) {
129        return LIB_ERR_MALLOC_FAIL;
130    }
131    xphi->id = num_xphi;
132    num_xphi++;
133
134    struct capref iommuep;
135
136    if (driverkit_iommu_present(xphi->iommu_client)) {
137        err = driverkit_get_iommu_cap(bfi, &iommuep);
138        if (err_is_fail(err)) {
139            goto err_out;
140        }
141
142        err = driverkit_iommu_client_init_cl(iommuep, &xphi->iommu_client);
143        if (err_is_fail(err)) {
144            goto err_out;
145        }
146
147        int32_t init_nodeid = driverkit_iommu_get_nodeid(xphi->iommu_client);
148        debug_printf("[knc] adding xeon phi model nodes...\n");
149        err = add_xeon_phi_model_nodes(init_nodeid, &xphi->nodeid);
150        debug_printf("[knc] addded xeon phi model nodes: init_nodeid=%"PRIi32", xphi nodeid=%"PRIi32"\n",
151                init_nodeid, xphi->nodeid);
152        if (err_is_fail(err)) {
153            DEBUG_ERR(err, "add model nodes");
154            goto err_out2;
155        }
156    } else {
157        xphi->iommu_client = NULL;
158    }
159    debug_printf("[knc] iommu is %s.\n",
160                 driverkit_iommu_present(xphi->iommu_client) ? "on" : "off");
161
162    /* set the client flag to false */
163    xphi->is_client = XEON_PHI_IS_CLIENT;
164    xphi->state = XEON_PHI_STATE_NULL;
165
166    debug_printf("[knc] obtaining the bar caps.\n");
167
168    struct capref mmio;
169    err = driverkit_get_bar_cap(bfi, 1, &mmio);
170    if (err_is_fail(err)) {
171        goto err_out2;
172    }
173
174    struct capref apt;
175    err = driverkit_get_bar_cap(bfi, 0, &apt);
176    if (err_is_fail(err)) {
177        goto err_out2;
178    }
179
180    debug_printf("[knc] initialize the xeon phi\n");
181
182    err = xeon_phi_init(xphi, mmio, apt);
183    if (err_is_fail(err)) {
184        DEBUG_ERR(err, "could not do the card initialization\n");
185        goto err_out2;
186    }
187
188    debug_printf("[knc] booting it.\n");
189
190    err = xeon_phi_boot(xphi, xeon_phi_mod_uri, xeon_phi_mod_list);
191    if (err_is_fail(err)) {
192        DEBUG_ERR(err, "could not boot the card\n");
193        goto err_out2;
194    }
195
196#if 0
197    err = service_init(xphi);
198    if (err_is_fail(err)) {
199        USER_PANIC_ERR(err, "could not start the driver service\n");
200    }
201
202    uint8_t num;
203    iref_t *irefs;
204    err = xeon_phi_manager_client_register(xphi.iref, &xphi.id, &num, &irefs);
205    if (err_is_fail(err)) {
206        USER_PANIC_ERR(err, "could not register with the Xeon Phi manager\n");
207    }
208
209#endif
210
211    debug_printf("[knc] waiting for the client to connect");
212    interphi_wait_for_client(xphi);
213
214#if 0
215    err = service_register(&xphi, irefs, num);
216    if (err_is_fail(err)) {
217        USER_PANIC_ERR(err, "could not register with the other drivers");
218    }
219
220#endif
221
222    err = xdma_service_init(xphi);
223    if (err_is_fail(err)) {
224        USER_PANIC_ERR(err, "could not initialize the DMA engine\n");
225    }
226
227    err = xeon_phi_service_init(xphi);
228    if (err_is_fail(err)) {
229        USER_PANIC_ERR(err, "could not initialize the messaging service");
230    }
231
232    #if 0
233    /*
234     * in case there are more than one Xeon Phi present in the system, indicated
235     * by an id > 0, the driver will register itself with the other Xeon Phi
236     * driver instances running in the system and initializes the inter-Phi
237     * messaging frame
238     */
239    if (xphi.id != 0) {
240        XDEBUG("Doing Intra Xeon Phi setup with %u other instances\n", xphi.id);
241        for (uint32_t i = 0; i < xphi.id; ++i) {
242            /* initialize the messaging frame */
243            err = interphi_init_xphi(i, &xphi, NULL_CAP, XEON_PHI_IS_CLIENT);
244            if (err_is_fail(err)) {
245                XDEBUG("Could not initialize messaging\n");
246                continue;
247            }
248        }
249    }
250    #endif
251
252    char buf[20];
253    snprintf(buf, 20, "xeon_phi.%u.ready", xphi->id);
254
255    XDEBUG("registering ready\n");
256    err = domain_register(buf, 0xcafebabe);
257    assert(err_is_ok(err));
258
259    /* signal for the test */
260    debug_printf("Xeon Phi operational: %s\n", buf);
261
262
263    // 2. Export service to talk to the device:
264
265    // 3. Set iref of your exported service (this is reported back to Kaluga)
266    *dev = 0x0;
267
268    struct xeon_phi *tmp = phis;
269    if (phis == NULL) {
270        phis = xphi;
271    } else {
272        while(tmp->next != NULL) {
273            tmp = tmp->next;
274        }
275        tmp->next = xphi;
276    }
277
278    XDEBUG("initialization done. \n");
279    return SYS_ERR_OK;
280err_out2:
281    driverkit_iommu_client_disconnect_cl(xphi->iommu_client);
282err_out:
283    free(xphi);
284    return err;
285}
286
287/**
288 * Instructs driver to attach to the device.
289 * This function is only called if the driver has previously detached
290 * from the device (see also detach).
291 *
292 * \note After detachment the driver can not assume anything about the
293 * configuration of the device.
294 *
295 * \param[in]   bfi   The instance of this driver.
296 * \retval SYS_ERR_OK Device initialized successfully.
297 */
298static errval_t attach(struct bfdriver_instance* bfi) {
299
300    return SYS_ERR_OK;
301}
302
303/**
304 * Instructs driver to detach from the device.
305 * The driver must yield any control over to the device after this function returns.
306 * The device may be left in any state.
307 *
308 * \param[in]   bfi   The instance of this driver.
309 * \retval SYS_ERR_OK Device initialized successfully.
310 */
311static errval_t detach(struct bfdriver_instance* bfi) {
312
313    return SYS_ERR_OK;
314}
315
316/**
317 * Instructs the driver to go in a particular sleep state.
318 * Supported states are platform/device specific.
319 *
320 * \param[in]   bfi   The instance of this driver.
321 * \retval SYS_ERR_OK Device initialized successfully.
322 */
323static errval_t set_sleep_level(struct bfdriver_instance* bfi, uint32_t level) {
324
325    return SYS_ERR_OK;
326}
327
328/**
329 * Destroys this driver instance. The driver will yield any
330 * control over the device and free any state allocated.
331 *
332 * \param[in]   bfi   The instance of this driver.
333 * \retval SYS_ERR_OK Device initialized successfully.
334 */
335static errval_t destroy(struct bfdriver_instance* bfi) {
336
337    // XXX: Tear-down the service
338    bfi->device = 0x0;
339
340    return SYS_ERR_OK;
341}
342
343static errval_t get_ep(struct bfdriver_instance* bfi, bool lmp, struct capref* ret_cap)
344{
345    return SYS_ERR_OK;
346}
347
348/**
349 * Registers the driver module with the system.
350 *
351 * To link this particular module in your driver domain,
352 * add it to the addModules list in the Hakefile.
353 */
354DEFINE_MODULE(knc_module, init, attach, detach, set_sleep_level, destroy, get_ep);
355