1use 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>, 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 if let Some(uxn) = self.uxn {
194 solver.assert_eq(Extract(54, 54, Box::new(Var(desc))), bool_to_bit(uxn))
195 }
196
197 if let Some(pxn) = self.pxn {
199 solver.assert_eq(Extract(54, 54, Box::new(Var(desc))), bool_to_bit(pxn))
200 }
201
202 if let Some(contiguous) = self.contiguous {
204 solver.assert_eq(Extract(52, 52, Box::new(Var(desc))), bool_to_bit(contiguous))
205 }
206
207 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 if let Some(af) = self.af {
214 solver.assert_eq(Extract(10, 10, Box::new(Var(desc))), bool_to_bit(af))
215 }
216
217 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 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 if let Some(ns) = self.ns {
229 solver.assert_eq(Extract(5, 5, Box::new(Var(desc))), bool_to_bit(ns))
230 }
231
232 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 if let Some(xn) = self.xn {
263 solver.assert_eq(Extract(54, 54, Box::new(Var(desc))), bool_to_bit(xn))
264 }
265
266 solver.assert_eq(Extract(53, 53, Box::new(Var(desc))), bits64(0, 1));
268
269 if let Some(contiguous) = self.contiguous {
271 solver.assert_eq(Extract(52, 52, Box::new(Var(desc))), bool_to_bit(contiguous))
272 }
273
274 solver.assert_eq(Extract(53, 53, Box::new(Var(desc))), bits64(0, 1));
276
277 if let Some(af) = self.af {
279 solver.assert_eq(Extract(10, 10, Box::new(Var(desc))), bool_to_bit(af))
280 }
281
282 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 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 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#[derive(Copy, Clone)]
304pub struct L3Index {
305 base_addr: u64,
306 ix: usize,
307}
308
309#[derive(Copy, Clone)]
311pub struct L012Index {
312 base_addr: u64,
313 ix: usize,
314}
315
316#[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
335pub 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#[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 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 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 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 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 solver.assert_eq(Extract(51, 48, Box::new(Var(desc))), bits64(0b0000, 4));
422
423 solver.assert_eq(Extract(1, 0, Box::new(Var(desc))), bits64(0b11, 2));
425
426 attrs.set(desc, solver);
428
429 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#[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 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#[derive(Copy, Clone, Debug, PartialEq, Eq)]
489pub struct VirtualAddress {
490 bits: u64,
491}
492
493impl VirtualAddress {
494 pub fn from_u64(bits: u64) -> Self {
498 VirtualAddress { bits: bzhi_u64(bits, 48) }
499 }
500
501 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 pub fn page_offset(self) -> u64 {
511 self.bits & 0xFFF
512 }
513
514 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 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 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 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 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 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 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 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 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 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 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 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 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}