[Barrelfish-users] How to write user space driver for ARM?

Wang Nan wangnan0 at huawei.com
Wed Jul 3 15:03:05 CEST 2013


Hi,
I can't find a reference implementation of ARM driver. I implement an UART driver my self. Here I want you to provide some suggestion on my design.

The key problem is to allow user space program to access ARM's memory mapping registers.

Following is my design and code fragment:

1. Export RegionType_PlatformData to init during booting:

--------------
/* called by spawn_bsp_init() */
static void
create_arch_caps(void)
{
        errval_t err;
        /* sysctl */
        err = create_caps_to_cnode(0x20000000, 0x1000, RegionType_PlatformData, &spawn_state, bootinfo);
        assert(err_is_ok(err));

        /* uart 0~2 */
        err = create_caps_to_cnode(0x20001000, 0x1000, RegionType_PlatformData, &spawn_state, bootinfo);
        assert(err_is_ok(err));
        err = create_caps_to_cnode(0x20002000, 0x1000, RegionType_PlatformData, &spawn_state, bootinfo);
        assert(err_is_ok(err));
        err = create_caps_to_cnode(0x20003000, 0x1000, RegionType_PlatformData, &spawn_state, bootinfo);
        assert(err_is_ok(err));
}
--------------

2. pass pacn to mem_serv in init:
--------------
diff --git a/usr/init/spawn.c b/usr/init/spawn.c
index e343bbe..4bf35b4 100644
--- a/usr/init/spawn.c
+++ b/usr/init/spawn.c
@@ -35,6 +35,21 @@ errval_t initialize_mem_serv(struct spawninfo *si)
         return err_push(err, INIT_ERR_COPY_SUPERCN_CAP);
     }

+    /* copy physcn to memory server */
+    struct capref init_pacn_cap = {
+       .cnode = cnode_root,
+        .slot  = ROOTCN_SLOT_PACN,
+    };
+
+    struct capref child_pacn_cap = {
+        .cnode = si->rootcn,
+        .slot  = ROOTCN_SLOT_PACN,
+    };
+
+    err = cap_copy(child_pacn_cap, init_pacn_cap);
+    if (err_is_fail(err)) {
+        return err_push(err, INIT_ERR_COPY_SUPERCN_CAP);
+    }
     return SYS_ERR_OK;
 }
------------

3. add a new function to mem_serv to let it alloc PhysAddr


--------------
diff --git a/if/mem.if b/if/mem.if
index e487bf5..71469cf 100644
--- a/if/mem.if
+++ b/if/mem.if
@@ -23,4 +23,10 @@ interface mem "Memory allocation RPC interface" {
   // XXX: Trusted call, may only be called by monitor.
   // Should move this to its own binding.
   rpc free_monitor(in give_away_cap mem_cap, in genpaddr base, in uint8 bits, out errval err);
+
+  rpc allocate_devram( in genpaddr addr,
+                       in uint8 sizebit,
+                       out errval ret,
+                       out give_away_cap devmem_cap );
 }
--------------



4. ugly code: let mem_serv alloc devram: I put the code fragment at the end of this mail.

5. driver: use devram:

+       struct mem_rpc_client *mem = get_mem_client();
+       assert(mem != NULL);
+
+       errval_t errrpc, err;
+       struct capref serial_cap;
+       errrpc = mem->vtbl.allocate_devram(mem, 0x20001000ULL, 12, &err, &serial_cap);
+       printf("allocate_devram rpc end\n");
+       assert(err_is_ok(errrpc));
+       assert(err_is_ok(err));
+       void *uart_base;
+       err = vspace_map_one_frame_attr(&uart_base,
+                       0x1000,
+                       serial_cap,
+                       KPI_PAGING_FLAGS_READ | KPI_PAGING_FLAGS_WRITE | KPI_PAGING_FLAGS_NOCACHE,
+                       NULL,
+                       NULL);
+       assert(err_is_ok(err));
+
+       printf("after vspace_map_one_frame_attr\n");
+       printf("uart_base=%p\n", uart_base);
+       assert(uart_base != NULL);
+
+       struct hi1380_uart_t *dev;
+       hi1380_uart_initialize(dev, uart_base);


My driver works, but code is ugly, especially the mem_serv part. I think the additional rpc interface
should be avoid.

Could anyone can provide a 'standard way' to write such driver?





Following code allows mem_serv management devram:
--------------
diff --git a/usr/mem_serv/mem_serv.c b/usr/mem_serv/mem_serv.c
index fba93e2..556a506 100644
--- a/usr/mem_serv/mem_serv.c
+++ b/usr/mem_serv/mem_serv.c
@@ -467,6 +467,96 @@ initialize_ram_alloc(void)
     return SYS_ERR_OK;
 }

+struct alloc_devram_response_reply {
+       struct mem_binding *b;
+       errval_t err;
+       struct capref cap;
+};
+static void
+mem_allocate_devram_handler_response_done(void *args)
+{
+       errval_t err;
+       struct alloc_devram_response_reply *r = args;
+       if(!capref_is_null(r->cap)) {
+               err = cap_delete(r->cap);
+               if(err_is_fail(err)) {
+                       DEBUG_ERR(err, "cap_delete failed after send. This memory will leak.");
+               }
+               err = msa.a.free(&msa.a, r->cap);
+               if(err_is_fail(err)) {
+                       DEBUG_ERR(err, "msa.a.free failed after send. This memory will leak.");
+               }
+       }
+       free(r);
+}
+static void
+mem_allocate_devram_handler_reply(void *args)
+{
+       struct alloc_devram_response_reply *r = args;
+       struct mem_binding *b = r->b;
+       errval_t err;
+
+       err = b->tx_vtbl.allocate_devram_response(b, MKCONT(mem_allocate_devram_handler_response_done, r),
+                       r->err, r->cap);
+       if (err_is_fail(err)) {
+               if (err_no(err) == FLOUNDER_ERR_TX_BUSY) {
+                       err = b->register_send(b, get_default_waitset(), MKCONT(mem_allocate_devram_handler_reply ,r));
+                       assert(err_is_ok(err));
+               } else {
+                       DEBUG_ERR(err, "failed to reply to memory request");
+                       mem_allocate_devram_handler_response_done(r);
+               }
+       }
+}
+
+static void
+mem_allocate_devram_handler(struct mem_binding *_binding, mem_genpaddr_t addr, uint8_t sizebit)
+{
+       /* find the device */
+       struct capref phyaddr_cap = {
+               .cnode = cnode_phyaddr,
+               .slot = 0,
+       };
+
+       errval_t err = SYS_ERR_KERNEL_MEM_INVALID;
+       for (int i = 0; i < bi->regions_length; i++) {
+               if (bi->regions[i].mr_type != RegionType_PlatformData)
+                       continue;
+
+               if (bi->regions[i].mr_base == addr) {
+                       if (bi->regions[i].mr_bits != sizebit)
+                               break;
+                       err = SYS_ERR_OK;
+                       break;
+               }
+               phyaddr_cap.slot++;
+       }
+
+       struct capref devframe;
+       err = msa.a.alloc(&msa.a, &devframe);
+       if (err_is_fail(err))
+               goto out;
+
+       err = cap_retype(devframe, phyaddr_cap, ObjType_DevFrame, sizebit);
+
+
+       struct alloc_devram_response_reply *r;
+out:
+       r = malloc(sizeof(*r));
+       assert(r != NULL);
+       r->b = _binding;
+       if (err_is_ok(err)) {
+
+
+               r->err = err;
+               r->cap = devframe;
+               /* need delete?? */
+       } else {
+               r->err = SYS_ERR_KERNEL_MEM_LOOKUP;
+               r->cap = NULL_CAP;
+       }
+       mem_allocate_devram_handler_reply(r);
+}
+
 static void export_callback(void *st, errval_t err, iref_t iref)
 {
     assert(err_is_ok(err));
@@ -479,6 +569,7 @@ static struct mem_rx_vtbl rx_vtbl = {
     .allocate_call = mem_allocate_handler,
     .available_call = mem_available_handler,
     .free_monitor_call = mem_free_handler,
+    .allocate_devram_call = mem_allocate_devram_handler,
 };
 static errval_t connect_callback(void *st, struct mem_binding *b)
--------------





More information about the Barrelfish-users mailing list