static int vtd_page_walk_level(dma_addr_t addr, uint64_t start,

                               uint64_t end, vtd_page_walk_hook hook_fn,

                               void *private, uint32_t level,

                               bool read, bool write, bool notify_unmap)

{

    bool read_cur, write_cur, entry_valid;

    uint32_t offset;

    uint64_t slpte;

    uint64_t subpage_size, subpage_mask;

    IOMMUTLBEntry entry;

    uint64_t iova = start;

    uint64_t iova_next;

    int ret = 0;



    trace_vtd_page_walk_level(addr, level, start, end);



    subpage_size = 1ULL << vtd_slpt_level_shift(level);

    subpage_mask = vtd_slpt_level_page_mask(level);



    while (iova < end) {

        iova_next = (iova & subpage_mask) + subpage_size;



        offset = vtd_iova_level_offset(iova, level);

        slpte = vtd_get_slpte(addr, offset);



        if (slpte == (uint64_t)-1) {

            trace_vtd_page_walk_skip_read(iova, iova_next);

            goto next;

        }



        if (vtd_slpte_nonzero_rsvd(slpte, level)) {

            trace_vtd_page_walk_skip_reserve(iova, iova_next);

            goto next;

        }



        /* Permissions are stacked with parents' */

        read_cur = read && (slpte & VTD_SL_R);

        write_cur = write && (slpte & VTD_SL_W);



        /*

         * As long as we have either read/write permission, this is a

         * valid entry. The rule works for both page entries and page

         * table entries.

         */

        entry_valid = read_cur | write_cur;



        if (vtd_is_last_slpte(slpte, level)) {

            entry.target_as = &address_space_memory;

            entry.iova = iova & subpage_mask;

            /* NOTE: this is only meaningful if entry_valid == true */

            entry.translated_addr = vtd_get_slpte_addr(slpte);

            entry.addr_mask = ~subpage_mask;

            entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur);

            if (!entry_valid && !notify_unmap) {

                trace_vtd_page_walk_skip_perm(iova, iova_next);

                goto next;

            }

            trace_vtd_page_walk_one(level, entry.iova, entry.translated_addr,

                                    entry.addr_mask, entry.perm);

            if (hook_fn) {

                ret = hook_fn(&entry, private);

                if (ret < 0) {

                    return ret;

                }

            }

        } else {

            if (!entry_valid) {

                trace_vtd_page_walk_skip_perm(iova, iova_next);

                goto next;

            }

            ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte), iova,

                                      MIN(iova_next, end), hook_fn, private,

                                      level - 1, read_cur, write_cur,

                                      notify_unmap);

            if (ret < 0) {

                return ret;

            }

        }



next:

        iova = iova_next;

    }



    return 0;

}

static int vtd_page_walk_level(dma_addr_t addr, uint64_t start,

                               uint64_t end, vtd_page_walk_hook hook_fn,

                               void *private, uint32_t level,

                               bool read, bool write, bool notify_unmap)

{

    bool read_cur, write_cur, entry_valid;

    uint32_t offset;

    uint64_t slpte;

    uint64_t subpage_size, subpage_mask;

    IOMMUTLBEntry entry;

    uint64_t iova = start;

    uint64_t iova_next;

    int ret = 0;



    trace_vtd_page_walk_level(addr, level, start, end);



    subpage_size = 1ULL << vtd_slpt_level_shift(level);

    subpage_mask = vtd_slpt_level_page_mask(level);



    while (iova < end) {

        iova_next = (iova & subpage_mask) + subpage_size;



        offset = vtd_iova_level_offset(iova, level);

        slpte = vtd_get_slpte(addr, offset);



        if (slpte == (uint64_t)-1) {

            trace_vtd_page_walk_skip_read(iova, iova_next);

            goto next;

        }



        if (vtd_slpte_nonzero_rsvd(slpte, level)) {

            trace_vtd_page_walk_skip_reserve(iova, iova_next);

            goto next;

        }



        

        read_cur = read && (slpte & VTD_SL_R);

        write_cur = write && (slpte & VTD_SL_W);



        









        entry_valid = read_cur | write_cur;



        if (vtd_is_last_slpte(slpte, level)) {

            entry.target_as = &address_space_memory;

            entry.iova = iova & subpage_mask;

            

            entry.translated_addr = vtd_get_slpte_addr(slpte);

            entry.addr_mask = ~subpage_mask;

            entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur);

            if (!entry_valid && !notify_unmap) {

                trace_vtd_page_walk_skip_perm(iova, iova_next);

                goto next;

            }

            trace_vtd_page_walk_one(level, entry.iova, entry.translated_addr,

                                    entry.addr_mask, entry.perm);

            if (hook_fn) {

                ret = hook_fn(&entry, private);

                if (ret < 0) {

                    return ret;

                }

            }

        } else {

            if (!entry_valid) {

                trace_vtd_page_walk_skip_perm(iova, iova_next);

                goto next;

            }

            ret = vtd_page_walk_level(vtd_get_slpte_addr(slpte), iova,

                                      MIN(iova_next, end), hook_fn, private,

                                      level - 1, read_cur, write_cur,

                                      notify_unmap);

            if (ret < 0) {

                return ret;

            }

        }



next:

        iova = iova_next;

    }



    return 0;

}

static int FUNC_0(dma_addr_t VAR_0, uint64_t VAR_1,

                               uint64_t VAR_2, vtd_page_walk_hook VAR_3,

                               void *VAR_4, uint32_t VAR_5,

                               bool VAR_6, bool VAR_7, bool VAR_8)

{

    bool read_cur, write_cur, entry_valid;

    uint32_t offset;

    uint64_t slpte;

    uint64_t subpage_size, subpage_mask;

    IOMMUTLBEntry entry;

    uint64_t iova = VAR_1;

    uint64_t iova_next;

    int VAR_9 = 0;



    trace_vtd_page_walk_level(VAR_0, VAR_5, VAR_1, VAR_2);



    subpage_size = 1ULL << vtd_slpt_level_shift(VAR_5);

    subpage_mask = vtd_slpt_level_page_mask(VAR_5);



    while (iova < VAR_2) {

        iova_next = (iova & subpage_mask) + subpage_size;



        offset = vtd_iova_level_offset(iova, VAR_5);

        slpte = vtd_get_slpte(VAR_0, offset);



        if (slpte == (uint64_t)-1) {

            trace_vtd_page_walk_skip_read(iova, iova_next);

            goto next;

        }



        if (vtd_slpte_nonzero_rsvd(slpte, VAR_5)) {

            trace_vtd_page_walk_skip_reserve(iova, iova_next);

            goto next;

        }



        

        read_cur = VAR_6 && (slpte & VTD_SL_R);

        write_cur = VAR_7 && (slpte & VTD_SL_W);



        









        entry_valid = read_cur | write_cur;



        if (vtd_is_last_slpte(slpte, VAR_5)) {

            entry.target_as = &address_space_memory;

            entry.iova = iova & subpage_mask;

            

            entry.translated_addr = vtd_get_slpte_addr(slpte);

            entry.addr_mask = ~subpage_mask;

            entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur);

            if (!entry_valid && !VAR_8) {

                trace_vtd_page_walk_skip_perm(iova, iova_next);

                goto next;

            }

            trace_vtd_page_walk_one(VAR_5, entry.iova, entry.translated_addr,

                                    entry.addr_mask, entry.perm);

            if (VAR_3) {

                VAR_9 = VAR_3(&entry, VAR_4);

                if (VAR_9 < 0) {

                    return VAR_9;

                }

            }

        } else {

            if (!entry_valid) {

                trace_vtd_page_walk_skip_perm(iova, iova_next);

                goto next;

            }

            VAR_9 = FUNC_0(vtd_get_slpte_addr(slpte), iova,

                                      MIN(iova_next, VAR_2), VAR_3, VAR_4,

                                      VAR_5 - 1, read_cur, write_cur,

                                      VAR_8);

            if (VAR_9 < 0) {

                return VAR_9;

            }

        }



next:

        iova = iova_next;

    }



    return 0;

}

static int FUNC_0(dma_addr_t VAR_0, uint64_t VAR_1,
uint64_t VAR_2, vtd_page_walk_hook VAR_3,
void *VAR_4, uint32_t VAR_5,
bool VAR_6, bool VAR_7, bool VAR_8)
{ bool read_cur, write_cur, entry_valid; uint32_t offset; uint64_t slpte; uint64_t subpage_size, subpage_mask; IOMMUTLBEntry entry; uint64_t iova = VAR_1; uint64_t iova_next; int VAR_9 = 0; trace_vtd_page_walk_level(VAR_0, VAR_5, VAR_1, VAR_2); subpage_size = 1ULL << vtd_slpt_level_shift(VAR_5); subpage_mask = vtd_slpt_level_page_mask(VAR_5); while (iova < VAR_2) { iova_next = (iova & subpage_mask) + subpage_size; offset = vtd_iova_level_offset(iova, VAR_5); slpte = vtd_get_slpte(VAR_0, offset); if (slpte == (uint64_t)-1) { trace_vtd_page_walk_skip_read(iova, iova_next); goto next; } if (vtd_slpte_nonzero_rsvd(slpte, VAR_5)) { trace_vtd_page_walk_skip_reserve(iova, iova_next); goto next; } read_cur = VAR_6 && (slpte & VTD_SL_R); write_cur = VAR_7 && (slpte & VTD_SL_W); entry_valid = read_cur | write_cur; if (vtd_is_last_slpte(slpte, VAR_5)) { entry.target_as = &address_space_memory; entry.iova = iova & subpage_mask; entry.translated_addr = vtd_get_slpte_addr(slpte); entry.addr_mask = ~subpage_mask; entry.perm = IOMMU_ACCESS_FLAG(read_cur, write_cur); if (!entry_valid && !VAR_8) { trace_vtd_page_walk_skip_perm(iova, iova_next); goto next; } trace_vtd_page_walk_one(VAR_5, entry.iova, entry.translated_addr,
entry.addr_mask, entry.perm); if (VAR_3) { VAR_9 = VAR_3(&entry, VAR_4); if (VAR_9 < 0) { return VAR_9; } } } else { if (!entry_valid) { trace_vtd_page_walk_skip_perm(iova, iova_next); goto next; } VAR_9 = FUNC_0(vtd_get_slpte_addr(slpte), iova,
MIN(iova_next, VAR_2), VAR_3, VAR_4,
VAR_5 - 1, read_cur, write_cur,
VAR_8); if (VAR_9 < 0) { return VAR_9; } } next:
iova = iova_next; } return 0; }