Added infrastructure for driver domains, device driver modules and
authorGerd Zellweger <mail@gerdzellweger.com>
Mon, 15 May 2017 15:01:17 +0000 (17:01 +0200)
committerGerd Zellweger <mail@gerdzellweger.com>
Mon, 15 May 2017 15:01:17 +0000 (17:01 +0200)
device driver instances.

Signed-off-by: Gerd Zellweger <mail@gerdzellweger.com>

13 files changed:
errors/errno.fugu
if/Hakefile
if/dcontrol.if [new file with mode: 0644]
if/ddomain.if [new file with mode: 0644]
include/driverkit/driverkit.h
lib/driverkit/Hakefile
lib/driverkit/bfdrivers.ld [new file with mode: 0644]
lib/driverkit/dcontrol_service.c [new file with mode: 0644]
lib/driverkit/ddomain_client.c [new file with mode: 0644]
lib/driverkit/ddomain_service.c [new file with mode: 0644]
lib/driverkit/debug.h [new file with mode: 0644]
lib/driverkit/map_devices.c
lib/driverkit/modules.c [new file with mode: 0644]

index b0e9a8f..b951b67 100755 (executable)
@@ -807,8 +807,13 @@ errors trace TRACE_ERR_ {
     failure KERNEL_INVOKE       "Failed to set up tracing in kernel",
 };
 
-errors driverkit DRIVERKIT_ {
-    failure NO_CAP_FOUND       "No capability to map this address range.",
+errors driverkit DRIVERKIT_ERR_ {
+    failure NO_CAP_FOUND         "No capability to map this address range.",
+    failure NO_DRIVER_FOUND      "No driver found that corresponds to this class.",
+    failure DRIVER_DETACH        "Unable to destroy driver by detaching it from device.",
+    failure DRIVER_INIT          "There was a problem initializing the driver.",
+    failure CONTROL_IFACE_EXPORT "Can't export control interface.",
+    failure CONTROL_SERVICE_INIT "Failed to initialize control service.",
 };
 
 // errors in PCI/device handling
@@ -1257,4 +1262,3 @@ errors psci PSCI_ERR_{
     failure INVALID_ADDRESS  "Invald address provided",
     failure UNKNOWN_ERROR    "Error number unknown",
 };
-
index d674a4f..da5b648 100644 (file)
@@ -92,7 +92,9 @@
                "xomp_gateway",
                "sfn5122f",
                "sfn5122f_devif",
-               "descq"
+               "descq",
+               "ddomain",
+               "dcontrol"
            ],
              arch <- allArchitectures
 ] ++
                "octopus",
                "omap_sdma",
                "ping_pong",
-               "xmplthc" ],
+               "xmplthc",
+               "ddomain",
+               "dcontrol" ],
              arch <- allArchitectures
 ]
diff --git a/if/dcontrol.if b/if/dcontrol.if
new file mode 100644 (file)
index 0000000..b0205ae
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+interface dcontrol "Kaluga <-> Driver Instance control interface" {
+
+    /**
+     * Driver takes control of device again (after detach was called).
+     */
+    rpc attach(out errval err);
+
+    /**
+     * Driver gives up control over device.
+     */
+    rpc detach(out errval err);
+
+    /**
+     * Driver is advised to go change mode in certain sleep state.
+     */
+    rpc set_sleep_level(in uint32 level, out errval err);
+
+};
diff --git a/if/ddomain.if b/if/ddomain.if
new file mode 100644 (file)
index 0000000..160b645
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2017, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+interface ddomain "Kaluga <-> Driver Domain Interface" {
+
+    /**
+     * Creates a new driver within the driver domain.
+     */
+    message create(char cls[clen, 256], char name[nlen, 256], cap cap, uint64 flags);
+    message create_response(iref devif, iref control, errval err);
+
+    /**
+     * Destroys a driver inside a domain.
+     */
+    rpc destroy(in char cls[length, 256], out errval err);
+};
index a938764..4b69b40 100644 (file)
 #ifndef DRIVERKIT_H
 #define DRIVERKIT_H
 
-#include <barrelfish/types.h> 
+#include <barrelfish/types.h>
 #include <errors/errno.h>
 
+struct bfdriver;
+
+// Generic struc to track all driver instance state.
+struct bfdriver_instance {
+    char* name; //< This is owned by driverkit 'modules.c'.
+    struct bfdriver* driver; //< This is owned by driverkit 'modules.c'.
+    iref_t control; //< This is initialized by driverkit 'dcontrol_service.c'.
+
+    iref_t device; //< Driver state. This is owned by the driver implementation.
+    void* dstate; //< Driver state. This is owned by the driver implementation.
+};
+
+typedef errval_t(*driver_init_fn)(struct bfdriver_instance*, const char*, uint64_t flags, struct capref, iref_t*);
+typedef errval_t(*driver_attach_fn)(struct bfdriver_instance*);
+typedef errval_t(*driver_detach_fn)(struct bfdriver_instance*);
+typedef errval_t(*driver_set_sleep_level_fn)(struct bfdriver_instance*, uint32_t level);
+typedef errval_t(*driver_destroy_fn)(struct bfdriver_instance*);
+
+struct bfdriver {
+    char name[256];
+    driver_init_fn init;
+    driver_attach_fn attach;
+    driver_detach_fn detach;
+    driver_set_sleep_level_fn set_sleep_level;
+    driver_destroy_fn destroy;
+};
+
+errval_t driverkit_create_driver(const char* cls, const char* name, struct capref caps, uint64_t flags, iref_t* dev, iref_t* ctrl);
+errval_t driverkit_destroy(const char* name);
+void driverkit_list(struct bfdriver**, size_t*);
+struct bfdriver* driverkit_lookup_cls(const char*);
+
+/** driver domain flounder interface */
+errval_t ddomain_communication_init(iref_t kaluga_iref);
+errval_t ddomain_controller_init(void);
+
+/** driver control flounder interface */
+errval_t dcontrol_service_init(struct bfdriver_instance* bfi, struct waitset* ws);
+
 errval_t map_device_register(lpaddr_t, size_t, lvaddr_t*);
-#endif // DRIVERKIT_H
\ No newline at end of file
+
+#define __bfdrivers            __attribute__((__section__(".bfdrivers")))
+#define __visible       __attribute__((__externally_visible__))
+#define __aligned8      __attribute__ ((__aligned__ (8)))
+#define __used          __attribute__((__used__))
+#define ___PASTE(a,b) a##b
+#define __PASTE(a,b) ___PASTE(a,b)
+#define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
+
+#define DEFINE_MODULE(name, init_fn, attach_fn, detach_fn, sleep_fn, destroy_fn) \
+    struct bfdriver __UNIQUE_ID(name)               \
+        __used                                      \
+        __visible                                   \
+        __bfdrivers                                 \
+        __aligned8 = {                              \
+        #name,                                      \
+        init_fn,                                    \
+        attach_fn,                                  \
+        detach_fn,                                  \
+        sleep_fn,                                   \
+        destroy_fn                                  \
+    };
+
+
+#endif // DRIVERKIT_H
index de7038d..e9da48a 100644 (file)
@@ -7,14 +7,17 @@
 -- ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
 --
 -- Hakefile for lib/driverkit
--- 
+--
 -- Provides common functions used to build device drivers.
 --
 --------------------------------------------------------------------------
 
 [
-    build library { 
+    build library {
         target = "driverkit",
+        flounderDefs = ["ddomain", "dcontrol"],
+        flounderBindings = ["ddomain", "dcontrol"],
+        addLibraries = ["collections"],
         cFiles = (find withSuffices [".c"])
     }
-]
\ No newline at end of file
+]
diff --git a/lib/driverkit/bfdrivers.ld b/lib/driverkit/bfdrivers.ld
new file mode 100644 (file)
index 0000000..afe37fd
--- /dev/null
@@ -0,0 +1,9 @@
+SECTIONS
+{
+    .rodata.bfdrivers : {
+        PROVIDE(bfdrivers_start = .);
+        KEEP( *(.bfdrivers) )
+        PROVIDE(bfdrivers_end = .);
+    }
+}
+INSERT AFTER .text;
diff --git a/lib/driverkit/dcontrol_service.c b/lib/driverkit/dcontrol_service.c
new file mode 100644 (file)
index 0000000..3c8a026
--- /dev/null
@@ -0,0 +1,189 @@
+/**
+ * \brief Implements the message interface for a device driver instance
+ * to act on requests from a controller (probably Kaluga).
+ *
+ * Essentially these are message handler stubs that forward requests
+ * for attach/detach/set_sleep_level etc. to the corresponding
+ * driver instance (struct bfdriver_instance) within a driver domain.
+ */
+/*
+ * Copyright (c) 2017, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include <barrelfish/barrelfish.h>
+#include <barrelfish/nameservice_client.h>
+
+#include <driverkit/driverkit.h>
+
+#include <if/dcontrol_defs.h>
+#include "debug.h"
+
+/**
+ * Binding state.
+ */
+static struct bind_state {
+    struct dcontrol_binding* binding;
+    bool is_done;
+    errval_t err;
+    iref_t control_iref;
+} rpc_export;
+
+/**
+ * Tells instance to reattach to device, replies with error.
+ */
+static void dcontrol_default_attach_handler(struct dcontrol_binding* binding) {
+    struct bfdriver_instance* bfi = (struct bfdriver_instance*) binding->st;
+    assert(bfi != NULL);
+    assert(bfi->driver != NULL);
+    assert(bfi->driver->attach != NULL);
+
+    errval_t out_err = bfi->driver->attach(bfi);
+
+    errval_t err = binding->tx_vtbl.attach_response(binding, NOP_CONT, out_err);
+    if (err_is_fail(err)) {
+        USER_PANIC_ERR(err, "Sending reply failed.");
+    }
+}
+
+/**
+ * Tells instance to detach from device, replies with error.
+ */
+static void dcontrol_default_detach_handler(struct dcontrol_binding* binding) {
+    struct bfdriver_instance* bfi = (struct bfdriver_instance*) binding->st;
+    assert(bfi != NULL);
+    assert(bfi->driver != NULL);
+    assert(bfi->driver->detach != NULL);
+
+
+    errval_t out_err = bfi->driver->detach(bfi);
+
+    errval_t err = binding->tx_vtbl.detach_response(binding, NOP_CONT, out_err);
+    if (err_is_fail(err)) {
+        USER_PANIC_ERR(err, "Sending reply failed.");
+    }
+}
+
+/**
+ * Sets sleep level on the instance, replies with error.
+ */
+static void dcontrol_default_set_sleep_level_handler(struct dcontrol_binding* binding, uint32_t level) {
+    struct bfdriver_instance* bfi = (struct bfdriver_instance*) binding->st;
+    assert(bfi != NULL);
+    assert(bfi->driver != NULL);
+    assert(bfi->driver->set_sleep_level != NULL);
+
+    errval_t out_err = bfi->driver->set_sleep_level(bfi, level);
+
+    errval_t err = binding->tx_vtbl.set_sleep_level_response(binding, NOP_CONT, out_err);
+    if (err_is_fail(err)) {
+        USER_PANIC_ERR(err, "Sending reply failed.");
+    }
+}
+
+/**
+ * Virtual table for controller stubs.
+ */
+static const struct dcontrol_rx_vtbl rpc_rx_vtbl = {
+    .attach_call = dcontrol_default_attach_handler,
+    .detach_call = dcontrol_default_detach_handler,
+    .set_sleep_level_call = dcontrol_default_set_sleep_level_handler,
+};
+
+/**
+ * Called when the service has successfully exported itself.
+ *
+ * Does two things:
+ *   - Registers the service as $instance_name.control in the nameserver
+ *   - Stores iref in rpc_export
+ *
+ * \param st   st Points to the driver instance that this service belongs to.
+ * \param err  Error of the export.
+ * \param iref Interface service reference.
+ */
+static void rpc_export_cb(void *st, errval_t err, iref_t iref) {
+    assert(st != NULL);
+    struct bfdriver_instance* bfi = (struct bfdriver_instance*) st;
+    assert(bfi->name != NULL);
+
+    if (err_is_fail(err)) {
+        DRIVERKIT_DEBUG("Exporting control interface for %s failed.", bfi->name);
+        err = err_push(err, DRIVERKIT_ERR_CONTROL_IFACE_EXPORT);
+    }
+    else {
+        char* service_name = NULL;
+        int r = asprintf(&service_name, "%s.control", bfi->name);
+        assert (r > 0);
+        err = nameservice_register(service_name, iref);
+        if (err_is_fail(err)) {
+            DEBUG_ERR(err, "Can't register control interface with nameserver.");
+            err = err_push(err, DRIVERKIT_ERR_CONTROL_IFACE_EXPORT);
+        }
+        else {
+            DRIVERKIT_DEBUG("Control interface for %s exported at [%s, %"PRIuIREF"]\n", bfi->name, service_name, iref);
+        }
+        free(service_name);
+    }
+
+    rpc_export.control_iref = iref;
+    rpc_export.err = err;
+    rpc_export.is_done = true;
+}
+
+/**
+ * Called when someone connects to the control interface of the driver instance.
+ *
+ * Stores a pointer to the driver instance in the binding state
+ * and sets up handler functions.
+ *
+ * \param  st The driver instance (struct bfdriver_instance)
+ * \param  b  The newly created binding
+ * \retval SYS_ERR_OK Accepts the connection.
+ */
+static errval_t rpc_connect_cb(void *st, struct dcontrol_binding *b)
+{
+    struct bfdriver_instance* bfi = (struct bfdriver_instance*) st;
+    DRIVERKIT_DEBUG("Got a new connection for controlling driver instance %s.\n", bfi->name);
+    b->rx_vtbl = rpc_rx_vtbl;
+    b->st = st;
+
+    return SYS_ERR_OK;
+}
+
+/**
+ * Export the control interface for a given driver instance.
+ * Stores the control iref in the bfi struct.
+ *
+ * \param[in]  bfi              The driver instance to associate with the service.
+ * \param[in]  waitset          The waitset to use for this serice (if NULL, default waitset is used).
+ * \retval SYS_ERR_OK           Service successfully exported.
+ */
+errval_t dcontrol_service_init(struct bfdriver_instance* bfi, struct waitset* ws)
+{
+    rpc_export.err = SYS_ERR_OK;
+    rpc_export.is_done = false;
+    rpc_export.control_iref = 0;
+
+    struct waitset* service_ws = (ws == NULL) ? get_default_waitset() : ws;
+    errval_t err = dcontrol_export(bfi, rpc_export_cb, rpc_connect_cb,
+                                   service_ws, IDC_EXPORT_FLAGS_DEFAULT);
+    if (err_is_fail(err)) {
+        return err;
+    }
+
+    // XXX: broken
+    while (!rpc_export.is_done) {
+        messages_wait_and_handle_next();
+    }
+
+    bfi->control = rpc_export.control_iref;
+    return rpc_export.err;
+}
diff --git a/lib/driverkit/ddomain_client.c b/lib/driverkit/ddomain_client.c
new file mode 100644 (file)
index 0000000..8353305
--- /dev/null
@@ -0,0 +1,108 @@
+/**
+ * \file
+ * \brief This code shall be used in Kaluga for receiving replies of various
+ * driver domains and sending requests to driver domains.
+ *
+ * Note the client/service distinction can be confusing here. Altough, Kaluga
+ * exports the service (for bootstrapping reasons and Barrelfish limitations
+ * -> can't just ship an endpoint). We call this the client even tough
+ * it exports the service. The ddomain_service.c which runs as part of
+ * the driver domain connects to 'this file' at runtime.
+ */
+
+/*
+ * Copyright (c) 2011, 2012, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#include <barrelfish/barrelfish.h>
+#include <barrelfish/nameservice_client.h>
+
+#include <driverkit/driverkit.h>
+#include <if/ddomain_defs.h>
+
+#include "debug.h"
+
+#define SERVICE_NAME "ddomain_controller"
+
+static struct export_state {
+    struct ddomain_binding* b;
+    bool is_done;
+    errval_t err;
+} rpc_export;
+
+static void rpc_export_cb(void *st, errval_t err, iref_t iref)
+{
+    rpc_export.is_done = true;
+    rpc_export.err = err;
+
+    if (err_is_ok(err)) {
+        DRIVERKIT_DEBUG("Exported ddomain service with iref: %"PRIu32"\n", iref);
+        err = nameservice_register(SERVICE_NAME, iref);
+        assert(err_is_ok(err));
+    }
+}
+
+static void ddomain_create_response_handler(struct ddomain_binding* binding, iref_t dev, iref_t control, errval_t err) {
+    DRIVERKIT_DEBUG("Driver domain created driver rechable at [%"PRIuIREF", %"PRIuIREF"]\n", dev, control);
+}
+
+static void ddomain_destroy_response_handler(struct ddomain_binding* binding, errval_t err) {
+    DRIVERKIT_DEBUG("Driver destroyed [%s]\n", err_getstring(err));
+}
+
+static const struct ddomain_rx_vtbl rpc_rx_vtbl = {
+    .create_response = ddomain_create_response_handler,
+    .destroy_response = ddomain_destroy_response_handler,
+};
+
+static errval_t rpc_connect_cb(void *st, struct ddomain_binding *b)
+{
+    DRIVERKIT_DEBUG("Got a new connection from driver domain.\n");
+    rpc_export.b = b;
+
+    b->rx_vtbl = rpc_rx_vtbl;
+
+    // Set up continuation queue
+    b->st = NULL;
+    return SYS_ERR_OK;
+}
+
+errval_t ddomain_controller_init(void)
+{
+    rpc_export.err = SYS_ERR_OK;
+    rpc_export.is_done = false;
+
+    errval_t err = ddomain_export(&rpc_export, rpc_export_cb, rpc_connect_cb,
+                                   get_default_waitset(), IDC_EXPORT_FLAGS_DEFAULT);
+
+    if (err_is_fail(err)) {
+        return err;
+    }
+
+    // XXX: broken
+    while (!rpc_export.is_done) {
+        messages_wait_and_handle_next();
+    }
+
+    /*
+    // Hack for testing: wait for 1st controller to connect
+    while(rpc_export.b == NULL) {
+        messages_wait_and_handle_next();
+    }
+
+    struct capref c;
+    err = slot_alloc(&c);
+    assert(err_is_ok(err));
+    err = frame_alloc(&c, 4096, NULL);
+    assert(err_is_ok(err));
+    (rpc_export.b)->tx_vtbl.create((rpc_export.b), NOP_CONT, "uart", 5, "uart_instance", 14, c, 0x0);
+    // end of hack for testing
+    */
+
+    return rpc_export.err;
+}
diff --git a/lib/driverkit/ddomain_service.c b/lib/driverkit/ddomain_service.c
new file mode 100644 (file)
index 0000000..6d1ecc2
--- /dev/null
@@ -0,0 +1,141 @@
+/**
+ * \brief Implements the handler function for a driver domain
+ * to act upon requests from a device manager (i.e., Kaluga).
+ *
+ * The stubs will create, destroy driver instances using functions
+ * mainly found in `modules.c` of this library.
+ */
+
+/*
+ * Copyright (c) 2016, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include <barrelfish/barrelfish.h>
+#include <barrelfish/nameservice_client.h>
+
+#include <driverkit/driverkit.h>
+
+#include <if/ddomain_defs.h>
+#include "debug.h"
+
+/**
+ * State for connection.
+ */
+static struct bind_state {
+    struct ddomain_binding* binding;
+    bool is_done;
+    errval_t err;
+} rpc_bind;
+
+/**
+ * Act upon request to create a driver instance.
+ *
+ * \param binding Controller binding
+ * \param cls     What class to instantiate?
+ * \param cls_len Ignored.
+ * \param name    What name the driver instance should have.
+ * \param nlen    Ignored.
+ * \param cap     Capabilities for the driver instance.
+ * \param flags   Flags for the driver instance.
+ */
+static void create_handler(struct ddomain_binding* binding, const char* cls, size_t cls_len,
+                           const char* name, size_t nlen, struct capref cap, uint64_t flags) {
+    DRIVERKIT_DEBUG("Driver domain got create message from kaluga for %s\n", cls);
+
+    iref_t dev = 0, ctrl = 0;
+    errval_t err = driverkit_create_driver(cls, name, cap, flags, &dev, &ctrl);
+    if (err_is_fail(err)) {
+        DEBUG_ERR(err, "Instantiating driver failed, report this back to Kaluga.");
+    }
+
+    err = binding->tx_vtbl.create_response(binding, NOP_CONT, dev, ctrl, err);
+    if (err_is_fail(err)) {
+        USER_PANIC_ERR(err, "Sending reply failed.");
+    }
+}
+
+/**
+ * Destroy an existing driver instance.
+ *
+ * \param binding Controller binding.
+ * \param name    Name of the driver instance.
+ * \param len     Ignored
+ */
+static void destroy_handler(struct ddomain_binding* binding, const char* name, size_t len) {
+    DRIVERKIT_DEBUG("Driver domain got destroy message for instance %s\n", name);
+    errval_t err = driverkit_destroy(name);
+    if (err_is_fail(err)) {
+        DEBUG_ERR(err, "Destroying driver failed, report this back to Kaluga.");
+    }
+
+    err = binding->tx_vtbl.destroy_response(binding, NOP_CONT, err);
+    if (err_is_fail(err)) {
+        USER_PANIC_ERR(err, "Sending reply failed.");
+    }
+}
+
+/**
+ * Stubs table for functions to call on driver instance.
+ */
+static const struct ddomain_rx_vtbl rpc_rx_vtbl = {
+    .create = create_handler,
+    .destroy_call = destroy_handler,
+};
+
+/**
+ * Called if connection to the manager has completed.
+ *
+ * \param st  NULL
+ * \param err Connection initiated successfully?
+ * \param b   Created binding, this is stored in rpc_bind.
+ */
+static void rpc_bind_cb(void *st, errval_t err, struct ddomain_binding *b)
+{
+    b->st = NULL;
+    if (err_is_fail(err)) {
+        DEBUG_ERR(err, "oct_event bind failed");
+        goto out;
+    }
+
+    DRIVERKIT_DEBUG("Driver domain has connected to ddomain controller service.\n");
+    rpc_bind.binding = b;
+    rpc_bind.binding->rx_vtbl = rpc_rx_vtbl;
+
+out:
+    assert(!rpc_bind.is_done);
+    rpc_bind.is_done = true;
+    rpc_bind.err = err;
+}
+
+/**
+ * Connects to the driver domain manager.
+ *
+ * \param  connect_to iref where to connect.
+ * \retval SYS_ERR_OK Connected to the driver manager.
+ */
+errval_t ddomain_communication_init(iref_t connect_to)
+{
+    rpc_bind.err = SYS_ERR_OK;
+    rpc_bind.is_done = false;
+
+    errval_t err = ddomain_bind(connect_to, rpc_bind_cb, NULL, get_default_waitset(), IDC_BIND_FLAGS_DEFAULT);
+    if (err_is_fail(err)) {
+        return err;
+    }
+
+    // XXX: broken
+    while (!rpc_bind.is_done) {
+        messages_wait_and_handle_next();
+    }
+
+    return rpc_bind.err;
+}
diff --git a/lib/driverkit/debug.h b/lib/driverkit/debug.h
new file mode 100644 (file)
index 0000000..f20d353
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2017, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#ifndef __DRIVERKIT_DEBUG__
+#define __DRIVERKIT_DEBUG__
+
+#define ENABLE_DRIVERKIT_DEBUG 1
+
+#if defined(ENABLE_DRIVERKIT_DEBUG) || defined(GLOBAL_DEBUG)
+#define DRIVERKIT_DEBUG(x...) printf("[dkit] " x)
+#else
+#define DRIVERKIT_DEBUG(x...) ((void)0)
+#endif
+
+#endif // __DRIVERKIT_DEBUG__
index 74e21e5..04e1b8e 100644 (file)
@@ -58,7 +58,7 @@ errval_t map_device_register(lpaddr_t address, size_t size, lvaddr_t *return_add
             return err;
         }
         assert(err_is_ok(err));
-        
+
         lpaddr_t address_base = address & ~(BASE_PAGE_SIZE-1);
         lpaddr_t offset = address & (BASE_PAGE_SIZE-1);
         // XXX: should be address+size <= ...
@@ -77,5 +77,5 @@ errval_t map_device_register(lpaddr_t address, size_t size, lvaddr_t *return_add
         }
     }
 
-    return DRIVERKIT_NO_CAP_FOUND;
+    return DRIVERKIT_ERR_NO_CAP_FOUND;
 }
diff --git a/lib/driverkit/modules.c b/lib/driverkit/modules.c
new file mode 100644 (file)
index 0000000..c01705b
--- /dev/null
@@ -0,0 +1,200 @@
+/**
+ * \file
+ * \brief Driverkit module implementation.
+ *
+ * Contians helper functions to iterate over driver modules in a domain
+ * and create driver instances from driver modules.
+ */
+/*
+ * Copyright (c) 2016, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+#include <stdlib.h>
+
+#include <barrelfish/barrelfish.h>
+#include <driverkit/driverkit.h>
+#include <collections/hash_table.h>
+
+
+#include "debug.h"
+
+#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers"
+
+///< Points to start of the ELF section where the bfdriver structs are.
+extern struct bfdriver bfdrivers_start[];
+///< Points to the end of the ELF section where the bfdriver structs are.
+extern struct bfdriver bfdrivers_end[];
+
+static collections_listnode* instances = NULL;
+
+/**
+ * Initializes a pointer to point to the list of all bfdrivers.
+ *
+ * The driver structs are stored in a special ELF section (called .bfdrivers).
+ * The custom link script of the driver domain makes sure there is a bfdrivers_start
+ * and bfdrivers_end symbol at the beginning and end of that section.
+ *
+ * \param[out] start Initialized to point to the first bfdriver instance.
+ * \param[out] num   How many drivers there are.
+ */
+void driverkit_list(struct bfdriver** start, size_t* num) {
+    *start = (struct bfdriver*) bfdrivers_start;
+    *num = ((ptrdiff_t)bfdrivers_end - (ptrdiff_t)bfdrivers_start) / sizeof(struct bfdriver);
+}
+
+/**
+ * Finds a driver instance linked with the driver domain:
+ * \param  name The name of the driver we're interested in.
+ * \return      bfdriver instance with matching name or NULL in case not found.
+ */
+struct bfdriver* driverkit_lookup_cls(const char* name) {
+    assert(name != NULL);
+
+    size_t drivers = 0;
+    struct bfdriver* cur = NULL;
+    driverkit_list(&cur, &drivers);
+
+    for (size_t i=0; i<drivers; i++) {
+        if (strcmp(name, cur->name) == 0) {
+            return cur;
+        }
+        cur += 1;
+    }
+
+    return NULL; // not found
+}
+
+/**
+ * Frees a bfdriver instance. Callers need to make sure
+ * that bfi->destroy was called beforehand to clean up
+ * state that is owned by the driver.
+ *
+ * \param arg bfdriver instance.
+ */
+static void free_driver_instance(void* arg) {
+    assert (arg != NULL);
+    struct bfdriver_instance* bfi = (struct bfdriver_instance*) arg;
+    free(bfi->name);
+    free(bfi);
+}
+
+/**
+ * Compare driver instance name with supplied argument.
+ *
+ * Helper function for collection_list_* data-structure.
+ *
+ * \param[in]  elem   bfdriver instance
+ * \param[in]  name   String
+ * \retval True   iff name == bfi->name
+ * \retval False  iff name != bfi->name
+ */
+static int32_t match_name(void* elem, void* name) {
+    assert(elem != NULL);
+    assert(name != NULL);
+
+    struct bfdriver_instance* bfi = (struct bfdriver_instance*) elem;
+    assert (bfi->name != NULL);
+    assert (name != NULL);
+    if (strcmp((char*)name, bfi->name) == 0) {
+        return 1;
+    }
+    else {
+        return 0;
+    }
+}
+
+/**
+ * Destroys a driver instances identified by its name.
+ * \todo various tricky service clean-up issues are simply ignored here.
+ *
+ * \param  name Name of the instance
+ * \retval SYS_ER_OK Driver successfully destroyed.
+ * \retval DRIVERKIT_ERR_DRIVER_DETACH detaching failed.
+ */
+errval_t driverkit_destroy(const char* name) {
+    assert(name != NULL);
+    if (instances == NULL) {
+        collections_list_create(&instances, free_driver_instance);
+    }
+
+    void* namearg = (void*) name; // Get rid of the const because collections_* API is not specific enough...
+    struct bfdriver_instance* bfi = collections_list_find_if(instances, match_name, namearg);
+    errval_t err = bfi->driver->destroy(bfi);
+    if (err_is_ok(err)) {
+        struct bfdriver_instance* bfi2 = (struct bfdriver_instance*) collections_list_remove_if(instances, match_name, namearg);
+        free_driver_instance(bfi2);
+    }
+    else {
+        err = err_push(err, DRIVERKIT_ERR_DRIVER_DETACH);
+    }
+
+    return err;
+}
+
+/**
+ * Create a driver instance within the driver domain.
+ *
+ * \param[in]   cls     The class of driver (found in bfdriver).
+ * \param[in]   name    The name of the driver instance.
+ * \param[in]   caps    Caps provided to the driver's init function.
+ * \param[in]   flags   Flags provided to the driver's init function.
+ * \param[out]  device  iref of the device interface (as created by the device).
+ * \param[out]  control iref of the control interface (created as part of this function).
+ * \return      Error status of driver creation.
+ */
+errval_t driverkit_create_driver(const char* cls, const char* name,
+                                 struct capref caps, uint64_t flags,
+                                 iref_t* device, iref_t* control)
+{
+    assert(cls != NULL);
+    assert(name != NULL);
+    assert(device != NULL);
+    assert(control != NULL);
+
+    errval_t err = SYS_ERR_OK;
+
+    struct bfdriver* drv = driverkit_lookup_cls(cls);
+    if (drv == NULL) {
+        return DRIVERKIT_ERR_NO_DRIVER_FOUND;
+    }
+    DRIVERKIT_DEBUG("Using driver %s for class %s\n", drv->name, cls);
+
+    struct bfdriver_instance* inst = malloc(sizeof(struct bfdriver_instance));
+    if (inst == NULL) {
+        return LIB_ERR_MALLOC_FAIL;
+    }
+    inst->name = strdup(name);
+    if(inst->name == NULL) {
+        free(inst);
+        return LIB_ERR_MALLOC_FAIL;
+    }
+    inst->driver = drv;
+
+    err = drv->init(inst, name, flags, caps, device);
+    if (err_is_fail(err)) {
+        DEBUG_ERR(err, "Can't initialize the device");
+        free_driver_instance(inst);
+        return err_push(err, DRIVERKIT_ERR_DRIVER_INIT);
+    }
+
+    err = dcontrol_service_init(inst, NULL);
+    if (err_is_fail(err)) {
+        DEBUG_ERR(err, "Can't set-up control interface for device.");
+        free_driver_instance(inst);
+        return err_push(err, DRIVERKIT_ERR_CONTROL_SERVICE_INIT);
+    }
+    assert (inst->control > 0);
+    *control = inst->control;
+
+    DRIVERKIT_DEBUG("Driver class %s initialized successfully for driver %s.\n", drv->name, name);
+    if (instances == NULL) {
+        collections_list_create(&instances, free_driver_instance);
+    }
+    collections_list_insert(instances, inst);
+
+    return err;
+}