qemu-arm
[Top][All Lists]
Advanced

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

Re: [PATCH v2 3/8] hw/intc: GICv3 ITS command queue framework


From: shashi . mallela
Subject: Re: [PATCH v2 3/8] hw/intc: GICv3 ITS command queue framework
Date: Thu, 29 Apr 2021 19:38:35 -0400

On Mon, 2021-04-19 at 11:30 +0100, Peter Maydell wrote:
> On Thu, 1 Apr 2021 at 03:41, Shashi Mallela <
> shashi.mallela@linaro.org> wrote:
> > Added functionality to trigger ITS command queue processing on
> > write to CWRITE register and process each command queue entry to
> > identify the command type and handle commands like MAPD,MAPC,SYNC.
> > 
> > Signed-off-by: Shashi Mallela <shashi.mallela@linaro.org>
> > ---
> >  hw/intc/arm_gicv3_its.c  | 347
> > +++++++++++++++++++++++++++++++++++++++
> >  hw/intc/gicv3_internal.h |  41 +++++
> >  2 files changed, 388 insertions(+)
> > 
> > diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
> > index 81373f049d..fcac33c836 100644
> > --- a/hw/intc/arm_gicv3_its.c
> > +++ b/hw/intc/arm_gicv3_its.c
> > @@ -28,6 +28,347 @@ struct GICv3ITSClass {
> >      void (*parent_reset)(DeviceState *dev);
> >  };
> > 
> > +static MemTxResult process_sync(GICv3ITSState *s, uint32_t offset)
> > +{
> > +    AddressSpace *as = &s->gicv3->sysmem_as;
> > +    uint64_t rdbase;
> > +    uint64_t value;
> > +    bool pta = false;
> > +    MemTxResult res = MEMTX_OK;
> > +
> > +    offset += NUM_BYTES_IN_DW;
> > +    offset += NUM_BYTES_IN_DW;
> > +
> > +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> > +                                     MEMTXATTRS_UNSPECIFIED,
> > &res);
> 
> If the ITS fails to read or write DMA, it does not report that
> by provoking a bus error for the CPU's write to GITS_CWRITER,
> which is what will happen if you just propagate up the MemTxResult
> from this DMA operation back through the register write function
> to its caller. The spec doesn't seem to define a particular behaviour
> for "I couldn't read the command out of the command queue", but
> it would seem sensible to pick one of the options from the "Command
> errors"
> section: ignore the command, or stall the command queue ("treat as
> valid" is the other option there but doesn't seem relevant here).
> 
> "Stall" is probably best as otherwise we might loop forever through
> a region of unreadable addresses.
>
> have implemented "stall" indication for all failing dma read/write
calls(based on the return value of each call) 
> 
> > +    if (FIELD_EX64(s->typer, GITS_TYPER, PTA)) {
> > +        /*
> > +         * only bits[47:16] are considered instead of bits [51:16]
> > +         * since with a physical address the target address must
> > be
> > +         * 64KB aligned
> > +         */
> > +        rdbase = (value >> RDBASE_OFFSET) & RDBASE_MASK;
> > +        pta = true;
> > +    } else {
> > +        rdbase = (value >> RDBASE_OFFSET) & RDBASE_PROCNUM_MASK;
> > +    }
> > +
> > +    if (!pta && (rdbase < (s->gicv3->num_cpu))) {
> > +        /*
> > +         * Current implementation makes a blocking synchronous
> > call
> > +         * for every command issued earlier,hence the internal
> > state
> > +         * is already consistent by the time SYNC command is
> > executed.
> > +         */
> > +    }
> > +
> > +    offset += NUM_BYTES_IN_DW;
> > +    return res;
> > +}
> 
> TODO: review process_mapd and process_mapc
> 
> > +
> > +static void update_cte(GICv3ITSState *s, uint16_t icid, uint64_t
> > cte)
> > +{
> > +    AddressSpace *as = &s->gicv3->sysmem_as;
> > +    uint64_t value;
> > +    uint8_t  page_sz_type;
> > +    uint64_t l2t_addr;
> > +    bool valid_l2t;
> > +    uint32_t l2t_id;
> > +    uint32_t page_sz = 0;
> > +    uint32_t max_l2_entries;
> 
> I think it's worth having a comment here:
> 
> /*
>  * The specification defines the format of level 1 entries of a
>  * 2-level table, but the format of level 2 entries and the format
>  * of flat-mapped tables is IMPDEF.
>  */
> 
> Q: why have you chosen to make the level-2 and flatmap entries
> 64 bits here ? They only need to contain the valid bit plus a
> 16 bit processor number. (We know the processor number fits in 16
> bits because the GICR_TYPER.Processor_Number field in the
> redistributor
> is that large.) Is there something I'm missing in the spec that makes
> 64-bit entries a good choice anyway ?
> 
> I chose 64 bits to be able to have the data structure ready for both
the processor number and RD_base address possibilities, in case we add
RD_Base address support in the future
> > +
> > +    if (s->ct.indirect) {
> > +        /* 2 level table */
> > +        page_sz_type = FIELD_EX64(s->baser[1], GITS_BASER,
> > PAGESIZE);
> > +
> > +        if (page_sz_type == 0) {
> > +            page_sz = GITS_ITT_PAGE_SIZE_0;
> > +        } else if (page_sz_type == 1) {
> > +            page_sz = GITS_ITT_PAGE_SIZE_1;
> > +        } else if (page_sz_type == 2) {
> > +            page_sz = GITS_ITT_PAGE_SIZE_2;
> > +        }
> 
> page_sz_type == 3 has a defined meaning: must be treated like 2.
> 
> If we're caching stuff in s->ct, maybe we should cache page_sz too ?
> 
> > +
> > +        l2t_id = icid / (page_sz / L1TABLE_ENTRY_SIZE);
> > +
> > +        value = address_space_ldq_le(as,
> > +                                     s->ct.base_addr +
> > +                                     (l2t_id *
> > L1TABLE_ENTRY_SIZE),
> > +                                     MEMTXATTRS_UNSPECIFIED,
> > NULL);
> > +
> > +        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> > +
> > +        if (valid_l2t) {
> > +            max_l2_entries = page_sz / s->ct.entry_sz;
> > +
> > +            l2t_addr = (value >> page_sz_type) &
> > +                        ((1ULL << (51 - page_sz_type)) - 1);
> 
> The spec text could be more clearly worded, but I don't think this is
> how
> the physaddr is encoded in the L1 descriptor. I think that the intent
> is
> that bits [51:0] are the physical address, with bits [N-1:0] of that
> being 0
> because the L2 table must be page aligned. Your intepretation (that
> the
> physaddr bit 0 is in bit N of the descriptor) wouldn't allow L2
> tables
> to be anywhere in the address space.
> 
> > +
> > +            address_space_write(as, l2t_addr +
> > +                                 ((icid % max_l2_entries) *
> > GITS_CTE_SIZE),
> > +                                 MEMTXATTRS_UNSPECIFIED,
> > +                                 &cte, sizeof(cte));
> 
> Writing the CTE word using address_space_write() like this will
> do the wrong thing on big-endian hosts. address_space_write() is the
> "write a string of bytes to guest memory" function, so it will write
> the uint64_t in whatever the host's byte order is. You want a fixed
> byte order, so use address_space_ldq_le() or similar.
> 
> > +        }
> > +    } else {
> > +        /* Flat level table */
> > +        address_space_write(as, s->ct.base_addr + (icid *
> > GITS_CTE_SIZE),
> > +                            MEMTXATTRS_UNSPECIFIED, &cte,
> > +                            sizeof(cte));
> > +    }
> > +}
> 
> I feel like there's a fair amount of code duplication between
> get_cte(), get_dte(), update_cte() and update_dte() which could
> perhaps be refactored out.
> 
> > +static MemTxResult process_mapc(GICv3ITSState *s, uint32_t offset)
> > +{
> > +    AddressSpace *as = &s->gicv3->sysmem_as;
> > +    uint16_t icid;
> > +    uint64_t rdbase;
> > +    bool valid;
> > +    bool pta = false;
> > +    MemTxResult res = MEMTX_OK;
> > +    uint64_t cte_entry;
> > +    uint64_t value;
> > +
> > +    offset += NUM_BYTES_IN_DW;
> > +    offset += NUM_BYTES_IN_DW;
> > +
> > +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> > +                                 MEMTXATTRS_UNSPECIFIED, &res);
> > +
> > +    icid = value & ICID_MASK;
> > +
> > +    if (FIELD_EX64(s->typer, GITS_TYPER, PTA)) {
> > +        /*
> > +         * only bits[47:16] are considered instead of bits [51:16]
> > +         * since with a physical address the target address must
> > be
> > +         * 64KB aligned
> > +         */
> > +        rdbase = (value >> RDBASE_OFFSET) & RDBASE_MASK;
> > +        pta = true;
> > +    } else {
> > +        rdbase = (value >> RDBASE_OFFSET) & RDBASE_PROCNUM_MASK;
> > +    }
> 
> GITS_TYPER.PTA is a read-only bit where we report to the guest how
> our implementation behaves (ie whether these are processor numbers
> or redistributor base addresses). We should implement one thing,
> whichever is the easy one to implement for us, not both.
> 
> The specification is giving implementations a choice here because
> there are different ways an ITS-to-redistributor interface might
> work:
>  * the ITS might talk to the redistributor by literally writing to
>    its registers via DMA (in which case setting GITS_TYPER.PTA makes
>    its life easier)
>  * the ITS might have its own backdoor or internal communications
>    channel to the redistributors (in which case it wants to know a
>    CPU number)
> 
> Looking forward, in patch 6 you add a comment:
>   * Current implementation only supports rdbase == procnum
>   * Hence rdbase physical address is ignored
> 
> So we're the second type of implementation. We should set
> GITS_TYPER.PTA=0, and then this code can simply assume that
> the data is always the "it's a CPU number" format.
> 
> > +
> > +    valid = (value >> VALID_SHIFT) & VALID_MASK;
> > +
> > +    if (valid) {
> > +        if ((icid > s->ct.max_collids) || (!pta &&
> > +                (rdbase > s->gicv3->num_cpu))) {
> > +            if (FIELD_EX64(s->typer, GITS_TYPER, SEIS)) {
> > +                /* Generate System Error here if supported */
> > +            }
> 
> Again, GITS_TYPER.SEIS is a bit which we use to tell the guest
> whether
> we're going to generate system errors or not; it's not a bit which
> we read in order to decide whether to generate them. For QEMU, the
> CPU itself doesn't have system-error support properly yet, so for
> us SEIS should be zero. In the ITS code we thus have two choices:
>  (1) just don't put anything in the code
>  (2) add a function call to an empty function called something
>      like its_raise_serror() which just has a comment noting that
>      it's a placeholder to mark where we would need to raise an
> SError
>      if we wanted to support it
> 
> I think I would favour option 1.
> 
> > +            qemu_log_mask(LOG_GUEST_ERROR,
> > +                "%s: invalid collection table attributes "
> > +                "icid %d rdbase %lu\n", __func__, icid, rdbase);
> 
> This kind of LOG_GUEST_ERROR logging doesn't need to include the
> __func__ string:
> saying "ITS MAPC command" is more comprehensible to a reader of the
> log who doesn't necessarily want to look into the QEMU source code
> than saying "process_mapc".
> 
> > +            /*
> > +             * in this implementation,in case of error
> > +             * we ignore this command and move onto the next
> > +             * command in the queue
> > +             */
> > +        } else {
> > +            if (s->ct.valid) {
> > +                /* add mapping entry to collection table */
> > +                cte_entry = (valid & VALID_MASK) |
> > +                            (pta ? ((rdbase & RDBASE_MASK) <<
> > 1ULL) :
> > +                            ((rdbase & RDBASE_PROCNUM_MASK) <<
> > 1ULL));
> > +
> > +                update_cte(s, icid, cte_entry);
> > +            }
> 
> Put the s->ct.valid check inside update_cte(). Pass the
> various parameters -- valid, pta, rdbase -- to update_cte()
> and have it do the assembly into a word. That way all the code
> dealing with the specific format of the table is inside that
> function,
> rather than being partly here and partly there.
> 
> > +        }
> > +    } else {
> > +        if (s->ct.valid) {
> > +            /* remove mapping entry from collection table */
> > +            cte_entry = 0;
> > +
> > +            update_cte(s, icid, cte_entry);
> > +        }
> > +    }
> > +
> > +    offset += NUM_BYTES_IN_DW;
> > +    offset += NUM_BYTES_IN_DW;
> > +
> > +    return res;
> > +}
> > +
> > +static void update_dte(GICv3ITSState *s, uint32_t devid, uint64_t
> > dte)
> > +{
> > +    AddressSpace *as = &s->gicv3->sysmem_as;
> > +    uint64_t value;
> > +    uint8_t  page_sz_type;
> > +    uint64_t l2t_addr;
> > +    bool valid_l2t;
> > +    uint32_t l2t_id;
> > +    uint32_t page_sz = 0;
> > +    uint32_t max_l2_entries;
> > +
> > +    if (s->dt.indirect) {
> > +        /* 2 level table */
> > +        page_sz_type = FIELD_EX64(s->baser[0], GITS_BASER,
> > PAGESIZE);
> > +
> > +        if (page_sz_type == 0) {
> > +            page_sz = GITS_ITT_PAGE_SIZE_0;
> > +        } else if (page_sz_type == 1) {
> > +            page_sz = GITS_ITT_PAGE_SIZE_1;
> > +        } else if (page_sz_type == 2) {
> > +            page_sz = GITS_ITT_PAGE_SIZE_2;
> > +        }
> > +
> > +        l2t_id = devid / (page_sz / L1TABLE_ENTRY_SIZE);
> > +
> > +        value = address_space_ldq_le(as,
> > +                                     s->dt.base_addr +
> > +                                     (l2t_id *
> > L1TABLE_ENTRY_SIZE),
> > +                                     MEMTXATTRS_UNSPECIFIED,
> > NULL);
> > +
> > +        valid_l2t = (value >> VALID_SHIFT) & VALID_MASK;
> > +
> > +        if (valid_l2t) {
> > +            max_l2_entries = page_sz / s->dt.entry_sz;
> > +
> > +            l2t_addr = (value >> page_sz_type) &
> > +                        ((1ULL << (51 - page_sz_type)) - 1);
> > +
> > +            address_space_write(as, l2t_addr +
> > +                                 ((devid % max_l2_entries) *
> > GITS_DTE_SIZE),
> > +                                 MEMTXATTRS_UNSPECIFIED, &dte,
> > sizeof(dte));
> > +        }
> > +    } else {
> > +        /* Flat level table */
> > +        address_space_write(as, s->dt.base_addr + (devid *
> > GITS_DTE_SIZE),
> > +                            MEMTXATTRS_UNSPECIFIED, &dte,
> > sizeof(dte));
> > +    }
> > +}
> > +
> > +static MemTxResult process_mapd(GICv3ITSState *s, uint64_t value,
> > +                                 uint32_t offset)
> > +{
> > +    AddressSpace *as = &s->gicv3->sysmem_as;
> > +    uint32_t devid;
> > +    uint8_t size;
> > +    uint64_t itt_addr;
> > +    bool valid;
> > +    MemTxResult res = MEMTX_OK;
> > +    uint64_t dte_entry = 0;
> > +
> > +    devid = (value >> DEVID_OFFSET) & DEVID_MASK;
> > +
> > +    offset += NUM_BYTES_IN_DW;
> > +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> > +                                 MEMTXATTRS_UNSPECIFIED, &res);
> > +    size = (value & SIZE_MASK);
> > +
> > +    offset += NUM_BYTES_IN_DW;
> > +    value = address_space_ldq_le(as, s->cq.base_addr + offset,
> > +                                 MEMTXATTRS_UNSPECIFIED, &res);
> > +    itt_addr = (value >> ITTADDR_OFFSET) & ITTADDR_MASK;
> > +
> > +    valid = (value >> VALID_SHIFT) & VALID_MASK;
> > +
> > +    if (valid) {
> > +        if ((devid > s->dt.max_devids) ||
> > +            (size > FIELD_EX64(s->typer, GITS_TYPER, IDBITS))) {
> > +            if (FIELD_EX64(s->typer, GITS_TYPER, SEIS)) {
> > +                /* Generate System Error here if supported */
> > +            }
> > +            qemu_log_mask(LOG_GUEST_ERROR,
> > +                "%s: invalid device table attributes "
> > +                "devid %d or size %d\n", __func__, devid, size);
> > +            /*
> > +             * in this implementation,in case of error
> > +             * we ignore this command and move onto the next
> > +             * command in the queue
> > +             */
> > +        } else {
> > +            if (s->dt.valid) {
> > +                /* add mapping entry to device table */
> > +                dte_entry = (valid & VALID_MASK) |
> > +                            ((size & SIZE_MASK) << 1U) |
> > +                            ((itt_addr & ITTADDR_MASK) << 6ULL);
> > +
> > +                update_dte(s, devid, dte_entry);
> > +            }
> > +        }
> > +    } else {
> > +        if (s->dt.valid) {
> > +            /* remove mapping entry from device table */
> > +            dte_entry = 0;
> > +            update_dte(s, devid, dte_entry);
> > +        }
> > +    }
> > +
> > +    offset += NUM_BYTES_IN_DW;
> > +    offset += NUM_BYTES_IN_DW;
> > +
> > +    return res;
> > +}
> 
> Review comments for process_mapc() and update_cte() apply also to
> process_mapd() and update_dte().
> 
> > +
> > +/*
> > + * Current implementation blocks until all
> > + * commands are processed
> > + */
> > +static MemTxResult process_cmdq(GICv3ITSState *s)
> 
> This should return 'void' (see comments earlier).
> 
> > +{
> > +    uint32_t wr_offset = 0;
> > +    uint32_t rd_offset = 0;
> > +    uint32_t cq_offset = 0;
> > +    uint64_t data;
> > +    AddressSpace *as = &s->gicv3->sysmem_as;
> > +    MemTxResult res = MEMTX_OK;
> > +    uint8_t cmd;
> > +
> > +    wr_offset = FIELD_EX64(s->cwriter, GITS_CWRITER, OFFSET);
> > +
> > +    if (wr_offset > s->cq.max_entries) {
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                        "%s: invalid write offset "
> > +                        "%d\n", __func__, wr_offset);
> > +        res = MEMTX_ERROR;
> > +        return res;
> > +    }
> > +
> > +    rd_offset = FIELD_EX64(s->creadr, GITS_CREADR, OFFSET);
> > +
> > +    while (wr_offset != rd_offset) {
> > +        cq_offset = (rd_offset * GITS_CMDQ_ENTRY_SIZE);
> > +        data = address_space_ldq_le(as, s->cq.base_addr +
> > cq_offset,
> > +                                      MEMTXATTRS_UNSPECIFIED,
> > &res);
> 
> I think I would just read all four quadwords of the command here
> into a local uint64_t data[4], and pass that to the process_$cmd()
> functions. That saves those functions doing the loads and handling
> possible errors doing the loads themselves.
> 
> There are 2 reasons i didn't pick reading all 4 quadwords at once:-
> 1) not all commands have valid quadword data making the dma call
> for such RES0 quadwords insignificant
> 2) Since we are now stalling on any dma read/write,in case we fail
>    during RES0 quadword read ,stalling doesnt seem significant
>
> Have optimised most of the similar-looking boilerplate code though
> > +        cmd = (data & CMD_MASK);
> > +
> > +        switch (cmd) {
> > +        case GITS_CMD_INT:
> > +            break;
> > +        case GITS_CMD_CLEAR:
> > +            break;
> > +        case GITS_CMD_SYNC:
> > +            res = process_sync(s, cq_offset);
> > +            break;
> > +        case GITS_CMD_MAPD:
> > +            res = process_mapd(s, data, cq_offset);
> > +            break;
> > +        case GITS_CMD_MAPC:
> > +            res = process_mapc(s, cq_offset);
> > +            break;
> > +        case GITS_CMD_MAPTI:
> > +            break;
> > +        case GITS_CMD_MAPI:
> > +            break;
> > +        case GITS_CMD_DISCARD:
> > +            break;
> > +        default:
> > +            break;
> > +        }
> > +        if (res == MEMTX_OK) {
> > +            rd_offset++;
> > +            rd_offset %= s->cq.max_entries;
> > +            s->creadr = FIELD_DP64(s->creadr, GITS_CREADR, OFFSET,
> > rd_offset);
> > +        } else {
> > +            qemu_log_mask(LOG_GUEST_ERROR,
> > +                "%s: %x cmd processing failed!!\n", __func__,
> > cmd);
> > +            break;
> > +        }
> > +    }
> > +    return res;
> > +}
> > +
> >  static bool extract_table_params(GICv3ITSState *s, int index)
> >  {
> >      uint16_t num_pages = 0;
> > @@ -282,6 +623,9 @@ static MemTxResult its_writel(GICv3ITSState *s,
> > hwaddr offset,
> >          break;
> >      case GITS_CWRITER:
> >          s->cwriter = deposit64(s->cwriter, 0, 32, value);
> > +        if ((s->ctlr & ITS_CTLR_ENABLED) && (s->cwriter != s-
> > >creadr)) {
> > +            result = process_cmdq(s);
> > +        }
> >          break;
> >      case GITS_CWRITER + 4:
> >          s->cwriter = deposit64(s->cwriter, 32, 32, value);
> > @@ -416,6 +760,9 @@ static MemTxResult its_writell(GICv3ITSState
> > *s, hwaddr offset,
> >          break;
> >      case GITS_CWRITER:
> >          s->cwriter = value;
> > +        if ((s->ctlr & ITS_CTLR_ENABLED) && (s->cwriter != s-
> > >creadr)) {
> > +            result = process_cmdq(s);
> > +        }
> 
> I would move the check on CTLR.ENABLED into process_cmdq() itself
> (which is already doing a writer != reader check).
> 
> >          break;
> >      case GITS_TYPER:
> >      case GITS_CREADR:
> > diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
> > index b7701e8ca5..7e1ff426ef 100644
> > --- a/hw/intc/gicv3_internal.h
> > +++ b/hw/intc/gicv3_internal.h
> > @@ -253,6 +253,12 @@ FIELD(GITS_CBASER, OUTERCACHE, 53, 3)
> >  FIELD(GITS_CBASER, INNERCACHE, 59, 3)
> >  FIELD(GITS_CBASER, VALID, 63, 1)
> > 
> > +FIELD(GITS_CREADR, STALLED, 0, 1)
> > +FIELD(GITS_CREADR, OFFSET, 5, 15)
> > +
> > +FIELD(GITS_CWRITER, RETRY, 0, 1)
> > +FIELD(GITS_CWRITER, OFFSET, 5, 15)
> > +
> >  FIELD(GITS_CTLR, ENABLED, 0, 1)
> >  FIELD(GITS_CTLR, QUIESCENT, 31, 1)
> > 
> > @@ -284,6 +290,41 @@ FIELD(GITS_TYPER, CIL, 36, 1)
> >  #define L1TABLE_ENTRY_SIZE         8
> > 
> >  #define GITS_CMDQ_ENTRY_SIZE               32
> > +#define NUM_BYTES_IN_DW                     8
> > +
> > +#define CMD_MASK                  0xff
> > +
> > +/* ITS Commands */
> > +#define GITS_CMD_CLEAR            0x04
> > +#define GITS_CMD_DISCARD          0x0F
> > +#define GITS_CMD_INT              0x03
> > +#define GITS_CMD_MAPC             0x09
> > +#define GITS_CMD_MAPD             0x08
> > +#define GITS_CMD_MAPI             0x0B
> > +#define GITS_CMD_MAPTI            0x0A
> > +#define GITS_CMD_SYNC             0x05
> > +
> > +/* MAPC command fields */
> > +#define ICID_LEN                  16
> > +#define ICID_MASK                 ((1U << ICID_LEN) - 1)
> > +#define RDBASE_LEN                32
> > +#define RDBASE_OFFSET             16
> > +#define RDBASE_MASK               ((1ULL << RDBASE_LEN) - 1)
> 
> If you want to define these by hand, you could at least use the same
> suffixes that the FIELD() macro does: _SHIFT, _LENGTH and _MASK.
> 
> > +#define RDBASE_PROCNUM_LEN        16
> > +#define RDBASE_PROCNUM_MASK       ((1ULL << RDBASE_PROCNUM_LEN) -
> > 1)
> > +
> > +#define DEVID_OFFSET              32
> > +#define DEVID_LEN                 32
> > +#define DEVID_MASK                ((1ULL << DEVID_LEN) - 1)
> > +
> > +/* MAPD command fields */
> > +#define ITTADDR_LEN               44
> > +#define ITTADDR_OFFSET            8
> > +#define ITTADDR_MASK              ((1ULL << ITTADDR_LEN) - 1)
> > +#define SIZE_MASK                 0x1f
> > +
> > +#define VALID_SHIFT               63
> > +#define VALID_MASK                0x1
> > 
> >  /**
> >   * Default features advertised by this version of ITS
> > --
> > 2.27
> 
> thanks
> -- PMM




reply via email to

[Prev in Thread] Current Thread [Next in Thread]