[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH v02 1/7] arm: introduce remoteprocessor iommu module



Hi Julien,

Thanks a lot for detailed review and sorry for so late reply. Was in
big rush last days.

On Sun, Jun 29, 2014 at 9:00 PM, Julien Grall <julien.grall@xxxxxxxxxx> wrote:
> Hi Andrii,
>
>
> On 26/06/14 12:07, Andrii Tseglytskyi wrote:
>>
>> This is a fisrst patch from patch series which was
>
>
> s/firsrst/first/
>
> Although I don't think you have to say that this is the first patch :). This
> is not useful for the commit message.
>
>
>> developed to handle remote (external) processors
>> memory management units. Remote processors are
>> typically used for graphic rendering (GPUs) and
>> high quality video decoding (IPUs). They are typically
>> installed on such multimedia SoCs as OMAP4 / OMAP5.
>>
>> As soon as remoteprocessor MMU typically works with
>> pagetables filled by physical addresses, which are
>> allocated by domU kernel, it is almost impossible to
>> use them under Xen, intermediate physical addresses
>> allocated by kernel, need to be translated to machine
>> addresses.
>>
>> This patch introduces a simple framework to perform
>> pfn -> mfn translation for external MMUs.
>> It introduces basic data structures and algorithms
>> needed for translation.
>>
>> Typically, when MMU is configured, some it registers
>> are updated by new values. Introduced frameworks
>> uses traps as starting point of remoteproc MMUs
>> pagetables translation.
>>
>> Change-Id: Ia4d311a40289df46a003f5ae8706c150bee1885d
>> Signed-off-by: Andrii Tseglytskyi <andrii.tseglytskyi@xxxxxxxxxxxxxxx>
>
>
> [..]
>
>
>> +static struct mmu_info *mmu_list[] = {
>> +};
>> +
>> +#define mmu_for_each(pfunc, data)                       \
>> +({                                                      \
>> +    u32 __i;                                            \
>> +    int __res = 0;                                      \
>> +                                                        \
>> +    for ( __i = 0; __i < ARRAY_SIZE(mmu_list); __i++ )  \
>> +    {                                                   \
>> +        __res |= pfunc(mmu_list[__i], data);            \
>
>
> Using _res |= will result to a wrong errno at the end.
> See the usage in iommu mmu_init_all.
>
> I would use
>
> __res = pfunc(...)
> if ( !__res )
>   break;
>
> And therefore modify mmu_check to return 1 if failing, 0 otherwise.
>
> This will also avoid to continue to browse all the MMU for nothing.
>

OK.

>
>> +    }                                                   \
>> +    __res;                                              \
>> +})
>> +
>> +static int mmu_check_mem_range(struct mmu_info *mmu, paddr_t addr)
>> +{
>> +    if ( (addr >= mmu->mem_start) && (addr < (mmu->mem_start +
>> mmu->mem_size)) )
>> +        return 1;
>> +
>> +    return 0;
>> +}
>> +
>> +static inline struct mmu_info *mmu_lookup(u32 addr)
>> +{
>> +    u32 i;
>> +
>> +    /* enumerate all registered MMU's and check is address in range */
>> +    for ( i = 0; i < ARRAY_SIZE(mmu_list); i++ )
>> +    {
>> +        if ( mmu_check_mem_range(mmu_list[i], addr) )
>> +            return mmu_list[i];
>> +    }
>> +
>> +    return NULL;
>> +}
>> +
>> +static int mmu_mmio_check(struct vcpu *v, paddr_t addr)
>> +{
>> +    return mmu_for_each(mmu_check_mem_range, addr);
>> +}
>
>
> This solution leads any guest to access to the MMU and therefore program it.
> If you plan to use for passthrough, you have to find a way to say that a
> specific domain is able to use the MMU, maybe an hypercall. Otherwise this
> is a security issue.
>
> IIRC I have already raised this concern on V1 :). It would be nice if you
> resolve it ASAP, because I suspect it will need some rework in the way you
> handle MMNU.
>

Agree. I need to think how to solve this. I don't think that the
hypercall is what is needed here.

>
>> +static int mmu_copy_pagetable(struct mmu_info *mmu, struct mmu_pagetable
>> *pgt)
>> +{
>> +    void __iomem *pagetable = NULL;
>> +    u32 maddr, i;
>> +
>> +    ASSERT(mmu);
>> +    ASSERT(pgt);
>> +
>> +    if ( !pgt->paddr )
>> +        return -EINVAL;
>> +
>> +    /* pagetable size can be more than one page */
>> +    for ( i = 0; i < MMU_PGD_TABLE_SIZE(mmu) / PAGE_SIZE; i++ )
>> +    {
>> +        /* lookup address where remoteproc pagetable is stored by kernel
>> */
>> +        maddr = p2m_lookup(current->domain, pgt->paddr + i * PAGE_SIZE,
>> NULL);
>> +        if ( !maddr )
>> +        {
>> +            pr_mmu("failed to translate 0x%08lx to maddr", pgt->paddr + i
>> * PAGE_SIZE);
>> +            return -EINVAL;
>> +        }
>> +
>> +        pagetable = ioremap_nocache(maddr, MMU_PGD_TABLE_SIZE(mmu));
>
>
> ioremap_* should only be used to map device memory. For the guest memory you
> have to use copy_from_guest helper.
>

OK. Just a small question - if I use copy_from_guest(), can I copy
from physical pointer ?  Here I have an address, which points to exact
physical memory.

>
>> +struct mmu_pagetable *mmu_pagetable_lookup(struct mmu_info *mmu, u32
>> addr, bool is_maddr)
>
>
> you have to use paddr_t for addr. The number of bit for the physical address
> can be greater than 40 bits.
>

OK

> The remark is the same everywhere within this file.
>
> [..]
>
>
>> +static u32 mmu_translate_pagetable(struct mmu_info *mmu, u32 paddr)
>> +{
>> +    struct mmu_pagetable *pgt;
>> +    int res;
>> +
>> +    /* lookup using machine address first */
>> +    pgt = mmu_pagetable_lookup(mmu, paddr, true);
>> +    if ( !pgt )
>> +    {
>> +        /* lookup using kernel physical address */
>> +        pgt = mmu_pagetable_lookup(mmu, paddr, false);
>> +        if ( !pgt )
>> +        {
>> +            /* if pagetable doesn't exists in lookup list - allocate it
>> */
>> +            pgt = mmu_alloc_pagetable(mmu, paddr);
>
>
> The function mmu_alloc_pagetable can return NULL. But you never check the
> return value and dereference it just below.
>

OK. Thank you for catching this.

>
>> +        }
>> +    }
>> +
>> +    pgt->maddr = MMU_INVALID_ADDRESS;
>
>
> [..]
>
>
>> +static int mmu_mmio_read(struct vcpu *v, mmio_info_t *info)
>> +{
>> +    struct mmu_info *mmu = NULL;
>> +    unsigned long flags;
>> +    register_t *r;
>> +
>> +    r = select_user_reg(guest_cpu_user_regs(), info->dabt.reg);
>> +
>> +    ASSERT(r);
>
>
> The ASSERT is pointless, select_user_reg will never return NULL.

OK

>
> [..]
>
>
>> +static int mmu_mmio_write(struct vcpu *v, mmio_info_t *info)
>> +{
>> +    struct mmu_info *mmu = NULL;
>> +    unsigned long flags;
>> +    register_t *r;
>> +    u32 new_addr, val;
>> +
>> +    r = select_user_reg(guest_cpu_user_regs(), info->dabt.reg);
>> +
>> +    ASSERT(r);
>
>
> Same here.
>

OK

>
>> +    /* dom0 should not access remoteproc MMU */
>> +    if ( 0 == current->domain->domain_id )
>> +        return 1;
>
>
> Why this restriction?
>

We agreed that remoteproc will be handled by domU, which doesn't has 1
to 1 memory mapping.
If remoteproc belongs to dom0, translation is not needed - it MMU will
be configured with machine pointers.

>
>> +    /* find corresponding MMU */
>> +    mmu = mmu_lookup(info->gpa);
>> +    if ( !mmu )
>> +    {
>> +        pr_mmu("can't get mmu for addr 0x%08x", (u32)info->gpa);
>> +        return -EINVAL;
>> +    }
>> +
>> +    ASSERT(v->domain == current->domain);
>
>
> I guess this restriction is because you are using current in
> mmu_trap_translate_pagetable?

Right. I just tried to make sure that p2m_lookup is called with proper
domain pointer.

>
> So, the iohandler is usually call on the current VCPU, there is no need to
> worry about it. Futhermore, I would pass the vcpu/domain in argument of the
> next function.
>

Can be. In first revision of these patches domain passed as a
parameter. But that leaded to one more additional parameter in all
functions below this call. I found that I can reduce arg list if I use
current-> domain pointer.

>
>> +static int mmu_init(struct mmu_info *mmu, u32 data)
>> +{
>> +    ASSERT(mmu);
>> +    ASSERT(!mmu->mem_map);
>> +
>> +    INIT_LIST_HEAD(&mmu->pagetables_list);
>> +
>> +    /* map MMU memory */
>> +    mmu->mem_map = ioremap_nocache(mmu->mem_start, mmu->mem_size);
>> +       if ( !mmu->mem_map )
>
>
> It looks like there is a hard tab here.
>

OK. Thank you for catching.


> [..]
>
>
>> +static int mmu_init_all(void)
>> +{
>> +    int res;
>> +
>> +    res = mmu_for_each(mmu_init, 0);
>> +    if ( res )
>> +    {
>> +        printk("%s error during init %d\n", __func__, res);
>> +        return res;
>> +    }
>
>
> Hmmm... do_initcalls doesn't check the return value. How your code behave we
> one of the MMU has not been initialized?
>
> I think do_initcalls & co should check the return, but as it's the common
> code I don't know how x86 respect this convention to return 0 if succeded.
> Ian, Stefano, any thoughs?
>

I would like to make this specific to ARM only if possible.

> [..]
>
>> diff --git a/xen/include/xen/remoteproc_iommu.h
>> b/xen/include/xen/remoteproc_iommu.h
>
>
> I think this file as
>
>
>> new file mode 100644
>> index 0000000..22e2951
>> --- /dev/null
>> +++ b/xen/include/xen/remoteproc_iommu.h
>
>
> The remoteproc things is ARM specific, right? If so, this header should be
> moved in include/asm-arm.
>

OK

>> +
>> +#define MMU_INVALID_ADDRESS ((u32)(-1))
>
>
> You should not assume that the MMU ADDRESS is always 32 bits. Please use
> paddr_t here.
>

Sure. Thank you. Next series will contain paddr_t everywhere, where
u32 is used for address definition.

>
>> +#define pr_mmu(fmt, ...) \
>> +       printk("%s: %s: "fmt"\n", __func__, ((mmu) ? (mmu)->name : ""),
>> ##__VA_ARGS__)
>
>
> Hmmm, you are assuming that mmu is existing within the function. You should
> pass the mmu in parameter of this macro.
>
> Also, most of the usage are for an error (except the one in mmu_page_alloc).
> I would prefix it by XENLOG_ERROR.
>

OK

>
>> +struct pagetable_data {
>> +       /* 1st level translation */
>> +       u32 pgd_shift;
>> +       u32 pte_shift;
>> +       u32 super_shift;
>> +       u32 section_shift;
>> +       /* 2nd level translation */
>> +       u32 pte_large_shift;
>> +};
>
>
> No hard tab in Xen. Please remove them.
>

OK

>
>> +
>> +struct mmu_pagetable {
>> +       u32                                     *hyp_pagetable;
>> +       u32                                     *kern_pagetable;
>> +       u32                                     paddr;
>> +       u32                                     maddr;
>> +       struct list_head        link_node;
>> +       u32                                     page_counter;
>> +};
>
>
> Same here.
>

OK

>
>> +
>> +struct mmu_info {
>> +       const char                              *name;
>> +       const struct pagetable_data *pg_data;
>> +       /* register where phys pointer to pagetable is stored */
>> +       u32                                     *trap_offsets;
>> +       paddr_t                         mem_start;
>> +       u32                                     mem_size;
>> +       spinlock_t                      lock;
>> +       struct list_head        pagetables_list;
>> +       u32                                     num_traps;
>> +       void __iomem            *mem_map;
>> +       u32     (*translate_pfunc)(struct mmu_info *, struct mmu_pagetable
>> *);
>> +       void (*print_pagetable_pfunc)(struct mmu_info *);
>> +};
>
>
> Same here.
>
> Regards,
>
> --
> Julien Grall



-- 

Andrii Tseglytskyi | Embedded Dev
GlobalLogic
www.globallogic.com

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.