def intremaps_equiv(conj, ctx, kernelstate):
    index = util.FreshBitVec('index', dt.size_t)  # intremap index

    conj.append(
        z3.ForAll([index],
                  z3.Implies(
                      is_intremap_valid(index),
                      util.global_field_element(
                          ctx, '@intremap_table', 'state',
                          index) == kernelstate.intremaps[index].state)))

    conj.append(
        z3.ForAll([index],
                  z3.Implies(
                      is_intremap_valid(index),
                      util.global_field_element(
                          ctx, '@intremap_table', 'devid',
                          index) == kernelstate.intremaps[index].devid)))

    conj.append(
        z3.ForAll([index],
                  z3.Implies(
                      is_intremap_valid(index),
                      util.global_field_element(
                          ctx, '@intremap_table', 'vector',
                          index) == kernelstate.intremaps[index].vector)))
def pages_equiv(conj, ctx, kernelstate):
    pn = util.FreshBitVec('pn', dt.pn_t)
    idx = util.FreshBitVec('page_index', 64)

    conj.append(
        z3.ForAll([pn, idx],
                  z3.Implies(
                      z3.And(is_pn_valid(pn), z3.ULT(idx, 512)),
                      util.global_to_uf_dict(ctx, '@pages')[()](
                          util.i64(0), pn,
                          idx) == kernelstate.pages[pn].data(idx))))

    conj.append(
        z3.ForAll(
            [pn],
            z3.Implies(
                is_pn_valid(pn),
                util.global_field_element(ctx, '@page_desc_table', 'pid',
                                          pn) == kernelstate.pages[pn].owner)))

    conj.append(
        z3.ForAll(
            [pn],
            z3.Implies(
                is_pn_valid(pn),
                util.global_field_element(ctx, '@page_desc_table', 'type',
                                          pn) == kernelstate.pages[pn].type)))
def pcipages_equiv(conj, ctx, kernelstate):
    pcipn = util.FreshBitVec('pcipn', dt.pn_t)

    conj.append(
        z3.ForAll([pcipn],
                  z3.Implies(
                      is_pcipn_valid(pcipn),
                      util.global_field_element(
                          ctx, '@pcipages', 'devid',
                          pcipn) == kernelstate.pcipages[pcipn].owner)))

    conj.append(
        z3.ForAll(
            [pcipn],
            z3.Implies(
                is_pcipn_valid(pcipn),
                (util.global_field_element(ctx, '@pcipages', 'valid', pcipn) !=
                 0) == kernelstate.pcipages[pcipn].valid)))
def dmapages_equiv(conj, ctx, kernelstate):
    dmapn = util.FreshBitVec('dmapn', dt.dmapn_t)

    conj.append(
        z3.ForAll([dmapn],
                  z3.Implies(
                      is_dmapn_valid(dmapn),
                      util.global_field_element(
                          ctx, '@dmapage_desc_table', 'type',
                          dmapn) == kernelstate.dmapages[dmapn].type)))

    conj.append(
        z3.ForAll([dmapn],
                  z3.Implies(
                      is_dmapn_valid(dmapn),
                      util.global_field_element(
                          ctx, '@dmapage_desc_table', 'pid',
                          dmapn) == kernelstate.dmapages[dmapn].owner)))
def proc_field_equiv(conj, pid, ctx, kernelstate, field_name):
    conj.append(
        z3.ForAll([pid],
                  z3.Implies(
                      is_pid_valid(pid),
                      util.global_field_element(ctx, '@proc_table', field_name,
                                                pid) == getattr(
                                                    kernelstate.procs[pid],
                                                    field_name))))
def files_equiv(conj, ctx, kernelstate):
    fn = util.FreshBitVec('fn', dt.fn_t)

    conj.append(
        z3.ForAll(
            [fn],
            z3.Implies(
                is_fn_valid(fn),
                util.global_field_element(ctx, '@file_table', 'type',
                                          fn) == kernelstate.files[fn].type)))

    conj.append(
        z3.ForAll([fn],
                  z3.Implies(
                      is_fn_valid(fn),
                      util.global_field_element(
                          ctx, '@file_table', 'refcnt',
                          fn) == kernelstate.files[fn].refcnt())))

    conj.append(
        z3.ForAll(
            [fn],
            z3.Implies(
                is_fn_valid(fn),
                util.global_field_element(ctx, '@file_table', 'value',
                                          fn) == kernelstate.files[fn].value)))

    conj.append(
        z3.ForAll(
            [fn],
            z3.Implies(
                is_fn_valid(fn),
                util.global_field_element(ctx, '@file_table', 'omode',
                                          fn) == kernelstate.files[fn].omode)))

    conj.append(
        z3.ForAll([fn],
                  z3.Implies(
                      is_fn_valid(fn),
                      util.global_field_element(
                          ctx, '@file_table', 'offset',
                          fn) == kernelstate.files[fn].offset)))
def procs_equiv(conj, ctx, kernelstate):
    pid = util.FreshBitVec('pid', dt.pid_t)
    fd = util.FreshBitVec('fd', dt.fd_t)
    idx = util.FreshBitVec('vector_index', 64)

    proc_field_equiv(conj, pid, ctx, kernelstate, 'state')
    proc_field_equiv(conj, pid, ctx, kernelstate, 'ppid')

    conj.append(
        z3.ForAll([pid],
                  z3.Implies(is_pid_valid(pid), (util.global_field_element(
                      ctx, '@proc_table', 'killed',
                      pid) == 0) == z3.Not(kernelstate.procs[pid].killed))))

    proc_field_equiv(conj, pid, ctx, kernelstate, 'ipc_from')
    proc_field_equiv(conj, pid, ctx, kernelstate, 'ipc_val')
    proc_field_equiv(conj, pid, ctx, kernelstate, 'ipc_page')
    proc_field_equiv(conj, pid, ctx, kernelstate, 'ipc_size')
    proc_field_equiv(conj, pid, ctx, kernelstate, 'ipc_fd')

    conj.append(
        z3.ForAll([pid, fd],
                  z3.Implies(
                      z3.And(is_pid_valid(pid), is_fd_valid(fd)),
                      util.global_field_element(
                          ctx, '@proc_table', 'ofile', pid, z3.ZeroExt(
                              32, fd)) == kernelstate.procs[pid].ofile(fd))))

    conj.append(
        z3.ForAll([pid],
                  z3.Implies(
                      is_pid_valid(pid),
                      util.global_field_element(
                          ctx, '@proc_table', 'nr_children',
                          pid) == kernelstate.procs[pid].nr_children())))

    conj.append(
        z3.ForAll([pid],
                  z3.Implies(
                      is_pid_valid(pid),
                      util.global_field_element(
                          ctx, '@proc_table', 'nr_fds',
                          pid) == kernelstate.procs[pid].nr_fds())))

    conj.append(
        z3.ForAll([pid],
                  z3.Implies(
                      is_pid_valid(pid),
                      util.global_field_element(
                          ctx, '@proc_table', 'nr_pages',
                          pid) == kernelstate.procs[pid].nr_pages())))

    conj.append(
        z3.ForAll([pid],
                  z3.Implies(
                      is_pid_valid(pid),
                      util.global_field_element(
                          ctx, '@proc_table', 'nr_dmapages',
                          pid) == kernelstate.procs[pid].nr_dmapages())))

    conj.append(
        z3.ForAll([pid],
                  z3.Implies(
                      is_pid_valid(pid),
                      util.global_field_element(
                          ctx, '@proc_table', 'nr_devs',
                          pid) == kernelstate.procs[pid].nr_devs())))

    conj.append(
        z3.ForAll([pid],
                  z3.Implies(
                      is_pid_valid(pid),
                      util.global_field_element(
                          ctx, '@proc_table', 'nr_ports',
                          pid) == kernelstate.procs[pid].nr_ports())))

    conj.append(
        z3.ForAll([pid],
                  z3.Implies(
                      is_pid_valid(pid),
                      util.global_field_element(
                          ctx, '@proc_table', 'nr_vectors',
                          pid) == kernelstate.procs[pid].nr_vectors())))

    conj.append(
        z3.ForAll([pid],
                  z3.Implies(
                      is_pid_valid(pid),
                      util.global_field_element(
                          ctx, '@proc_table', 'nr_intremaps',
                          pid) == kernelstate.procs[pid].nr_intremaps())))

    proc_field_equiv(conj, pid, ctx, kernelstate, 'stack')
    proc_field_equiv(conj, pid, ctx, kernelstate, 'hvm')
    proc_field_equiv(conj, pid, ctx, kernelstate, 'page_table_root')

    conj.append(
        z3.ForAll([pid],
                  z3.Implies(is_pid_valid(pid), (util.global_field_element(
                      ctx, '@proc_table', 'use_io_bitmap',
                      pid) == 0) == z3.Not(
                          kernelstate.procs[pid].use_io_bitmap))))

    proc_field_equiv(conj, pid, ctx, kernelstate, 'io_bitmap_a')
    proc_field_equiv(conj, pid, ctx, kernelstate, 'io_bitmap_b')

    conj.append(
        z3.ForAll([pid, idx],
                  z3.Implies(
                      z3.And(is_pid_valid(pid), z3.ULT(idx, 4)),
                      util.global_field_element(
                          ctx, '@proc_table', 'intr', pid,
                          idx) == kernelstate.procs[pid].intr(idx))))

    conj.append(
        z3.ForAll(
            [pid],
            ctx.globals['#tlbinv'](pid) == kernelstate.procs[pid].tlbinv))

    conj.append(ctx.globals['#iotlbinv'] == kernelstate.iotlbinv)
def pn_has_owner_and_type(ctx, pn, pid, type):
    return z3.And(
        is_pn_valid(pn),
        util.global_field_element(ctx, '@page_desc_table', 'pid', pn) == pid,
        util.global_field_element(ctx, '@page_desc_table', 'type', pn) == type)
def impl_invariants_py(ctx):
    conj = []

    pid = util.FreshBitVec('pid', dt.pid_t)
    pn = util.FreshBitVec('pn', dt.pn_t)
    fd = util.FreshBitVec('fd', dt.fd_t)
    # fn = util.FreshBitVec('fn', dt.fn_t)

    # embryos, runnable or running processes own the pages in their structs
    conj.append(z3.ForAll([pid], z3.Implies(
        z3.Or(util.global_field_element(ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_EMBRYO,
              util.global_field_element(
                  ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_RUNNING,
              util.global_field_element(
                  ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_RUNNABLE,
              util.global_field_element(ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_SLEEPING),
        z3.And(
            pn_has_owner_and_type(ctx, util.global_field_element(
                ctx, '@proc_table', 'page_table_root', pid), pid, dt.page_type.PAGE_TYPE_X86_PML4),
            pn_has_owner_and_type(ctx, util.global_field_element(
                ctx, '@proc_table', 'hvm', pid), pid, dt.page_type.PAGE_TYPE_PROC_DATA),
            pn_has_owner_and_type(ctx, util.global_field_element(
                ctx, '@proc_table', 'stack', pid), pid, dt.page_type.PAGE_TYPE_PROC_DATA),
        ))))

    # sleeping processes own their ipc_page
    conj.append(z3.ForAll([pid], z3.Implies(z3.And(is_pid_valid(pid),
        util.global_field_element(ctx, '@proc_table', 'state', pid) != dt.proc_state.PROC_ZOMBIE),
        z3.Implies(util.global_field_element(ctx, '@proc_table', 'use_io_bitmap', pid) != 0,
            z3.And(
                is_pn_valid(util.global_field_element(ctx, '@proc_table', 'io_bitmap_a', pid)),
                is_pn_valid(util.global_field_element(ctx, '@proc_table', 'io_bitmap_b', pid)),
                pn_has_owner_and_type(ctx, util.global_field_element(ctx, '@proc_table', 'io_bitmap_a', pid), pid, dt.page_type.PAGE_TYPE_PROC_DATA),
                pn_has_owner_and_type(ctx, util.global_field_element(ctx, '@proc_table', 'io_bitmap_b', pid), pid, dt.page_type.PAGE_TYPE_PROC_DATA))))))

    # sleeping processes own their ipc_page
    conj.append(z3.ForAll([pid], z3.Implies(is_pid_valid(pid),
        z3.Implies(util.global_field_element(ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_SLEEPING,
            pn_has_owner_and_type(ctx, util.global_field_element(ctx, '@proc_table', 'ipc_page', pid), pid, dt.page_type.PAGE_TYPE_FRAME)))))

    conj.append(z3.ForAll([pid],
                          z3.And(
        is_pn_valid(util.global_field_element(
                ctx, '@proc_table', 'page_table_root', pid)),
        is_pn_valid(util.global_field_element(
            ctx, '@proc_table', 'hvm', pid)),
        is_pn_valid(util.global_field_element(ctx, '@proc_table', 'stack', pid)))))

    # sleeping processes' ipc fd are empty if valid
    conj.append(z3.ForAll([pid], z3.Implies(is_pid_valid(pid),
        z3.Implies(util.global_field_element(ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_SLEEPING,
            z3.Implies(is_fd_valid(util.global_field_element(ctx, '@proc_table', 'ipc_fd', pid)),
                util.global_field_element(ctx, '@proc_table', 'ofile', pid,
                    z3.ZeroExt(32, util.global_field_element(ctx, '@proc_table', 'ipc_fd', pid))) == z3.BitVecVal(0, dt.fn_t))))))

    conj.append(z3.ForAll([pid],
                          z3.And(
        is_pn_valid(util.global_field_element(
                ctx, '@proc_table', 'page_table_root', pid)),
        is_pn_valid(util.global_field_element(
            ctx, '@proc_table', 'hvm', pid)),
        is_pn_valid(util.global_field_element(ctx, '@proc_table', 'stack', pid)))))

    # page has an owner <=> page is not free
    conj.append(z3.ForAll([pn], z3.Implies(is_pn_valid(pn),
        is_pid_valid(util.global_field_element(ctx, '@page_desc_table', 'pid', pn)) ==
        (util.global_field_element(ctx, '@page_desc_table', 'type', pn) != dt.page_type.PAGE_TYPE_FREE))))

    # unused procs have zero refcnt
    conj.append(z3.ForAll([pid], z3.Implies(
        z3.And(
            is_pid_valid(pid),
            util.global_field_element(ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_UNUSED),
        z3.And(
            util.global_field_element(ctx, '@proc_table', 'nr_children', pid) == z3.BitVecVal(0, dt.size_t),
            util.global_field_element(ctx, '@proc_table', 'nr_fds', pid) == z3.BitVecVal(0, dt.size_t),
            util.global_field_element(ctx, '@proc_table', 'nr_pages', pid) == z3.BitVecVal(0, dt.size_t),
            util.global_field_element(ctx, '@proc_table', 'nr_dmapages', pid) == z3.BitVecVal(0, dt.size_t),
            util.global_field_element(ctx, '@proc_table', 'nr_devs', pid) == z3.BitVecVal(0, dt.size_t),
            util.global_field_element(ctx, '@proc_table', 'nr_ports', pid) == z3.BitVecVal(0, dt.size_t),
            util.global_field_element(ctx, '@proc_table', 'nr_vectors', pid) == z3.BitVecVal(0, dt.size_t),
            util.global_field_element(ctx, '@proc_table', 'nr_intremaps', pid) == z3.BitVecVal(0, dt.size_t)))))

    # conj.append(z3.ForAll([pid, fd], z3.Implies(z3.And(is_pid_valid(pid), is_fd_valid(fd)),
    #     z3.Implies(
    #         util.global_field_element(ctx, '@proc_table', 'nr_fds', pid) == z3.BitVecVal(0, dt.size_t),
    #         z3.Not(is_fn_valid(util.global_field_element(ctx, '@proc_table', 'ofile', pid, z3.ZeroExt(32, fd))))))))

    # # unused procs have zero fds
    # conj.append(z3.ForAll([pid, fd], z3.Implies(z3.And(is_pid_valid(pid), is_fd_valid(fd)),
    #     z3.Implies(
    #         util.global_field_element(ctx, '@proc_table', 'state', pid) == dt.proc_state.PROC_UNUSED,
    #         util.global_field_element(ctx, '@proc_table', 'ofile', pid, z3.ZeroExt(32, fd)) == z3.BitVecVal(0, dt.fn_t)))))

    # fds valid
    conj.append(z3.ForAll([pid, fd], z3.Implies(z3.And(is_pid_valid(pid), is_fd_valid(fd)),
        z3.Or(
            util.global_field_element(ctx, '@proc_table', 'ofile', pid, z3.ZeroExt(32, fd)) == z3.BitVecVal(0, dt.fn_t),
            is_fn_valid(util.global_field_element(ctx, '@proc_table', 'ofile', pid, z3.ZeroExt(32, fd)))))))

    # # FD_NONE's refcount is 0
    # conj.append(z3.ForAll([fn], z3.Implies(
    #     z3.And(
    #         is_fn_valid(fn),
    #         util.global_field_element(ctx, '@file_table', 'type', fn) == dt.file_type.FD_NONE),
    #     util.global_field_element(ctx, '@file_table', 'refcnt', fn) == z3.BitVecVal(0, dt.size_t))))

    # # FD never points to FD_NONE
    # conj.append(z3.ForAll([pid, fd], z3.Implies(
    #     z3.And(
    #         is_pid_valid(pid),
    #         is_fd_valid(fd),
    #         is_fn_valid(util.global_field_element(ctx, '@proc_table', 'ofile', pid, z3.ZeroExt(32, fd)))),
    #     util.global_field_element(ctx, '@file_table', 'type',
    #         util.global_field_element(ctx, '@proc_table', 'ofile', pid, z3.ZeroExt(32, fd))) != dt.file_type.FD_NONE)))

    # page freelist is well formed
    conj.append(z3.ForAll([pn], z3.Implies(is_pn_valid(pn),
        z3.And(
            is_pn_valid(util.global_field_element(ctx, '@page_desc_table', ['link', 'prev'], pn)),
            is_pn_valid(util.global_field_element(ctx, '@page_desc_table', ['link', 'next'], pn))))))

    # ready queue is well formed
    # don't use is_pid_valid as some ->prev and ->next are zeros
    conj.append(z3.ForAll([pid], z3.Implies(is_pid_bounded(pid),
        z3.And(
            is_pid_bounded(util.global_field_element(ctx, '@proc_table', ['ready', 'prev'], pid)),
            is_pid_bounded(util.global_field_element(ctx, '@proc_table', ['ready', 'next'], pid))))))

    # Current is always a valid and running
    conj.append(is_pid_valid(util.global_value(ctx, '@current')))

    conj.append(util.global_field_element(ctx, '@proc_table', 'state', util.global_value(ctx, '@current')) == dt.proc_state.PROC_RUNNING)

    return z3.And(*conj)