[Barrelfish-users] How to write user space driver for ARM?
Wang Nan
wangnan0 at huawei.com
Thu Jul 4 12:38:12 CEST 2013
Could you please send your patchs to me and explain a few about the code?
Thank you.
于 2013/7/4 18:04, Gerd Zellweger 写道:
> Hi Wang Nan,
>
> You are right, there are currently no user-space drivers for ARM in the tree and an easy way for user-space programs to access device register is currently missing (as well as interrupt forwarding to
> user-space). We are currently investing some effort in better support for the pandaboard platform. Therefore, we need this as well. I am currently working on a solution that works similar to what we
> have for PCI on x86 right now. I hope this will be ready soon and we can release it with the next release.
>
> If you want to test my changes prematurely, let me know. I can probably send you patches in a couple of days.
>
> Gerd
>
>
> On Wed, Jul 3, 2013 at 3:03 PM, Wang Nan <wangnan0 at huawei.com <mailto:wangnan0 at huawei.com>> wrote:
>
> 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)
> --------------
>
>
>
> _______________________________________________
> Barrelfish-users mailing list
> Barrelfish-users at lists.inf.ethz.ch <mailto:Barrelfish-users at lists.inf.ethz.ch>
> https://lists.inf.ethz.ch/mailman/listinfo/barrelfish-users
>
>
More information about the Barrelfish-users
mailing list