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

Re: [Xen-devel] [PATCH v7] x86/hvm: Implement hvmemul_write() using real mappings



>>> On 28.09.17 at 11:07, <Paul.Durrant@xxxxxxxxxx> wrote:
>> From: Alexandru Isaila [mailto:aisaila@xxxxxxxxxxxxxxx]
>> Sent: 28 September 2017 09:52
>> +static void *hvmemul_map_linear_addr(
>> +    unsigned long linear, unsigned int bytes, uint32_t pfec,
>> +    struct hvm_emulate_ctxt *hvmemul_ctxt)
>> +{
>> +    struct vcpu *curr = current;
>> +    void *err, *mapping;
>> +
>> +    /* First and final gfns which need mapping. */
>> +    unsigned long frame = linear >> PAGE_SHIFT, first = frame;
>> +    unsigned long final = (linear + bytes - !!bytes) >> PAGE_SHIFT;
>> +    unsigned long nr_frames = ((linear + bytes) >> PAGE_SHIFT) - (linear >>
>> PAGE_SHIFT) + 1;
> 
> I'm unclear as to why you don't calculate nr_frames first and then calculate 
> final as first + nr_frames - 1? Having completely independent calculations 
> like this seems prone to error.

Nor is the calculation any better than that in v6 for the 64-bit
wrapping case. Easiest here may be to utilize truncation by
making nr_frames unsigned int (but you'll need to check carefully
that this doesn't cause issues elsewhere).

Plus the use of "linear + bytes" (without the "- !!bytes") causes the
result to be one too high when "linear" is exactly "bytes" away from
a page boundary, and "bytes" is not zero.

>> +    int i = 0;

unsigned int

>> +    /*
>> +     * mfn points to the next free slot.  All used slots have a page 
> reference
>> +     * held on them.
>> +     */
>> +    mfn_t *mfn = &hvmemul_ctxt->mfn[0];
>> +
> 
> Extraneous blank line.
> 
>> +
>> +    /*
>> +     * The caller has no legitimate reason for trying a zero-byte write, but
>> +     * final is calculate to fail safe in release builds.
>> +     *
>> +     * The maximum write size depends on the number of adjacent mfns[]
>> which
>> +     * can be vmap()'d, accouting for possible misalignment within the 
>> region.
>> +     * The higher level emulation callers are responsible for ensuring that
>> +     * mfns[] is large enough for the requested write size.
>> +     */
>> +    if ( bytes == 0 ||
>> +         nr_frames > ARRAY_SIZE(hvmemul_ctxt->mfn) )
>> +    {
>> +        ASSERT_UNREACHABLE();
>> +        goto unhandleable;
>> +    }
>> +
>> +    do {
>> +        enum hvm_translation_result res;
>> +        struct page_info *page;
>> +        pagefault_info_t pfinfo;
>> +        p2m_type_t p2mt;
>> +
> 
> Can't frame now be made local to this loop and calculated up-front using 
> linear and i? It looks like the loop could become a simpler for-loop too.
> 
>> +        /* Error checking.  Confirm that the current slot is clean. */
>> +        ASSERT(mfn_x(*mfn) == 0);
>> +
>> +        res = hvm_translate_get_page(curr, frame, true, pfec,
>> +                                     &pfinfo, &page, NULL, &p2mt);
>> +
>> +        switch ( res )
>> +        {
>> +        case HVMTRANS_okay:
>> +            break;
>> +
>> +        case HVMTRANS_bad_linear_to_gfn:
>> +            x86_emul_pagefault(pfinfo.ec, pfinfo.linear, 
>> &hvmemul_ctxt->ctxt);
>> +            err = ERR_PTR(~X86EMUL_EXCEPTION);
>> +            goto out;
>> +
>> +        case HVMTRANS_bad_gfn_to_mfn:
>> +            err = NULL;
>> +            goto out;
>> +
>> +        case HVMTRANS_gfn_paged_out:
>> +        case HVMTRANS_gfn_shared:
>> +            err = ERR_PTR(~X86EMUL_RETRY);
>> +            goto out;
>> +
>> +        default:
>> +            goto unhandleable;
>> +        }
>> +
>> +        *mfn++ = _mfn(page_to_mfn(page));
>> +        i++;
>> +
>> +        if ( p2m_is_discard_write(p2mt) )
>> +        {
>> +            err = ERR_PTR(~X86EMUL_OKAY);
>> +            goto out;
>> +        }
>> +
>> +        frame = (linear + i);

i is the number of pages, not bytes. Also stray parentheses here.

>> +        if ( hvmemul_ctxt->ctxt.addr_size < 64 )
>> +            frame = (uint32_t)frame;
>> +        frame = frame << PAGE_SHIFT;
>> +
>> +    } while ( i < nr_frames );
>> +
>> +    /* Entire access within a single frame? */
>> +    if ( first == final )

nr_frames == 1

>> +        mapping = map_domain_page(hvmemul_ctxt->mfn[0]);
>> +    /* Multiple frames? Need to vmap(). */
>> +    else if ( (mapping = vmap(hvmemul_ctxt->mfn,
>> +                              nr_frames)) == NULL )
>> +        goto unhandleable;
>> +
>> +#ifndef NDEBUG /* Poision unused mfn[]s with INVALID_MFN. */
>> +    while ( mfn < hvmemul_ctxt->mfn + ARRAY_SIZE(hvmemul_ctxt->mfn) )
>> +    {
>> +        ASSERT(mfn_x(*mfn) == 0);
>> +        *mfn++ = INVALID_MFN;
>> +    }
>> +#endif
>> +
>> +    return mapping + (linear & ~PAGE_MASK);
>> +
>> + unhandleable:
>> +    err = ERR_PTR(~X86EMUL_UNHANDLEABLE);
>> +
>> + out:
>> +    /* Drop all held references. */
>> +    while ( mfn-- > hvmemul_ctxt->mfn )
>> +        put_page(mfn_to_page(mfn_x(*mfn)));
>> +
>> +    return err;
>> +}
>> +
>> +static void hvmemul_unmap_linear_addr(
>> +    void *mapping, unsigned long linear, unsigned int bytes,
>> +    struct hvm_emulate_ctxt *hvmemul_ctxt)
>> +{
>> +    struct domain *currd = current->domain;
>> +    unsigned long frame = linear >> PAGE_SHIFT;
>> +    unsigned long final = (linear + bytes - !!bytes) >> PAGE_SHIFT;
>> +    mfn_t *mfn = &hvmemul_ctxt->mfn[0];
>> +
> 
> Extraneous blank line.
> 
>> +
>> +    ASSERT(bytes > 0);
>> +
>> +    if ( frame == final )
>> +        unmap_domain_page(mapping);
>> +    else
>> +        vunmap(mapping);
>> +
>> +    do
>> +    {
>> +        ASSERT(mfn_valid(*mfn));
>> +        paging_mark_dirty(currd, *mfn);
>> +        put_page(mfn_to_page(mfn_x(*mfn)));
>> +
>> +        *mfn++ = _mfn(0); /* Clean slot for map()'s error checking. */
>> +    } while ( ++frame < final );
> 
> Do you not have the same overflow issues here? I would have though the 
> looping construct here should be near identical to that in 
> hvmemul_map_linear_addr().

Indeed, and I _specifically_ said so in reply to v6. Alexandru,
please be quite a bit more careful. Just to emphasize it again -
any comments made to the mapping code need to also be
checked for applicability to the unmapping side.

Jan

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

 


Rackspace

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