[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