qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Laszlo Ersek <lersek@redhat.com>
To: "Gabriel L. Somlo" <somlo@cmu.edu>, qemu-devel@nongnu.org
Cc: peter.maydell@linaro.org, markmb@redhat.com, pbonzini@redhat.com,
	kraxel@redhat.com, jordan.l.justen@intel.com
Subject: Re: [Qemu-devel] [PATCH v4 5/6] fw_cfg: add generic non-DMA read method
Date: Wed, 4 Nov 2015 16:04:09 +0100	[thread overview]
Message-ID: <563A1E69.40602@redhat.com> (raw)
In-Reply-To: <1446586842-21793-6-git-send-email-somlo@cmu.edu>

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.")

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. :)

Thanks, and I'm sorry.
Laszlo

> +
>  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-04 15:04 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 [this message]
2015-11-04 16:35     ` Gabriel L. Somlo
2015-11-05 12:23       ` Laszlo Ersek
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=563A1E69.40602@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).