All of lore.kernel.org
 help / color / mirror / Atom feed
From: Laszlo Ersek <lersek@redhat.com>
To: "Gabriel L. Somlo" <somlo@cmu.edu>
Cc: peter.maydell@linaro.org, jordan.l.justen@intel.com,
	qemu-devel@nongnu.org, kraxel@redhat.com, pbonzini@redhat.com,
	markmb@redhat.com
Subject: Re: [Qemu-devel] [PATCH v4 5/6] fw_cfg: add generic non-DMA read method
Date: Thu, 5 Nov 2015 13:23:15 +0100	[thread overview]
Message-ID: <563B4A33.7000708@redhat.com> (raw)
In-Reply-To: <20151104163505.GA4505@HEDWIG.INI.CMU.EDU>

On 11/04/15 17:35, Gabriel L. Somlo wrote:
> On Wed, Nov 04, 2015 at 04:04:09PM +0100, Laszlo Ersek wrote:
>> On 11/03/15 22:40, Gabriel L. Somlo wrote:
>>> Introduce fw_cfg_data_read(), a generic read method which works
>>> on all access widths (1 through 8 bytes, inclusive), and can be
>>> used during both IOPort and MMIO read accesses.
>>>
>>> To maintain legibility, only fw_cfg_data_mem_read() (the MMIO
>>> data read method) is replaced by this patch. The new method
>>> essentially unwinds the fw_cfg_data_mem_read() + fw_cfg_read()
>>> combo, but without unnecessarily repeating all the validity
>>> checks performed by the latter on each byte being read.
>>>
>>> This patch also modifies the trace_fw_cfg_read prototype to
>>> accept a 64-bit value argument, allowing it to work properly
>>> with the new read method, but also remain backward compatible
>>> with existing call sites.
>>>
>>> Cc: Laszlo Ersek <lersek@redhat.com>
>>> Cc: Gerd Hoffmann <kraxel@redhat.com>
>>> Cc: Marc Marí <markmb@redhat.com>
>>> Signed-off-by: Gabriel Somlo <somlo@cmu.edu>
>>> ---
>>>  hw/nvram/fw_cfg.c | 44 ++++++++++++++++++++++++++++++--------------
>>>  trace-events      |  2 +-
>>>  2 files changed, 31 insertions(+), 15 deletions(-)
>>>
>>> diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c
>>> index 046fa74..9e01b46 100644
>>> --- a/hw/nvram/fw_cfg.c
>>> +++ b/hw/nvram/fw_cfg.c
>>> @@ -274,6 +274,35 @@ static int fw_cfg_select(FWCfgState *s, uint16_t key)
>>>      return ret;
>>>  }
>>>  
>>> +static uint64_t fw_cfg_data_read(void *opaque, hwaddr addr, unsigned size)
>>> +{
>>> +    FWCfgState *s = opaque;
>>> +    int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
>>> +    FWCfgEntry *e = (s->cur_entry == FW_CFG_INVALID) ? NULL :
>>> +                    &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
>>> +    uint64_t value = 0;
>>> +
>>> +    assert(size <= sizeof(value));
>>> +    if (s->cur_entry != FW_CFG_INVALID && e->data) {
>>> +        /* The least significant 'size' bytes of the return value are
>>> +         * expected to contain a string preserving portion of the item
>>> +         * data, padded with zeros to the right in case we run out early.
>>> +         */
>>> +        while (size && s->cur_offset < e->len) {
>>> +            value = (value << 8) | e->data[s->cur_offset++];
>>> +            size--;
>>> +        }
>>> +        /* If size is still not zero, we *did* run out early, so continue
>>> +         * left-shifting, to add the appropriate number of padding zeros
>>> +         * on the right.
>>> +         */
>>> +        value <<= 8 * size;
>>> +    }
>>> +
>>> +    trace_fw_cfg_read(s, value);
>>> +    return value;
>>> +}
>>
>> With the wording you proposed in
>> <http://thread.gmane.org/gmane.comp.emulators.qemu/373165/focus=373507>,
>> this looks okay.
>>
>> ... Except my (2a) proposal wasn't entirely correct, and now you get to
>> fix it up for v5. :( Apologies. (It is a different experience when you
>> see the code in full.)
>>
>> Namely, consider the case when this code is entered with:
>>
>>   (size == 8 && s->cur_offset == e->len)
>>
>> (Which is possible if the guest makes a qword read access just after
>> reading the full blob.)
>>
>> In this case, the loop won't be entered at all (which is okay), but then
>> you'll have:
>>
>>   uint64_t << 64
>>
>> which is undefined behavior. ("If the value of the right operand is
>> negative or is greater than or equal to the width of the promoted left
>> operand, the behavior is undefined.")
> 
> Yeah, we're hitting all the corner cases of the C standard, aren't we :)
> 
>> So please protect the final shift with "if (size < 8)".
>>
>> *Alternatively*, you could restrict the *outer* condition, i.e.,
>>
>>   s->cur_entry != FW_CFG_INVALID && e->data
>>
>> with (s->cur_offset < e->len).
>>
>> ... And then you can even replace the "while" with a "do" loop. (Because
>> both (size > 0) and (s->cur_offset < e->len) will be true if the loop is
>> reached at all.)
>>
>> Just the code, without comments:
>>
>>     assert(size <= sizeof(value));
>>     assert(size > 0);
>>     if (s->cur_entry != FW_CFG_INVALID && e->data &&
>>         s->cur_offset < e->len) {
>>         /* ... */
>>         do {
>>             value = (value << 8) | e->data[s->cur_offset++];
>>             size--;
>>         } while (size && s->cur_offset < e->len);
>>         /* ... */
>>         value <<= 8 * size;
>>     }
>>
>> This makes it clear that "size" is strictly smaller than sizeof(value)
>> when the shift is reached.
>>
>> I'll let you choose between the two alternatives. :)
> 
> I like the do/while idea, so here's the new function:
> 
> +static uint64_t fw_cfg_data_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    FWCfgState *s = opaque;
> +    int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
> +    FWCfgEntry *e = (s->cur_entry == FW_CFG_INVALID) ? NULL :
> +                    &s->entries[arch][s->cur_entry & FW_CFG_ENTRY_MASK];
> +    uint64_t value = 0;
> +
> +    assert(size > 0 && size <= sizeof(value));

It's a matter of taste, and I won't insist at all, just mention that I
didn't write those two assert()s as separate statements :)

Namely, with a conjunction (P1 && P2 && ... && Pn), you have the
possibility to spell the assertion as:

  assert(P1);
  assert(P2);
  ...
  assert(Pn);

And, if any one of those fails, you will know *which one*. Because the
line number in the "assertion failed" message will tell you.

> +    if (s->cur_entry != FW_CFG_INVALID && e->data && s->cur_offset < e->len) {
> +        /* The least significant 'size' bytes of the return value are
> +         * expected to contain a string preserving portion of the item
> +         * data, padded with zeros on the right in case we run out early.
> +         * In technical terms, we're composing the host-endian representation
> +         * of the big endian interpretation of the fw_cfg string.
> +         */
> +        do {
> +            value = (value << 8) | e->data[s->cur_offset++];
> +        } while (--size && s->cur_offset < e->len);
> +        /* If size is still not zero, we *did* run out early, so continue
> +         * left-shifting, to add the appropriate number of padding zeros
> +         * on the right.
> +         */
> +        value <<= 8 * size;
> +    }
> +
> +    trace_fw_cfg_read(s, value);
> +    return value;
> +}

Looks good!

> 
>>
>> Thanks, and I'm sorry.
> 
> Thank you, and no worries -- after all, what's *my* excuse for not
> catching it ? :) 

"No interest in language lawyering", perhaps? :)

> Guess I'll put a low-pass filter on blasting out v5, given how this is
> a "target rich environment" for subtle C standard violations :)

I think I'm ready to give my R-b for the final missing piece. (Not sure
if others would like to comment as well, on v4.) Your call :)

Cheers
Laszlo

> 
> Cheers,
> --Gabriel
> 
>>
>>> +
>>>  static uint8_t fw_cfg_read(FWCfgState *s)
>>>  {
>>>      int arch = !!(s->cur_entry & FW_CFG_ARCH_LOCAL);
>>> @@ -291,19 +320,6 @@ static uint8_t fw_cfg_read(FWCfgState *s)
>>>      return ret;
>>>  }
>>>  
>>> -static uint64_t fw_cfg_data_mem_read(void *opaque, hwaddr addr,
>>> -                                     unsigned size)
>>> -{
>>> -    FWCfgState *s = opaque;
>>> -    uint64_t value = 0;
>>> -    unsigned i;
>>> -
>>> -    for (i = 0; i < size; ++i) {
>>> -        value = (value << 8) | fw_cfg_read(s);
>>> -    }
>>> -    return value;
>>> -}
>>> -
>>>  static void fw_cfg_data_mem_write(void *opaque, hwaddr addr,
>>>                                    uint64_t value, unsigned size)
>>>  {
>>> @@ -485,7 +501,7 @@ static const MemoryRegionOps fw_cfg_ctl_mem_ops = {
>>>  };
>>>  
>>>  static const MemoryRegionOps fw_cfg_data_mem_ops = {
>>> -    .read = fw_cfg_data_mem_read,
>>> +    .read = fw_cfg_data_read,
>>>      .write = fw_cfg_data_mem_write,
>>>      .endianness = DEVICE_BIG_ENDIAN,
>>>      .valid = {
>>> diff --git a/trace-events b/trace-events
>>> index 72136b9..5073040 100644
>>> --- a/trace-events
>>> +++ b/trace-events
>>> @@ -196,7 +196,7 @@ ecc_diag_mem_readb(uint64_t addr, uint32_t ret) "Read diagnostic %"PRId64"= %02x
>>>  
>>>  # hw/nvram/fw_cfg.c
>>>  fw_cfg_select(void *s, uint16_t key, int ret) "%p key %d = %d"
>>> -fw_cfg_read(void *s, uint8_t ret) "%p = %d"
>>> +fw_cfg_read(void *s, uint64_t ret) "%p = %"PRIx64
>>>  fw_cfg_add_file(void *s, int index, char *name, size_t len) "%p #%d: %s (%zd bytes)"
>>>  
>>>  # hw/block/hd-geometry.c
>>>
>>

  reply	other threads:[~2015-11-05 12:23 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-11-03 21:40 [Qemu-devel] [PATCH v4 0/6] fw_cfg: spec update, misc. cleanup, optimize read Gabriel L. Somlo
2015-11-03 21:40 ` [Qemu-devel] [PATCH v4 1/6] fw_cfg: move internal function call docs to header file Gabriel L. Somlo
2015-11-03 21:40 ` [Qemu-devel] [PATCH v4 2/6] fw_cfg: amend callback behavior spec to once per select Gabriel L. Somlo
2015-11-03 21:40 ` [Qemu-devel] [PATCH v4 3/6] fw_cfg: remove offset argument from callback prototype Gabriel L. Somlo
2015-11-03 21:40 ` [Qemu-devel] [PATCH v4 4/6] fw_cfg: avoid calculating invalid current entry pointer Gabriel L. Somlo
2015-11-04 14:33   ` Laszlo Ersek
2015-11-03 21:40 ` [Qemu-devel] [PATCH v4 5/6] fw_cfg: add generic non-DMA read method Gabriel L. Somlo
2015-11-04 15:04   ` Laszlo Ersek
2015-11-04 16:35     ` Gabriel L. Somlo
2015-11-05 12:23       ` Laszlo Ersek [this message]
2015-11-05 12:57         ` Markus Armbruster
2015-11-05 13:34           ` Laszlo Ersek
2015-11-05 13:54         ` Gabriel L. Somlo
2015-11-03 21:40 ` [Qemu-devel] [PATCH v4 6/6] fw_cfg: replace ioport data read with generic method Gabriel L. Somlo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=563B4A33.7000708@redhat.com \
    --to=lersek@redhat.com \
    --cc=jordan.l.justen@intel.com \
    --cc=kraxel@redhat.com \
    --cc=markmb@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-devel@nongnu.org \
    --cc=somlo@cmu.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.