Skip to main content

isla_axiomatic/
page_table.rs

1// BSD 2-Clause License
2//
3// Copyright (c) 2020 Alasdair Armstrong
4//
5// All rights reserved.
6//
7// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions are
9// met:
10//
11// 1. Redistributions of source code must retain the above copyright
12// notice, this list of conditions and the following disclaimer.
13//
14// 2. Redistributions in binary form must reproduce the above copyright
15// notice, this list of conditions and the following disclaimer in the
16// documentation and/or other materials provided with the distribution.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30use std::convert::{From, Into};
31use std::ops::Range;
32use std::sync::Arc;
33
34use isla_lib::bitvector::{bzhi_u64, BV, b64::B64};
35use isla_lib::error::ExecError;
36use isla_lib::executor::LocalFrame;
37use isla_lib::ir::Val;
38use isla_lib::log;
39use isla_lib::memory::CustomRegion;
40use isla_lib::primop::{length_bits, smt_sbits};
41use isla_lib::smt::{
42    smtlib::{Def, Exp, Ty, bits64},
43    Event, SmtResult, Solver, Sym,
44};
45
46pub struct S1PageAttrs {
47    uxn: Option<bool>, // UXN in EL1&0 translation regime, XN in others
48    pxn: Option<bool>,
49    contiguous: Option<bool>,
50    n_g: Option<bool>,
51    af: Option<bool>,
52    sh: Option<u8>,
53    ap: Option<u8>,
54    ns: Option<bool>,
55    attr_indx: Option<u8>,
56}
57
58impl Default for S1PageAttrs {
59    fn default() -> Self {
60        S1PageAttrs {
61            uxn: Some(false),
62            pxn: Some(false),
63            contiguous: Some(false),
64            n_g: Some(false),
65            af: Some(true),
66            sh: Some(0b00),
67            ap: Some(0b01),
68            ns: Some(false),
69            attr_indx: Some(0b000),
70        }
71    }
72}
73
74impl S1PageAttrs {
75    pub fn code() -> Self {
76        S1PageAttrs {
77            uxn: Some(false),
78            pxn: Some(false),
79            contiguous: Some(false),
80            n_g: Some(false),
81            af: Some(true),
82            sh: Some(0b00),
83            ap: Some(0b11),
84            ns: Some(false),
85            attr_indx: Some(0b000),
86        }
87    }
88}
89
90pub struct S2PageAttrs {
91    xn: Option<bool>,
92    contiguous: Option<bool>,
93    af: Option<bool>,
94    sh: Option<u8>,
95    s2ap: Option<u8>,
96    mem_attr: Option<u8>,
97}
98
99impl Default for S2PageAttrs {
100    fn default() -> Self {
101        S2PageAttrs {
102            xn: Some(false),
103            contiguous: Some(false),
104            af: Some(true),
105            sh: Some(0b00),
106            s2ap: Some(0b01),
107            mem_attr: Some(0b0000),
108        }
109    }
110}
111
112impl S2PageAttrs {
113    pub fn code() -> Self {
114        S2PageAttrs {
115            xn: Some(false),
116            contiguous: Some(false),
117            af: Some(true),
118            sh: Some(0b00),
119            s2ap: Some(0b00),
120            mem_attr: Some(0b0000),
121        }
122    }
123}
124
125fn bool_to_bit(b: bool) -> Exp {
126    bits64(if b { 1 } else { 0 }, 1)
127}
128
129pub trait PageAttrs {
130    fn unknown() -> Self;
131
132    fn bits(&self) -> (u64, u64);
133
134    fn set<B: BV>(&self, desc: Sym, solver: &mut Solver<B>);
135}
136
137macro_rules! attr_bool {
138    ($field: expr, $n: expr, $set: ident, $unknown: ident) => {
139        if let Some(bit) = $field {
140            $set |= u64::from(bit) << $n
141        } else {
142            $unknown |= 1 << $n
143        }
144    }
145}
146
147macro_rules! attr_u8 {
148    ($field: expr, $hi: expr, $lo: expr, $set: ident, $unknown: ident) => {
149        if let Some(bits) = $field {
150            $set |= bzhi_u64(bits as u64, ($hi - $lo) + 1) << $lo
151        } else {
152            $unknown |= bzhi_u64(u64::MAX, ($hi - $lo) + 1) << $lo
153        }
154    }
155}
156
157impl PageAttrs for S1PageAttrs {
158    fn unknown() -> Self {
159        S1PageAttrs {
160            uxn: None,
161            pxn: None,
162            contiguous: None,
163            n_g: None,
164            af: None,
165            sh: None,
166            ap: None,
167            ns: None,
168            attr_indx: None,
169        }
170    }
171
172    fn bits(&self) -> (u64, u64) {
173        let mut set = 0;
174        let mut unknown = 0;
175
176        attr_bool!(self.uxn, 53, set, unknown);
177        attr_bool!(self.pxn, 54, set, unknown);
178        attr_bool!(self.contiguous, 52, set, unknown);
179        attr_bool!(self.n_g, 11, set, unknown);
180        attr_bool!(self.af, 10, set, unknown);
181        attr_u8!(self.sh, 9, 8, set, unknown);
182        attr_u8!(self.ap, 7, 6, set, unknown);
183        attr_bool!(self.ns, 5, set, unknown);
184        attr_u8!(self.attr_indx, 4, 2, set, unknown);
185
186        (set, unknown)
187    }
188
189    fn set<B: BV>(&self, desc: Sym, solver: &mut Solver<B>) {
190        use Exp::*;
191
192        // Bit 54 is UXN (Unprivileged execute-never)
193        if let Some(uxn) = self.uxn {
194            solver.assert_eq(Extract(54, 54, Box::new(Var(desc))), bool_to_bit(uxn))
195        }
196
197        // Bit 53 is PXN (Privileged execute-never)
198        if let Some(pxn) = self.pxn {
199            solver.assert_eq(Extract(54, 54, Box::new(Var(desc))), bool_to_bit(pxn))
200        }
201
202        // Bit 52 is the contiguous bit
203        if let Some(contiguous) = self.contiguous {
204            solver.assert_eq(Extract(52, 52, Box::new(Var(desc))), bool_to_bit(contiguous))
205        }
206
207        // Bit 11 is nG (not global bit)
208        if let Some(n_g) = self.n_g {
209            solver.assert_eq(Extract(11, 11, Box::new(Var(desc))), bool_to_bit(n_g))
210        }
211
212        // Bit 10 is AF (access flag)
213        if let Some(af) = self.af {
214            solver.assert_eq(Extract(10, 10, Box::new(Var(desc))), bool_to_bit(af))
215        }
216
217        // Bits 9-8 is SH (shareability field)
218        if let Some(sh) = self.sh {
219            solver.assert_eq(Extract(9, 8, Box::new(Var(desc))), bits64(sh as u64 & 0b11, 2))
220        }
221
222        // Bits 7-6 is AP (access permissions)
223        if let Some(ap) = self.ap {
224            solver.assert_eq(Extract(7, 6, Box::new(Var(desc))), bits64(ap as u64 & 0b11, 2))
225        }
226
227        // Bit 5 is NS (non-secure bit)
228        if let Some(ns) = self.ns {
229            solver.assert_eq(Extract(5, 5, Box::new(Var(desc))), bool_to_bit(ns))
230        }
231
232        // Bits 4-2 AttrIndx
233        if let Some(attr_indx) = self.attr_indx {
234            solver.assert_eq(Extract(4, 2, Box::new(Var(desc))), bits64(attr_indx as u64 & 0b111, 3))
235        }
236    }
237}
238
239impl PageAttrs for S2PageAttrs {
240    fn unknown() -> Self {
241        S2PageAttrs { xn: None, contiguous: None, af: None, sh: None, s2ap: None, mem_attr: None }
242    }
243
244    fn bits(&self) -> (u64, u64) {
245        let mut set = 0;
246        let mut unknown = 0;
247
248        attr_bool!(self.xn, 54, set, unknown);
249        attr_bool!(self.contiguous, 52, set, unknown);
250        attr_bool!(self.af, 10, set, unknown);
251        attr_u8!(self.sh, 9, 8, set, unknown);
252        attr_u8!(self.s2ap, 7, 6, set, unknown);
253        attr_u8!(self.mem_attr, 5, 2, set, unknown);
254        
255        (set, unknown)
256    }
257
258    fn set<B: BV>(&self, desc: Sym, solver: &mut Solver<B>) {
259        use Exp::*;
260
261        // Bit 54 is XN (Execute-never)
262        if let Some(xn) = self.xn {
263            solver.assert_eq(Extract(54, 54, Box::new(Var(desc))), bool_to_bit(xn))
264        }
265
266        // Bit 53 is always 0
267        solver.assert_eq(Extract(53, 53, Box::new(Var(desc))), bits64(0, 1));
268
269        // Bit 52 is the contiguous bit
270        if let Some(contiguous) = self.contiguous {
271            solver.assert_eq(Extract(52, 52, Box::new(Var(desc))), bool_to_bit(contiguous))
272        }
273
274        // Bit 11 is always 0
275        solver.assert_eq(Extract(53, 53, Box::new(Var(desc))), bits64(0, 1));
276
277        // Bit 10 is AF (access flag)
278        if let Some(af) = self.af {
279            solver.assert_eq(Extract(10, 10, Box::new(Var(desc))), bool_to_bit(af))
280        }
281
282        // Bits 9-8 is SH (shareability field)
283        if let Some(sh) = self.sh {
284            solver.assert_eq(Extract(9, 8, Box::new(Var(desc))), bits64(sh as u64 & 0b11, 2))
285        }
286
287        // Bits 7-6 is S2AP (stage 2 access permissions)
288        if let Some(s2ap) = self.s2ap {
289            solver.assert_eq(Extract(7, 6, Box::new(Var(desc))), bits64(s2ap as u64 & 0b11, 2))
290        }
291
292        // Bits 5-2 MemAttr (memory regions attributes for stage 2 translations)
293        if let Some(mem_attr) = self.mem_attr {
294            solver.assert_eq(Extract(5, 2, Box::new(Var(desc))), bits64(mem_attr as u64 & 0b1111, 4))
295        }
296    }
297}
298
299/// An index for a level 3 page table. For type-safety and to aid in
300/// constructing valid page tables, we wrap page table addresses into
301/// a set of indexing types for level 3 and level 0, 1, and 2 page
302/// tables, as well as generic indices which can be used for either.
303#[derive(Copy, Clone)]
304pub struct L3Index {
305    base_addr: u64,
306    ix: usize,
307}
308
309/// An index for a level 0, 1, or 2 page table.
310#[derive(Copy, Clone)]
311pub struct L012Index {
312    base_addr: u64,
313    ix: usize,
314}
315
316/// An index for a level 0, 1, 2, or 3 page table.
317#[derive(Copy, Clone)]
318pub struct GenericIndex {
319    base_addr: u64,
320    ix: usize,
321}
322
323impl From<L3Index> for GenericIndex {
324    fn from(i: L3Index) -> Self {
325        GenericIndex { base_addr: i.base_addr, ix: i.ix }
326    }
327}
328
329impl From<L012Index> for GenericIndex {
330    fn from(i: L012Index) -> Self {
331        GenericIndex { base_addr: i.base_addr, ix: i.ix }
332    }
333}
334
335/// Get the physical address of a page table from it's index.
336pub fn table_address<I: Into<GenericIndex>>(i: I) -> u64 {
337    let i = i.into();
338    i.base_addr + ((i.ix as u64) << 12)
339}
340
341/// A level 3 page table descriptor.
342#[derive(Copy, Clone, Debug)]
343pub enum L3Desc {
344    Concrete(u64),
345    Symbolic(u64, Sym),
346}
347
348impl<B: BV> Into<Val<B>> for L3Desc {
349    fn into(self) -> Val<B> {
350        match self {
351            L3Desc::Concrete(bits) => Val::Bits(B::new(bits, 64)),
352            L3Desc::Symbolic(_, v) => Val::Symbolic(v),
353        }
354    }
355}
356
357impl L3Desc {
358    // An invalid level 3 descriptor is any where bit 0 is 0
359    pub fn new_invalid() -> Self {
360        L3Desc::Concrete(0)
361    }
362
363    pub fn initial_value(self) -> u64 {
364        match self {
365            L3Desc::Concrete(bits) => bits,
366            L3Desc::Symbolic(init, _) => init,
367        }
368    }
369
370    // A reserved level 3 descriptor is any where bits 1-0 are 0b01. The other bits are RES0
371    pub fn new_reserved() -> Self {
372        L3Desc::Concrete(1)
373    }
374
375    pub fn page<P: PageAttrs>(page: u64, attrs: P) -> Self {
376        let mask: u64 = ((1 << 36) - 1) << 12;
377        let (attrs, unknowns) = attrs.bits();
378
379        assert!(page & !mask == 0);
380        assert!(unknowns == 0);
381        
382        let desc = (page & mask) | 0b11 | attrs;
383
384        L3Desc::Concrete(desc)
385    }
386
387    pub fn symbolic_address<B: BV>(self, solver: &mut Solver<B>) -> Sym {
388        use Exp::*;
389        match self {
390            L3Desc::Concrete(addr) => {
391                let mask = bzhi_u64(u64::MAX ^ 0xFFF, 48);
392                solver.define_const(bits64(addr & mask, 64))
393            }
394            L3Desc::Symbolic(_, v) => solver.define_const(ZeroExtend(
395                16,
396                Box::new(Concat(Box::new(Extract(47, 12, Box::new(Var(v)))), Box::new(bits64(0, 12)))),
397            )),
398        }
399    }
400
401    /// Make a level 3 descriptor potentially be invalid
402    pub fn or_invalid<B: BV>(self, solver: &mut Solver<B>) -> Self {
403        use Exp::*;
404        let (init, old_desc) = match self {
405            L3Desc::Concrete(bits) => (bits, bits64(bits, 64)),
406            L3Desc::Symbolic(init, v) => (init, Var(v)),
407        };
408        let is_invalid = solver.declare_const(Ty::Bool);
409        let new_desc = solver.define_const(Ite(Box::new(Var(is_invalid)), Box::new(bits64(0, 64)), Box::new(old_desc)));
410        L3Desc::Symbolic(init, new_desc)
411    }
412
413    // A symbolic level 3 descriptor pointing to a set of possible
414    // pages. If pages is empty return an invalid descriptor
415    pub fn new_symbolic<B: BV, P: PageAttrs>(pages: &[u64], attrs: P, solver: &mut Solver<B>) -> Self {
416        use Exp::*;
417
418        let desc = solver.declare_const(Ty::BitVec(64));
419
420        // bits 51 to 48 are reserved and always zero (RES0)
421        solver.assert_eq(Extract(51, 48, Box::new(Var(desc))), bits64(0b0000, 4));
422
423        // buts 1 to 0 are always 0b11 for a valid address descriptor
424        solver.assert_eq(Extract(1, 0, Box::new(Var(desc))), bits64(0b11, 2));
425
426        // Attributes are in bits 63-52 and 11-2
427        attrs.set(desc, solver);
428
429        // For a 4K page size bits 47-12 contain the output address
430        let mut page_constraints = Vec::new();
431        for page in pages {
432            page_constraints.push(Eq(Box::new(Extract(47, 12, Box::new(Var(desc)))), Box::new(bits64(page >> 12, 36))))
433        }
434
435        if let Some(p) = page_constraints.pop() {
436            let constraint = page_constraints.drain(..).fold(p, |p1, p2| Or(Box::new(p1), Box::new(p2)));
437            solver.add(Def::Assert(constraint))
438        } else {
439            return L3Desc::new_invalid();
440        }
441
442        L3Desc::Symbolic(pages[0], desc)
443    }
444}
445
446/// A level 0, 1, or 2 page table descriptor.
447#[derive(Copy, Clone, Debug)]
448pub enum L012Desc {
449    Concrete(u64),
450    Symbolic(Sym),
451}
452
453impl<B: BV> Into<Val<B>> for L012Desc {
454    fn into(self) -> Val<B> {
455        match self {
456            L012Desc::Concrete(bits) => Val::Bits(B::new(bits, 64)),
457            L012Desc::Symbolic(v) => Val::Symbolic(v),
458        }
459    }
460}
461
462impl L012Desc {
463    // An invalid level 0, 1 or 2 descriptor is any where bit 0 is 0
464    fn new_invalid() -> Self {
465        L012Desc::Concrete(0)
466    }
467
468    fn is_concrete_invalid(self) -> bool {
469        match self {
470            L012Desc::Concrete(desc) => desc == 0,
471            _ => false,
472        }
473    }
474
475    pub fn concrete_address(self) -> Option<u64> {
476        match self {
477            L012Desc::Concrete(desc) => Some(desc & !0b11),
478            _ => None,
479        }
480    }
481
482    pub fn new_table<T: Into<GenericIndex>>(table: T) -> Self {
483        L012Desc::Concrete(table_address(table) | 0b11)
484    }
485}
486
487/// A concrete ARMv8 virtual address
488#[derive(Copy, Clone, Debug, PartialEq, Eq)]
489pub struct VirtualAddress {
490    bits: u64,
491}
492
493impl VirtualAddress {
494    /// Create a virtual address from a 64-bit unsigned integer. This
495    /// function will clear all bits from 48 and above to guarantee it
496    /// is a valid ARMv8 virtual address.
497    pub fn from_u64(bits: u64) -> Self {
498        VirtualAddress { bits: bzhi_u64(bits, 48) }
499    }
500
501    /// `va.level_index(n)` will return the index used for the
502    /// translation table at level `n` when translating `va`. Panics
503    /// if `n > 3`.
504    pub fn level_index(self, level: u64) -> usize {
505        assert!(level <= 3);
506        ((self.bits >> ((3 - level) * 9 + 12)) & ((1 << 9) - 1)) as usize
507    }
508
509    /// Return the offset of a virtual address within a 4K page.
510    pub fn page_offset(self) -> u64 {
511        self.bits & 0xFFF
512    }
513
514    /// Create a virtual address that will be translated by the
515    /// translation table indices in order from level 0 to 3 plus a
516    /// page offset. Panics if any level argument is not less than
517    /// 512, and the page offset is not less than 4096.
518    pub fn from_indices(level0: usize, level1: usize, level2: usize, level3: usize, page_offset: usize) -> Self {
519        let mut bits = 0;
520
521        assert!(level0 < 512);
522        bits |= (level0 as u64) << (12 + (9 * 3));
523
524        assert!(level1 < 512);
525        bits |= (level1 as u64) << (12 + (9 * 2));
526
527        assert!(level2 < 512);
528        bits |= (level2 as u64) << (12 + 9);
529
530        assert!(level3 < 512);
531        bits |= (level3 as u64) << 12;
532
533        assert!(page_offset < 4096);
534        bits |= page_offset as u64;
535
536        VirtualAddress { bits }
537    }
538}
539
540#[derive(Clone)]
541enum PageTable {
542    L3([L3Desc; 512]),
543    L012([L012Desc; 512]),
544}
545
546#[derive(Clone)]
547pub struct PageTables {
548    base_addr: u64,
549    tables: Vec<PageTable>,
550    kind: &'static str,
551}
552
553#[derive(Clone)]
554pub struct ImmutablePageTables {
555    base_addr: u64,
556    tables: Arc<[PageTable]>,
557    kind: &'static str,
558}
559
560impl PageTables {
561    /// Create a new set of ARMv8 page tables, which is initially
562    /// empty. The base address will be the address used to allocate
563    /// the first table, which are then allocated contiguously in 4K
564    /// chunks. A translation table base register (e.g. TTBR0_EL1) can
565    /// point to any valid translation table, so does not have to
566    /// match this value.
567    pub fn new(kind: &'static str, base_addr: u64) -> Self {
568        PageTables { base_addr, tables: Vec::new(), kind }
569    }
570
571    pub fn range(&self) -> Range<u64> {
572        self.base_addr..(self.base_addr + 4096 * self.tables.len() as u64)
573    }
574
575    /// Allocate a new level 3 translation table.
576    pub fn alloc_l3(&mut self) -> L3Index {
577        log!(log::MEMORY, "Allocating new level 3 table");
578        self.tables.push(PageTable::L3([L3Desc::new_invalid(); 512]));
579        L3Index { base_addr: self.base_addr, ix: self.tables.len() - 1 }
580    }
581
582    /// Allocate a new level 0, 1, or 2 translation table
583    pub fn alloc(&mut self) -> L012Index {
584        log!(log::MEMORY, "Allocating new level 0, 1, or 2 table");
585        self.tables.push(PageTable::L012([L012Desc::new_invalid(); 512]));
586        L012Index { base_addr: self.base_addr, ix: self.tables.len() - 1 }
587    }
588
589    pub fn get_l3(&self, i: L3Index) -> &[L3Desc; 512] {
590        match &self.tables[i.ix] {
591            PageTable::L3(table) => table,
592            _ => panic!("invalid page table index"),
593        }
594    }
595
596    pub fn get_l3_mut(&mut self, i: L3Index) -> &mut [L3Desc; 512] {
597        match &mut self.tables[i.ix] {
598            PageTable::L3(table) => table,
599            _ => panic!("invalid page table index"),
600        }
601    }
602
603    pub fn get(&self, i: L012Index) -> &[L012Desc; 512] {
604        match &self.tables[i.ix] {
605            PageTable::L012(table) => table,
606            _ => panic!("invalid page table index"),
607        }
608    }
609
610    pub fn get_mut(&mut self, i: L012Index) -> &mut [L012Desc; 512] {
611        match &mut self.tables[i.ix] {
612            PageTable::L012(table) => table,
613            _ => panic!("invalid page table index"),
614        }
615    }
616
617    /// Lookup a level 3 translation table at a specific physical
618    /// address. Returns None if there is no level 3 translation table
619    /// at that address.
620    pub fn lookup_l3(&self, addr: u64) -> Option<L3Index> {
621        if addr < self.base_addr {
622            return None;
623        };
624
625        let i = ((addr - self.base_addr) >> 12) as usize;
626        if let Some(PageTable::L3(_)) = self.tables.get(i) {
627            Some(L3Index { base_addr: self.base_addr, ix: i })
628        } else {
629            None
630        }
631    }
632
633    /// The same as `lookup_l3` but for level 0, 1 and 2 tables.
634    pub fn lookup(&self, addr: u64) -> Option<L012Index> {
635        if addr < self.base_addr {
636            return None;
637        };
638
639        let i = ((addr - self.base_addr) >> 12) as usize;
640        if let Some(PageTable::L012(_)) = self.tables.get(i) {
641            Some(L012Index { base_addr: self.base_addr, ix: i })
642        } else {
643            None
644        }
645    }
646
647    pub fn map<B: BV, P: PageAttrs>(&mut self, level0: L012Index, va: VirtualAddress, page: u64, attrs: P, maybe_invalid: Option<&mut Solver<B>>) -> Option<()> {
648        log!(log::MEMORY, &format!("Creating page table mapping: 0x{:x} -> 0x{:x}", va.bits, page));
649
650        let mut desc: L012Desc = self.get(level0)[va.level_index(0)];
651        let mut table = level0;
652
653        for i in 1..=2 {
654            if desc.is_concrete_invalid() {
655                log!(log::MEMORY, &format!("Creating new level {} descriptor", i - 1));
656                desc = L012Desc::new_table(self.alloc());
657                self.get_mut(table)[va.level_index(i - 1)] = desc;
658            }
659
660            table = self.lookup(desc.concrete_address().unwrap())?;
661            desc = self.get(table)[va.level_index(i)]
662        }
663
664        let table = self.lookup_l3(desc.concrete_address()?).unwrap_or_else(|| {
665            log!(log::MEMORY, "Creating new level 3 descriptor");
666            let l3_table = self.alloc_l3();
667            self.get_mut(table)[va.level_index(2)] = L012Desc::new_table(l3_table);
668            l3_table
669        });
670        self.get_l3_mut(table)[va.level_index(3)] = if let Some(solver) = maybe_invalid {
671            L3Desc::page(page, attrs).or_invalid(solver)
672        } else {
673            L3Desc::page(page, attrs)
674        };
675
676        Some(())
677    }
678
679    pub fn identity_map<P: PageAttrs>(&mut self, level0: L012Index, page: u64, attrs: P) -> Option<()> {
680        self.map::<B64, P>(level0, VirtualAddress::from_u64(page), page, attrs, None)
681    }
682
683    pub fn identity_or_invalid_map<B: BV, P: PageAttrs>(&mut self, level0: L012Index, page: u64, attrs: P, solver: &mut Solver<B>) -> Option<()> {
684        self.map(level0, VirtualAddress::from_u64(page), page, attrs, Some(solver))
685    }
686    
687    pub fn alias<B: BV>(&mut self, addr: u64, i: usize, pages: &[u64], solver: &mut Solver<B>) -> Option<()> {
688        let table = self.lookup_l3(addr)?;
689        self.get_l3_mut(table)[i] = L3Desc::new_symbolic(pages, S1PageAttrs::default(), solver);
690        Some(())
691    }
692
693    pub fn freeze(&self) -> ImmutablePageTables {
694        ImmutablePageTables { base_addr: self.base_addr, tables: self.tables.clone().into(), kind: self.kind }
695    }
696}
697
698impl ImmutablePageTables {
699    fn initial_descriptor<B: BV>(&self, addr: u64) -> Option<u64> {
700        let table_addr = addr & !0xFFF;
701
702        // Ensure page table reads are 8 bytes and aligned
703        if (addr & 0b111) != 0 || table_addr < self.base_addr {
704            return None;
705        }
706
707        let offset = ((addr & 0xFFF) >> 3) as usize;
708        let i = ((table_addr - self.base_addr) >> 12) as usize;
709
710        let desc: Val<B> = match self.tables.get(i) {
711            Some(PageTable::L012(table)) => table[offset].into(),
712            Some(PageTable::L3(table)) => Val::Bits(B::new(table[offset].initial_value(), 64)),
713            None => return None,
714        };
715
716        match desc {
717            Val::Bits(bv) => Some(bv.lower_u64()),
718            _ => None,
719        }
720    }
721}
722
723impl<B: BV> CustomRegion<B> for ImmutablePageTables {
724    fn read(
725        &self,
726        read_kind: Val<B>,
727        addr: u64,
728        bytes: u32,
729        solver: &mut Solver<B>,
730        _tag: bool,
731    ) -> Result<Val<B>, ExecError> {
732        log!(log::MEMORY, &format!("Page table read: 0x{:x}", addr));
733
734        let table_addr = addr & !0xFFF;
735
736        // Ensure page table reads are 8 bytes and aligned
737        if (addr & 0b111) != 0 || bytes != 8 || table_addr < self.base_addr {
738            return Err(ExecError::BadRead("unaligned page table read"));
739        }
740
741        let offset = ((addr & 0xFFF) >> 3) as usize;
742        let i = ((table_addr - self.base_addr) >> 12) as usize;
743
744        let desc: Val<B> = match self.tables.get(i) {
745            Some(PageTable::L012(table)) => table[offset].into(),
746            Some(PageTable::L3(table)) => table[offset].into(),
747            None => return Err(ExecError::BadRead("page table index out of bounds")),
748        };
749
750        solver.add_event(Event::ReadMem {
751            value: desc.clone(),
752            read_kind,
753            address: Val::Bits(B::from_u64(addr)),
754            bytes,
755            tag_value: None,
756            kind: self.kind,
757        });
758 
759        log!(log::MEMORY, &format!("Page table descriptor: 0x{:x} -> {:?}", addr, desc));
760
761        Ok(desc)
762    }
763
764    fn write(
765        &mut self,
766        write_kind: Val<B>,
767        addr: u64,
768        write_desc: Val<B>,
769        solver: &mut Solver<B>,
770        tag: Option<Val<B>>,
771    ) -> Result<Val<B>, ExecError> {
772        log!(log::MEMORY, &format!("Page table write: 0x{:x} <- {:?}", addr, write_desc));
773
774        let table_addr = addr & !0xFFF;
775        let write_len_bits = length_bits(&write_desc, solver)?;
776
777        // Ensure page table writes are also 8 bytes and aligned
778        if (addr & 0b111) != 0 || write_len_bits != 64 || table_addr < self.base_addr {
779            return Err(ExecError::BadWrite("unaligned page table write"));
780        }
781
782        let offset = ((addr & 0xFFF) >> 3) as usize;
783        let i = ((table_addr - self.base_addr) >> 12) as usize;
784
785        let current_desc: Val<B> = match self.tables.get(i) {
786            Some(PageTable::L012(table)) => table[offset].into(),
787            Some(PageTable::L3(table)) => table[offset].into(),
788            None => return Err(ExecError::BadWrite("page table index out of bounds")),
789        };
790
791        let (skip_sat_check, query) = match (current_desc, &write_desc) {
792            (Val::Bits(d1), Val::Bits(d2)) if d1 == *d2 => (true, Exp::Bool(true)),
793            (Val::Bits(d1), Val::Symbolic(d2)) => (false, Exp::Eq(Box::new(smt_sbits(d1)), Box::new(Exp::Var(*d2)))),
794            (Val::Symbolic(d1), Val::Bits(d2)) => (false, Exp::Eq(Box::new(Exp::Var(d1)), Box::new(smt_sbits(*d2)))),
795            (Val::Symbolic(d1), Val::Symbolic(d2)) => (false, Exp::Eq(Box::new(Exp::Var(d1)), Box::new(Exp::Var(*d2)))),
796            (Val::Bits(_), Val::Bits(_))=> return Err(ExecError::BadWrite("page table write trivially unsatisfiable")),
797            (_, _) => return Err(ExecError::BadWrite("ill-typed descriptor")),
798        };
799
800        if skip_sat_check || solver.check_sat_with(&query) == SmtResult::Sat {
801            let value = solver.declare_const(Ty::Bool);
802            solver.add_event(Event::WriteMem {
803                value,
804                write_kind,
805                address: Val::Bits(B::from_u64(addr)),
806                data: write_desc,
807                bytes: 8,
808                tag_value: tag,
809                kind: self.kind,
810            });
811            Ok(Val::Symbolic(value))
812        } else {
813            Err(ExecError::BadWrite("page table write unsatisfiable"))
814        }
815    }
816
817    fn initial_value(&self, addr: u64, bytes: u32) -> Option<B> {
818        let desc_addr = addr & !0b111;
819        let desc_offset = addr & 0b111;
820
821        if (bytes as u64 + desc_offset) > 8 {
822            return None;
823        };
824
825        let desc = self.initial_descriptor::<B>(desc_addr)?;
826
827        Some(B::new(bzhi_u64(desc >> (desc_offset * 8), bytes * 8), bytes * 8))
828    }
829
830    fn memory_kind(&self) -> &'static str {
831        self.kind
832    }
833
834    fn clone_dyn(&self) -> Box<dyn Send + Sync + CustomRegion<B>> {
835        Box::new(self.clone())
836    }
837}
838
839pub fn primop_setup_page_tables<B: BV>(
840    _args: Vec<Val<B>>,
841    _solver: &mut Solver<B>,
842    _frame: &mut LocalFrame<B>,
843) -> Result<Val<B>, ExecError> {
844    Ok(Val::Unit)
845}
846
847#[cfg(test)]
848mod tests {
849    use isla_lib::bitvector::b64::B64;
850    use isla_lib::smt::{Config, Context};
851
852    use super::*;
853
854    #[test]
855    fn test_va_index() {
856        let va = VirtualAddress::from_u64(0x8000_1000);
857        assert_eq!(va.level_index(3), 1);
858        assert_eq!(va.level_index(2), 0);
859        assert_eq!(va.level_index(1), 2);
860        assert_eq!(va.level_index(0), 0);
861
862        assert_eq!(va, VirtualAddress::from_indices(0, 2, 0, 1, 0));
863
864        let va = VirtualAddress::from_u64(0x8000_0004);
865        assert_eq!(va.level_index(3), 0);
866        assert_eq!(va.level_index(2), 0);
867        assert_eq!(va.level_index(1), 2);
868        assert_eq!(va.level_index(0), 0);
869
870        assert_eq!(va, VirtualAddress::from_indices(0, 2, 0, 0, 4));
871    }
872
873    #[test]
874    fn test_table_address() {
875        let mut tbls = PageTables::new("test", 0x5000_0000);
876        let tbl1 = tbls.alloc_l3();
877        let tbl2 = tbls.alloc_l3();
878        let tbl3 = tbls.alloc();
879        assert_eq!(table_address(tbl1), 0x5000_0000);
880        assert_eq!(table_address(tbl2), 0x5000_0000 + 4096);
881        assert_eq!(table_address(tbl3), 0x5000_0000 + 4096 * 2);
882    }
883
884    /// A simple translation table walk for testing purposes. We
885    /// assume 4k pages, ignore attributes, and assume that level 0-2
886    /// tables always concretely point to lower level tables and not
887    /// pages
888    fn simple_translation_table_walk<B: BV>(
889        tables: &PageTables,
890        level0: L012Index,
891        va: VirtualAddress,
892        solver: &mut Solver<B>,
893    ) -> Option<Sym> {
894        use Exp::*;
895
896        let l0desc = tables.get(level0)[va.level_index(0)];
897
898        let level1 = tables.lookup(l0desc.concrete_address()?)?;
899        let l1desc = tables.get(level1)[va.level_index(1)];
900
901        let level2 = tables.lookup(l1desc.concrete_address()?)?;
902        let l2desc = tables.get(level2)[va.level_index(2)];
903
904        let level3 = tables.lookup_l3(l2desc.concrete_address()?)?;
905        let l3desc = tables.get_l3(level3)[va.level_index(3)];
906
907        let page_addr = l3desc.symbolic_address(solver);
908        let addr = solver.define_const(Bvadd(Box::new(Var(page_addr)), Box::new(bits64(va.page_offset(), 64))));
909
910        Some(addr)
911    }
912
913    #[test]
914    fn test_translate() {
915        use Def::*;
916        use Exp::*;
917        use SmtResult::*;
918
919        let mut cfg = Config::new();
920        cfg.set_param_value("model", "true");
921        let ctx = Context::new(cfg);
922        let mut solver = Solver::<B64>::new(&ctx);
923
924        let mut tables = PageTables::new("test", 0x5000_0000);
925        let l3 = tables.alloc_l3();
926        let l2 = tables.alloc();
927        let l1 = tables.alloc();
928        let l0 = tables.alloc();
929
930        let va = VirtualAddress::from_u64(0xDEAD_BEEF);
931
932        // Create a level 3 descriptor that can point at one of either two pages
933        tables.get_l3_mut(l3)[va.level_index(3)] =
934            L3Desc::new_symbolic(&[0x8000_0000, 0x8000_1000], S1PageAttrs::unknown(), &mut solver);
935        tables.get_mut(l2)[va.level_index(2)] = L012Desc::new_table(l3);
936        tables.get_mut(l1)[va.level_index(1)] = L012Desc::new_table(l2);
937        tables.get_mut(l0)[va.level_index(0)] = L012Desc::new_table(l1);
938
939        assert_eq!(Sat, solver.check_sat());
940
941        // Translate our concrete virtual address to a symbolic
942        // physical address, and check it could be in either page
943        if let Some(pa) = simple_translation_table_walk(&tables, l0, va, &mut solver) {
944            assert_eq!(Sat, solver.check_sat_with(&Eq(Box::new(Var(pa)), Box::new(bits64(0x8000_0EEF, 64)))));
945            assert_eq!(Sat, solver.check_sat_with(&Eq(Box::new(Var(pa)), Box::new(bits64(0x8000_1EEF, 64)))));
946
947            // Additionally, it  can't be anything other than those two addresses
948            solver.add(Assert(Neq(Box::new(Var(pa)), Box::new(bits64(0x8000_0EEF, 64)))));
949            solver.add(Assert(Neq(Box::new(Var(pa)), Box::new(bits64(0x8000_1EEF, 64)))));
950            assert_eq!(Unsat, solver.check_sat());
951        } else {
952            panic!("simple_translation_table_walk failed")
953        }
954    }
955}