Komodo/verified/entry.sdfy

934 строки
34 KiB
Plaintext

include {:verbatim} "kom_common.i.dfy"
include {:verbatim} "pagedb.i.dfy"
include {:verbatim} "smcapi.i.dfy"
include {:verbatim} "entry.i.dfy"
include {:verbatim} "entrybits.i.dfy"
include "ARMdecls.sdfy"
include "kom_utils.sdfy"
#verbatim
function method banked_regs_framesize(): int { 18*4 }
predicate {:opaque} wellformed_banked_regs_stackframe(m:memstate, a:addr)
requires ValidMemState(m)
{
ValidMemRange(a, a + banked_regs_framesize())
&& forall i:int | 12 <= i <= 17 ::
ValidPsrWord(MemContents(m, a + i * 4))
}
lemma lemma_wellformed_banked_regs_stackframe_preserved(m1:memstate, m2:memstate, a:addr)
requires ValidMemState(m1) && ValidMemState(m2)
requires wellformed_banked_regs_stackframe(m1, a)
requires forall p:addr :: ValidMem(p) && (a <= p < a + banked_regs_framesize())
==> MemContents(m1, p) == MemContents(m2, p)
ensures wellformed_banked_regs_stackframe(m2, a)
{
reveal_wellformed_banked_regs_stackframe();
forall i:int | 12 <= i <= 17
ensures ValidPsrWord(MemContents(m2, a + i * 4));
{
assert MemContents(m1, a + i * 4) == MemContents(m2, a + i * 4);
}
}
predicate {:opaque} banked_regs_stackframe(s:state, m:memstate, a:addr)
requires ValidState(s) && ValidMemState(m)
ensures banked_regs_stackframe(s,m,a) ==> wellformed_banked_regs_stackframe(m, a)
{
reveal_ValidRegState();
reveal_ValidSRegState();
reveal_wellformed_banked_regs_stackframe();
wellformed_banked_regs_stackframe(m, a)
&& MemContents(m, a+(0*4)) == s.regs[SP(User)]
&& MemContents(m, a+(1*4)) == s.regs[SP(FIQ)]
&& MemContents(m, a+(2*4)) == s.regs[SP(IRQ)]
&& MemContents(m, a+(3*4)) == s.regs[SP(Supervisor)]
&& MemContents(m, a+(4*4)) == s.regs[SP(Abort)]
&& MemContents(m, a+(5*4)) == s.regs[SP(Undefined)]
&& MemContents(m, a+(6*4)) == s.regs[LR(User)]
&& MemContents(m, a+(7*4)) == s.regs[LR(FIQ)]
&& MemContents(m, a+(8*4)) == s.regs[LR(IRQ)]
&& MemContents(m, a+(9*4)) == s.regs[LR(Supervisor)]
&& MemContents(m, a+(10*4)) == s.regs[LR(Abort)]
&& MemContents(m, a+(11*4)) == s.regs[LR(Undefined)]
&& MemContents(m, a+(12*4)) == s.sregs[spsr(FIQ)]
&& MemContents(m, a+(13*4)) == s.sregs[spsr(IRQ)]
&& MemContents(m, a+(14*4)) == s.sregs[spsr(Supervisor)]
&& MemContents(m, a+(15*4)) == s.sregs[spsr(Abort)]
&& MemContents(m, a+(16*4)) == s.sregs[spsr(Undefined)]
&& MemContents(m, a+(17*4)) == s.sregs[spsr(Monitor)]
}
lemma lemma_stackunstack_banked_regs(s1:state, m1:memstate, s2:state, m2:memstate, a:addr)
requires SaneState(s1) && ValidMemState(m1) && SaneState(s2) && ValidMemState(m2)
requires banked_regs_stackframe(s1, m1, a) && banked_regs_stackframe(s2, m2, a)
requires forall p:addr :: ValidMem(p) && (a <= p < a + banked_regs_framesize())
==> MemContents(m1, p) == MemContents(m2, p)
ensures BankedRegsInvariant(s1, s2) && SpsrsInvariant(s1, s2)
{
reveal_wellformed_banked_regs_stackframe();
// sigh. help Dafny see that we're enumerating all constructors of a datatype
assert forall m:mode {:trigger SP(m)} {:trigger LR(m)} {:trigger spsr(m)} ::
m.User? || m.FIQ? || m.IRQ? || m.Supervisor? || m.Abort? || m.Undefined?
|| m.Monitor?;
assert BankedRegsInvariant(s1, s2) by {
reveal_ValidRegState();
forall m | m != Monitor
ensures s1.regs[SP(m)] == s2.regs[SP(m)] && s1.regs[LR(m)] == s2.regs[LR(m)]
{ reveal_banked_regs_stackframe(); }
}
assert SpsrsInvariant(s1, s2) by {
reveal_ValidSRegState();
forall m | m != User
ensures s1.sregs[spsr(m)] == s2.sregs[spsr(m)]
{ reveal_banked_regs_stackframe(); }
}
}
predicate SpsrsInvariant(s:state, r:state)
requires ValidState(s) && ValidState(r)
{
reveal_ValidSRegState();
forall m | m != User :: s.sregs[spsr(m)] == r.sregs[spsr(m)]
}
#endverbatim
procedure MRS_STR(
operand reg:sreg,
operand base:word,
operand ofs:word,
out operand tmp:reg)
requires/ensures
SaneState(this);
requires
ValidMem(base + ofs);
@tmp != OSP && @tmp != @base && @tmp != @ofs;
modifies
mem;
ensures
//sp == old(sp);
MemPreservingExcept(old(this), this, old(base+ofs), old(base+ofs+4));
MemContents(this.m, old(base+ofs)) == old(reg);
{
MRS(tmp, reg);
STR(tmp, base, ofs);
}
procedure stack_banked_regs(out operand tmp:reg, ghost stack_bytes_in:int)
returns (ghost stack_bytes:int)
requires/ensures
SaneState(this);
requires
@tmp != OSP;
stack_bytes_in >= banked_regs_framesize();
StackBytesRemaining(this, stack_bytes_in);
reads
spsr_fiq; spsr_irq; spsr_svc; spsr_abt; spsr_und; spsr_mon;
sp_usr; sp_fiq; sp_irq; sp_svc; sp_abt; sp_und;
lr_usr; lr_fiq; lr_irq; lr_svc; lr_abt; lr_und;
modifies
mem; sp;
ensures
sp == old(sp)-banked_regs_framesize();
stack_bytes == stack_bytes_in-banked_regs_framesize();
StackBytesRemaining(this,stack_bytes);
banked_regs_stackframe(old(this), this.m, sp);
NonStackMemPreserving(old(this),this);
ParentStackPreserving(old(this),this);
{
SUB(sp, sp, const(banked_regs_framesize()));
stack_bytes := stack_bytes_in - banked_regs_framesize();
ghost var start := this;
MRS_STR(spsr_fiq, sp, const(12*4), tmp);
MRS_STR(spsr_irq, sp, const(13*4), tmp);
MRS_STR(spsr_svc, sp, const(14*4), tmp);
MRS_STR(spsr_abt, sp, const(15*4), tmp);
MRS_STR(spsr_und, sp, const(16*4), tmp);
MRS_STR(spsr_mon, sp, const(17*4), tmp);
ghost var midway := this;
assert wellformed_banked_regs_stackframe(this.m, sp) by {
reveal_wellformed_banked_regs_stackframe();
forall i:int :| 12 <= i <= 17 :: ValidPsrWord(MemContents(this.m, sp + i * 4))
{ reveal_ValidSRegState(); }
}
//assert RegPreservingExcept(start,this,set(@tmp));
assert MemPreservingExcept(old(this), this, sp + 12*4, sp + 18*4);
MRS_STR(sp_usr, sp, const(0*4), tmp);
MRS_STR(sp_fiq, sp, const(1*4), tmp);
MRS_STR(sp_irq, sp, const(2*4), tmp);
MRS_STR(sp_svc, sp, const(3*4), tmp);
MRS_STR(sp_abt, sp, const(4*4), tmp);
MRS_STR(sp_und, sp, const(5*4), tmp);
MRS_STR(lr_usr, sp, const(6*4), tmp);
MRS_STR(lr_fiq, sp, const(7*4), tmp);
MRS_STR(lr_irq, sp, const(8*4), tmp);
MRS_STR(lr_svc, sp, const(9*4), tmp);
MRS_STR(lr_abt, sp, const(10*4), tmp);
MRS_STR(lr_und, sp, const(11*4), tmp);
//assert RegPreservingExcept(midway, this, set(@tmp));
assert MemPreservingExcept(midway, this, sp, sp + 12*4);
assert wellformed_banked_regs_stackframe(this.m, sp)
by { reveal_wellformed_banked_regs_stackframe(); }
assert banked_regs_stackframe(old(this), this.m, sp)
by { reveal_banked_regs_stackframe(); }
}
procedure LDR_MSR_banked(
out operand reg:sreg,
operand base:word,
operand ofs:word,
out operand tmp:reg)
requires/ensures
SaneState(this);
requires
ValidBankedRegOperand(this, @reg) && @reg is OReg && @reg != OReg(SP(Monitor));
ValidMem(base + ofs);
@tmp != OSP && @tmp != @base && @tmp != @ofs;
reads
mem;
ensures
//CoreRegPreservingExcept(old(this), this, set(@tmp));
forall r:ARMReg :: ((r is SP && r.spm != Monitor) || (r is LR && r.lrm != Monitor)) && r != @reg.r ==> this.regs[r] == old(this).regs[r];
//sp == old(sp);
SRegsInvariant(old(this), this);
reg == MemContents(old(this.m), old(base+ofs));
{
LDR(tmp, base, ofs);
MSR(reg, tmp);
}
procedure LDR_MSR_spsr(
out operand reg:sreg,
operand base:word,
operand ofs:word,
out operand tmp:reg)
requires/ensures
SaneState(this);
requires
ValidMem(base + ofs);
//ValidMrsMsrOperand(this, @reg);
@reg is OSReg && @reg.sr is spsr && ValidPsrWord(MemContents(this.m, base + ofs));
@tmp != OSP && @tmp != @base && @tmp != @ofs;
reads
mem;
ensures
//sp == old(sp);
//CoreRegPreservingExcept(old(this),this,set(@tmp));
BankedRegsInvariant(old(this),this);
forall sr :: this.sregs?[sr] && old(this).sregs?[sr] && sr != @reg.sr
==> this.sregs[sr] == old(this).sregs[sr];
reg == MemContents(old(this.m), old(base+ofs));
{
LDR(tmp, base, ofs);
MSR(reg, tmp);
}
procedure unstack_banked_regs(out operand tmp:reg, ghost stack_bytes_in:int)
returns (ghost stack_bytes:int)
requires/ensures
SaneState(this);
requires
@tmp == OReg(R2); //@tmp != OSP;
isUInt32(sp + banked_regs_framesize());
sp + banked_regs_framesize() <= StackBase();
StackBytesRemaining(this, stack_bytes_in);
wellformed_banked_regs_stackframe(this.m, sp);
reads
mem;
modifies
sp;
spsr_fiq; spsr_irq; spsr_svc; spsr_abt; spsr_und; spsr_mon;
sp_usr; sp_fiq; sp_irq; sp_svc; sp_abt; sp_und;
lr_usr; lr_fiq; lr_irq; lr_svc; lr_abt; lr_und;
ensures
sp == old(sp)+banked_regs_framesize();
stack_bytes == stack_bytes_in + banked_regs_framesize();
StackBytesRemaining(this, stack_bytes);
banked_regs_stackframe(this, old(this.m), old(sp));
//CoreRegPreservingExcept(old(this), this, set(@tmp,@sp));
{
reveal_banked_regs_stackframe();
reveal_wellformed_banked_regs_stackframe();
reveal ValidSRegState;
LDR_MSR_banked(sp_usr, sp, const(0*4), tmp);
LDR_MSR_banked(sp_fiq, sp, const(1*4), tmp);
LDR_MSR_banked(sp_irq, sp, const(2*4), tmp);
LDR_MSR_banked(sp_svc, sp, const(3*4), tmp);
LDR_MSR_banked(sp_abt, sp, const(4*4), tmp);
LDR_MSR_banked(sp_und, sp, const(5*4), tmp);
LDR_MSR_banked(lr_usr, sp, const(6*4), tmp);
LDR_MSR_banked(lr_fiq, sp, const(7*4), tmp);
LDR_MSR_banked(lr_irq, sp, const(8*4), tmp);
LDR_MSR_banked(lr_svc, sp, const(9*4), tmp);
LDR_MSR_banked(lr_abt, sp, const(10*4), tmp);
LDR_MSR_banked(lr_und, sp, const(11*4), tmp);
LDR_MSR_spsr(spsr_fiq, sp, const(12*4), tmp);
LDR_MSR_spsr(spsr_irq, sp, const(13*4), tmp);
LDR_MSR_spsr(spsr_svc, sp, const(14*4), tmp);
LDR_MSR_spsr(spsr_abt, sp, const(15*4), tmp);
LDR_MSR_spsr(spsr_und, sp, const(16*4), tmp);
LDR_MSR_spsr(spsr_mon, sp, const(17*4), tmp);
ADD(sp, sp, const(banked_regs_framesize()));
stack_bytes := stack_bytes_in + banked_regs_framesize();
}
procedure smc_enter_err(
operand callno:reg,
operand pagenr:reg,
operand pagedb_base:reg,
out operand err:reg,
ghost pagedb:PageDb)
requires/ensures
SaneState(this);
requires
@callno == @err == OReg(R0) && @pagenr == OReg(R1);
@pagedb_base == OReg(R12) && pagedb_base == AddressOfGlobal(PageDb());
validPageDb(pagedb);
pageDbCorresponds(this.m, pagedb);
callno == KOM_SMC_ENTER || callno == KOM_SMC_RESUME;
reads
globals; mem;
modifies
r8; r9; r10; r11;
ensures
SmcProcedureInvariant(old(this), this);
err == smc_enter_err(pagedb, old(pagenr), old(callno) == KOM_SMC_RESUME);
{
if (pagenr >= const(KOM_SECURE_NPAGES)) {
assert !validPageNr(pagenr);
err := const(KOM_ERR_INVALID_PAGENO);
} else {
assert validPageNr(pagenr);
load_page_type(pagenr, pagedb_base, r10, r9, pagedb);
if( r9 != const(KOM_PAGE_DISPATCHER) ) {
assert !(pagedb[pagenr] is PageDbEntryTyped && pagedb[pagenr].entry is Dispatcher);
err := const(KOM_ERR_INVALID_PAGENO);
} else {
assert pagedb[pagenr] is PageDbEntryTyped && pagedb[pagenr].entry is Dispatcher;
ADD(r10, r10, const(PAGEDB_ENTRY_ADDRSPACE));
assert r10 == G_PAGEDB_ENTRY(pagenr) + PAGEDB_ENTRY_ADDRSPACE;
WordAlignedAdd_(G_PAGEDB_ENTRY(pagenr), PAGEDB_ENTRY_ADDRSPACE, r10);
LDRglobal(r8, PageDb(), pagedb_base, r10);
ghost var addrspace := pagedb[pagenr].addrspace;
assert validPageNr(addrspace) by { reveal_validPageDb(); }
assert r8 == page_monvaddr(addrspace) && WordAligned(r8) by {
reveal_pageDbEntryCorresponds();
reveal_validPageDb();
assert pageDbCorrespondsOnly(this.m, pagedb, pagenr);
ghost var entryWords := extractPageDbEntry(this.m, pagenr);
assert entryWords[BytesToWords(PAGEDB_ENTRY_ADDRSPACE)] ==
page_monvaddr(pagedb[pagenr].addrspace);
extractPageDbToAbstractOne(this.m, pagenr, PAGEDB_ENTRY_ADDRSPACE);
assert r10 == G_PAGEDB_ENTRY(pagenr) + PAGEDB_ENTRY_ADDRSPACE;
assert entryWords[BytesToWords(PAGEDB_ENTRY_ADDRSPACE)] ==
GlobalWord(this.m, PageDb(), r10);
assert GlobalWord(this.m, PageDb(), r10) ==
page_monvaddr(pagedb[pagenr].addrspace);
}
WordAlignedAdd_(r8,ADDRSPACE_STATE,r8+ADDRSPACE_STATE);
assert ValidMem(r8 + ADDRSPACE_STATE) by { reveal_validPageDb(); }
LDR(r8, r8, const(ADDRSPACE_STATE));
assert r8 == KOM_ADDRSPACE_FINAL
<==> pagedb[addrspace].entry.state == FinalState
by {
reveal_validPageDb();
reveal_pageContentsCorresponds();
reveal_pageDbAddrspaceCorresponds();
}
if( r8 != const(KOM_ADDRSPACE_FINAL) ) {
err := const(KOM_ERR_NOT_FINAL);
} else {
assert this.m == old(this.m);
assert SaneState(this);
page_monvaddr_impl(r9,pagenr,r10);
assert r9 == page_monvaddr(pagenr);
assert MemContents(this.m, page_monvaddr(pagenr) +
DISPATCHER_ENTERED) == to_i(pagedb[pagenr].entry.entered)
by {
reveal_pageContentsCorresponds();
reveal_pageDbDispatcherCorresponds();
}
LDR(r9,r9,const(DISPATCHER_ENTERED));
assert r9 == 1 <==> pagedb[pagenr].entry.entered;
assert callno == old(callno);
if (callno == const(KOM_SMC_RESUME)) {
// smc_resume
if( r9 != 1 ) {
err := const(KOM_ERR_NOT_ENTERED);
} else {
err := const(KOM_ERR_SUCCESS);
}
} else {
// smc_enter
if( r9 == 1 ) {
err := const(KOM_ERR_ALREADY_ENTERED);
} else {
err := const(KOM_ERR_SUCCESS);
}
}
}
}
}
}
var {:state sreg(scr)} scr:word;
var {:state sreg(ttbr0)} ttbr0:word;
procedure switch_addrspace(
operand pagenr:reg,
operand pagedb_base:reg,
ghost isresume:bool,
ghost pagedb:PageDb)
requires/ensures
SaneState(this);
requires
@pagenr == OReg(R1);
@pagedb_base == OReg(R12) && pagedb_base == AddressOfGlobal(PageDb());
validPageDb(pagedb);
pageDbCorresponds(this.m, pagedb);
validPageNr(pagenr);
validDispatcherPage(pagedb, pagenr);
!hasStoppedAddrspace(pagedb, pagenr);
reads
globals; mem;
modifies
scr; ttbr0; r10; r11;
ensures
BankedRegsInvariant(old(this),this);
this.conf.ttbr0.ptbase == page_paddr(l1pOfDispatcher(pagedb, old(pagenr)));
this.conf.scr.ns == Secure && this.conf.scr.irq && this.conf.scr.fiq;
{
//-------------------------------------------------------------------------
// update SCR
//-------------------------------------------------------------------------
// read SCR
MRC(r10, scr);
ghost var old_scr := r10;
// clear NS bit
AND(r10, r10, 0xfffffffe);
// set IRQ and FIQ bits
ORR(r10, r10, 6);
// write SCR
lemma_scr_entry(old_scr, r10);
MCR(scr, r10);
assert this.conf.scr.ns == Secure;
//-------------------------------------------------------------------------
// load l1p into ttbr0
//-------------------------------------------------------------------------
ghost var addrspace := pagedb[pagenr].addrspace;
assert validAddrspacePage(pagedb, addrspace) by { reveal_validPageDb(); }
ghost var l1p := l1pOfDispatcher(pagedb, pagenr);
lemma_LeftShift3(pagenr);
LSL(r10, pagenr, const(PAGEDB_ENTRY_SHIFT));
assert r10 == G_PAGEDB_ENTRY(pagenr);
ADD(r10, r10, const(PAGEDB_ENTRY_ADDRSPACE));
assert r10 == G_PAGEDB_ENTRY(pagenr) + PAGEDB_ENTRY_ADDRSPACE;
WordAlignedAdd_(G_PAGEDB_ENTRY(pagenr), PAGEDB_ENTRY_ADDRSPACE, r10);
LDRglobal(r11, PageDb(), pagedb_base, r10);
assert r11 == page_monvaddr(addrspace) by {
reveal_pageDbEntryCorresponds();
assert validAddrspacePage(pagedb, addrspace);
}
LDR(r10,r11,const(ADDRSPACE_L1PT_PHYS));
assert r10 == page_paddr(pagedb[addrspace].entry.l1ptnr) by {
assert pageDbAddrspaceCorresponds(addrspace, pagedb[addrspace].entry,
extractPage(this.m, addrspace))
by {
assert validAddrspacePage(pagedb, addrspace);
assert pageDbCorrespondsOnly(this.m, pagedb, addrspace);
reveal_pageContentsCorresponds();
}
reveal_pageDbAddrspaceCorresponds();
}
assert r10 == page_paddr(l1p);
assert PageAligned(r10);
MCR(ttbr0, r10);
assert this.conf.ttbr0.ptbase == page_paddr(l1p);
}
procedure leave_secure_world(out operand tmp:reg)
requires/ensures
SaneState(this);
requires
@tmp != OSP;
modifies
scr;
ensures
//BankedRegsInvariant(old(this),this);
//SpsrsInvariant(old(this), this);
this.conf.scr.ns == NotSecure && !this.conf.scr.irq && !this.conf.scr.fiq;
{
// read SCR
MRC(tmp, scr);
ghost var old_scr := tmp;
// clear IRQ and FIQ bits
AND(tmp, tmp, 0xfffffff9);
// set NS bit
ORR(tmp, tmp, 1);
// write SCR
lemma_scr_exit(old_scr, tmp);
MCR(scr, tmp);
}
#verbatim
predicate EnterResumeSmcProcedureInvariant(s:state, r:state)
requires SaneState(s) && SaneState(r)
{
mode_of_state(r) == mode_of_state(s) // implied by SaneState
&& StackPreserving(s,r)
&& BankedRegsInvariant(s, r)
&& SpsrsInvariant(s, r)
&& r.conf.scr.ns == NotSecure
}
#endverbatim
procedure pre_entry_enter(
operand pagenr:reg,
operand arg1:reg,
operand arg2:reg,
operand arg3:reg,
operand pagedb_base:reg,
ghost pagedb:PageDb)
requires/ensures
SaneState(this);
requires
@pagenr == OReg(R1) && @arg1 == OReg(R2) && @arg2 == OReg(R3) && @arg3 == OReg(R4);
@pagedb_base == OReg(R12) && pagedb_base == AddressOfGlobal(PageDb());
validPageDb(pagedb);
pageDbCorresponds(this.m, pagedb);
smc_enter_err(pagedb, pagenr, false) == KOM_ERR_SUCCESS;
reads
mem;
modifies
globals; scr; ttbr0; spsr_mon; r0; r1; r2; r10; r11; r12; lr;
ensures
GlobalsPreservingExcept(old(this), this, set(CurDispatcherOp()));
pageDbCorresponds(this.m, pagedb);
StackPreserving(old(this), this);
//BankedRegsInvariant(old(this), this);
preEntryEnter(old(this), this, pagedb, old(pagenr), old(arg1), old(arg2), old(arg3));
spsr_of_state(this).m == User;
//sp == old(sp);
{
assert validPageNr(pagenr);
assert validDispatcherPage(pagedb, pagenr);
assert !hasStoppedAddrspace(pagedb, pagenr);
ghost var l1p := l1pOfDispatcher(pagedb, pagenr);
//-------------------------------------------------------------------------
// Switch addrspace
//-------------------------------------------------------------------------
switch_addrspace(pagenr, pagedb_base, false, pagedb);
assert pagenr == old(pagenr);
//-------------------------------------------------------------------------
// Set SPSR to Usermode
//-------------------------------------------------------------------------
r10 := 0x10;
assert psr_mask_mode(0x10) == 0x10 by {
assert WordAsBits(0x10) == 0x10 && WordAsBits(0x1f) == 0x1f
by { reveal WordAsBits; }
assert BitAnd(0x10, 0x1f) == 0x10 by { reveal BitAnd; }
lemma_WordBitEquiv(0x10, 0x10);
}
assert decode_mode(psr_mask_mode(r10)) == User;
assert ValidModeChange'(this, User);
assert ValidModeChange(this, r10);
MSR(spsr_mon, r10);
assert spsr_of_state(this).m == User;
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
// Load entrypoint into LR
//-------------------------------------------------------------------------
page_monvaddr_impl(r10,pagenr,r11);
LDR(lr,r10,const(DISPATCHER_ENTRYPOINT));
assert lr == pagedb[pagenr].entry.entrypoint
by {
assert pageDbCorrespondsOnly(this.m,pagedb,pagenr);
assert validDispatcherPage(pagedb,pagenr);
reveal_pageContentsCorresponds();
assert pageContentsCorresponds(pagenr,pagedb[pagenr],
extractPage(this.m,pagenr));
reveal_pageDbDispatcherCorresponds();
}
//-------------------------------------------------------------------------
// Update current dispatcher
//-------------------------------------------------------------------------
LDRglobaladdr(r12, CurDispatcherOp());
STRglobal(r10, CurDispatcherOp(), r12, 0);
//-------------------------------------------------------------------------
// Move arguments to regs
//-------------------------------------------------------------------------
r0 := arg1;
r1 := arg2;
r2 := arg3;
// XXX: TODO (also in spec): zero other regs!
//-------------------------------------------------------------------------
lemma_SameMemAndGlobalsPreservesPageDb'(old(this), this, pagedb);
}
procedure pre_entry_resume_context(ghost dispPg:PageNr, ghost pagedb:PageDb)
requires/ensures
SaneState(this);
requires
lr == page_monvaddr(dispPg);
validPageDb(pagedb);
pageDbCorresponds(this.m, pagedb);
validDispatcherPage(pagedb, dispPg);
reads
mem;
modifies
r0; r1; r2; r3; r4; r5; r6; r7; r8; r9; r10; r11; r12; lr_usr; sp_usr; lr;
ensures
//SRegsInvariant(old(this),this);
//sp == old(sp);
let disp := pagedb[dispPg].entry in
r0 == disp.ctxt.regs[R0] && r1 == disp.ctxt.regs[R1] &&
r2 == disp.ctxt.regs[R2] && r3 == disp.ctxt.regs[R3] &&
r4 == disp.ctxt.regs[R4] && r5 == disp.ctxt.regs[R5] &&
r6 == disp.ctxt.regs[R6] && r7 == disp.ctxt.regs[R7] &&
r8 == disp.ctxt.regs[R8] && r9 == disp.ctxt.regs[R9] &&
r10 == disp.ctxt.regs[R10] && r11 == disp.ctxt.regs[R11] &&
r12 == disp.ctxt.regs[R12]
&& lr_usr == disp.ctxt.regs[LR(User)]
&& sp_usr == disp.ctxt.regs[SP(User)]
&& lr == pagedb[dispPg].entry.ctxt.pc;
{
ghost var disp := pagedb[dispPg].entry;
LDR_MSR_banked(lr_usr, lr, const(DISP_CTXT_LR), r0);
LDR_MSR_banked(sp_usr, lr, const(DISP_CTXT_SP), r0);
LDR(r0,lr,const(DISP_CTXT_R0));
LDR(r1,lr,const(DISP_CTXT_R1));
LDR(r2,lr,const(DISP_CTXT_R2));
LDR(r3,lr,const(DISP_CTXT_R3));
LDR(r4,lr,const(DISP_CTXT_R4));
LDR(r5,lr,const(DISP_CTXT_R5));
LDR(r6,lr,const(DISP_CTXT_R6));
LDR(r7,lr,const(DISP_CTXT_R7));
LDR(r8,lr,const(DISP_CTXT_R8));
LDR(r9,lr,const(DISP_CTXT_R9));
LDR(r10,lr,const(DISP_CTXT_R10));
LDR(r11,lr,const(DISP_CTXT_R11));
LDR(r12,lr,const(DISP_CTXT_R12));
LDR(lr,lr,const(DISP_CTXT_PC));
assert r0 == disp.ctxt.regs[R0] && r1 == disp.ctxt.regs[R1] &&
r2 == disp.ctxt.regs[R2] && r3 == disp.ctxt.regs[R3] &&
r4 == disp.ctxt.regs[R4] && r5 == disp.ctxt.regs[R5] &&
r6 == disp.ctxt.regs[R6] && r7 == disp.ctxt.regs[R7] &&
r8 == disp.ctxt.regs[R8] && r9 == disp.ctxt.regs[R9] &&
r10 == disp.ctxt.regs[R10] && r11 == disp.ctxt.regs[R11] &&
r12 == disp.ctxt.regs[R12]
&& lr_usr == disp.ctxt.regs[LR(User)]
&& sp_usr == disp.ctxt.regs[SP(User)]
&& lr == pagedb[dispPg].entry.ctxt.pc
by {
assert validDispatcherPage(pagedb,dispPg);
ghost var pg := extractPage(this.m,dispPg);
reveal_pageDbDispatcherCorresponds();
assert pageDbDispatcherCorresponds(dispPg, pagedb[dispPg].entry, pg) by {
assert pageDbCorrespondsOnly(this.m,pagedb,dispPg);
reveal_pageContentsCorresponds();
assert pageContentsCorresponds(dispPg,pagedb[dispPg], pg);
}
}
}
procedure pre_entry_resume(
operand pagenr:reg,
operand pagedb_base:reg,
ghost pagedb:PageDb)
requires/ensures
SaneState(this);
requires
@pagenr == OReg(R1);
@pagedb_base == OReg(R12) && pagedb_base == AddressOfGlobal(PageDb());
validPageDb(pagedb);
pageDbCorresponds(this.m, pagedb);
smc_enter_err(pagedb, pagenr, true) == KOM_ERR_SUCCESS;
reads
mem;
modifies
globals; scr; ttbr0; spsr_mon;
r0; r1; r2; r3; r4; r5; r6; r7; r8; r9; r10; r11; r12; lr_usr; sp_usr; lr;
ensures
GlobalsPreservingExcept(old(this),this, set(CurDispatcherOp()));
pageDbCorresponds(this.m, pagedb);
StackPreserving(old(this), this);
preEntryResume(old(this), this, pagedb, old(pagenr));
spsr_of_state(this).m == User;
//sp == old(sp);
{
ghost var dispPg := pagenr;
assert validPageNr(dispPg);
assert validDispatcherPage(pagedb, dispPg);
assert !hasStoppedAddrspace(pagedb, dispPg);
//-------------------------------------------------------------------------
// Switch addrspace
//-------------------------------------------------------------------------
switch_addrspace(pagenr, pagedb_base, true, pagedb);
assert pagenr == old(pagenr);
//-------------------------------------------------------------------------
// Update current dispatcher
//-------------------------------------------------------------------------
page_monvaddr_impl(lr,pagenr,r0);
LDRglobaladdr(r2, CurDispatcherOp());
STRglobal(lr, CurDispatcherOp(), r2, 0);
// help dafny see pagedb invariant
globalUnmodifiedImpliesCorrespondingPreserved(pagedb,old(this).m,this.m);
//-------------------------------------------------------------------------
// Restore SPSR
//-------------------------------------------------------------------------
assert validDispatcherContext(pagedb[dispPg].entry.ctxt) by { reveal_validPageDb(); }
LDR(r0,lr,const(DISP_CTXT_PSR));
assert r0 == pagedb[dispPg].entry.ctxt.cpsr by
{
assert pageDbCorrespondsOnly(this.m,pagedb,dispPg);
reveal_pageContentsCorresponds();
assert pageContentsCorresponds(dispPg,pagedb[dispPg],
extractPage(this.m,dispPg));
reveal_pageDbDispatcherCorresponds();
}
assert decode_mode'(psr_mask_mode(pagedb[dispPg].entry.ctxt.cpsr)) == Just(User);
MSR(spsr_mon, r0);
assert this.sregs[spsr(Monitor)] == pagedb[dispPg].entry.ctxt.cpsr;
assert spsr_of_state(this).m == User;
//-------------------------------------------------------------------------
// Restore integer context
//-------------------------------------------------------------------------
pre_entry_resume_context(dispPg, pagedb);
}
#verbatim
lemma lemma_ValidEntryPost(s:state, sd:PageDb, r1:state, rd:PageDb, r2:state, dp:word,
a1:word, a2:word, a3:word)
requires ValidState(s) && ValidState(r1) && ValidState(r2) && validPageDb(sd)
requires validExceptionTransition(SysState(r1, rd), SysState(r2, rd), dp)
requires OperandContents(r1, OReg(R0)) == OperandContents(r2, OReg(R0))
requires OperandContents(r1, OReg(R1)) == OperandContents(r2, OReg(R1))
ensures smc_enter(s, sd, r1, rd, dp, a1, a2, a3)
==> smc_enter(s, sd, r2, rd, dp, a1, a2, a3)
ensures smc_resume(s, sd, r1, rd, dp) ==> smc_resume(s, sd, r2, rd, dp)
{
reveal_ValidRegState();
if smc_enter(s, sd, r1, rd, dp, a1, a2, a3) {
if smc_enter_err(sd, dp, false) == KOM_ERR_SUCCESS {
lemma_validEnterPost(s, sd, r1, rd, r2, dp, a1, a2, a3);
}
}
if smc_resume(s, sd, r1, rd, dp) {
if smc_enter_err(sd, dp, true) == KOM_ERR_SUCCESS {
lemma_validResumePost(s, sd, r1, rd, r2, dp);
}
}
}
#endverbatim
procedure {:frame false} smc_enterresume_success(
operand pagenr:reg,
operand arg1:reg,
operand arg2:reg,
operand arg3:reg,
operand callno:reg,
operand pagedb_base:reg,
out operand err:reg,
out operand val:reg,
ghost pagedb_in: PageDb,
ghost stack_bytes: int)
returns (ghost pagedb: PageDb)
requires/ensures
SaneState(this);
StackBytesRemaining(this, stack_bytes);
requires
@pagenr == OReg(R1) && @arg1 == OReg(R2) && @arg2 == OReg(R3) && @arg3 == OReg(R4);
@callno == OReg(R5) && @err == OReg(R0) && @val == OReg(R1);
@pagedb_base == OReg(R12) && pagedb_base == AddressOfGlobal(PageDb());
stack_bytes >= banked_regs_framesize();
validPageDb(pagedb_in);
pageDbCorresponds(this.m, pagedb_in);
AUCIdef();
callno == KOM_SMC_ENTER || callno == KOM_SMC_RESUME;
smc_enter_err(pagedb_in, pagenr, callno == KOM_SMC_RESUME) == KOM_ERR_SUCCESS;
modifies // XXX: all saved and restored; not in fact "modified" on return!
spsr_fiq; spsr_irq; spsr_svc; spsr_abt; spsr_und; spsr_mon;
sp_usr; sp_fiq; sp_irq; sp_svc; sp_abt; sp_und;
lr_usr; lr_fiq; lr_irq; lr_svc; lr_abt; lr_und; sp;
modifies
mem; globals; scr; ttbr0; spsr_mon;
r0; r1; r2; r3; r4; r5; r6; r7; r8; r9; r10; r11; r12; lr_usr; sp_usr; lr;
ensures
EnterResumeSmcProcedureInvariant(old(this), this);
if old(callno) == KOM_SMC_ENTER then
validEnter(SysState(old(this), pagedb_in), SysState(this, pagedb),
old(pagenr), old(arg1), old(arg2), old(arg3))
else
validResume(SysState(old(this), pagedb_in), SysState(this, pagedb),
old(pagenr));
validPageDb(pagedb);
pageDbCorresponds(this.m, pagedb);
{
pagedb := pagedb_in;
ghost var dispPg := old(pagenr);
ghost var isEnter := old(callno) == KOM_SMC_ENTER;
ghost var exs;
assert nonStoppedDispatcher(pagedb, dispPg);
ghost var stack_bytes_local;
stack_bytes_local := stack_banked_regs(r0, stack_bytes);
lemma_SameMemAndGlobalsPreservesPageDb(old(this), this, pagedb);
ghost var s0 := this;
if (callno == const(KOM_SMC_ENTER)) {
pre_entry_enter(pagenr, arg1, arg2, arg3, pagedb_base, pagedb);
assert preEntryEnter(old(this), this, pagedb, dispPg, old(arg1),
old(arg2), old(arg3));
} else {
pre_entry_resume(pagenr, pagedb_base, pagedb);
assert preEntryResume(old(this), this, pagedb, dispPg);
}
ghost var s1 := this;
MOVS_PCLR_TO_USERMODE_AND_CONTINUE();
lemma_evalMOVSPCLRUC(s1, this, pagedb_in, dispPg);
ghost if (isEnter) {
exs, pagedb := lemma_validEnter(s0, s1, this, pagedb_in, dispPg,
old(arg1), old(arg2), old(arg3));
} else {
exs, pagedb := lemma_validResume(s0, s1, this, pagedb_in, dispPg);
}
ghost var s6 := this;
ghost var s6_sp := sp;
lemma_wellformed_banked_regs_stackframe_preserved(s0.m, this.m, sp);
stack_bytes_local := unstack_banked_regs(r2, stack_bytes_local);
assert stack_bytes_local == stack_bytes;
lemma_SameMemAndGlobalsPreservesPageDb(s6, this, pagedb);
assert StackPreserving(old(this), this);
lemma_stackunstack_banked_regs(old(this), s0.m, this, s6.m, s6_sp);
leave_secure_world(r2);
lemma_ValidEntryPre(old(this), s0, pagedb_in, s6, pagedb, dispPg,
old(arg1), old(arg2), old(arg3));
assert validExceptionTransition(SysState(s6, pagedb), SysState(this, pagedb), dispPg)
by { reveal_validExceptionTransition(); }
lemma_ValidEntryPost(old(this), pagedb_in, s6, pagedb, this,
dispPg, old(arg1), old(arg2), old(arg3));
}
procedure {:frame false} kom_smc_enterresume(
operand callno:reg,
operand disppg:reg,
operand arg1:reg,
operand arg2:reg,
operand arg3:reg,
operand pagedb_base:reg,
out operand err:reg,
out operand val:reg,
ghost pagedb_in:PageDb,
ghost stack_bytes: int)
returns (ghost pagedb:PageDb)
requires/ensures
SaneState(this);
StackBytesRemaining(this, stack_bytes);
requires
@callno == OReg(R0) && @disppg == OReg(R1);
@arg1 == OReg(R2) && @arg2 == OReg(R3) && @arg3 == OReg(R4);
@err == OReg(R0) && @val == OReg(R1);
@pagedb_base == OReg(R12) && pagedb_base == AddressOfGlobal(PageDb());
stack_bytes >= banked_regs_framesize();
validPageDb(pagedb_in);
pageDbCorresponds(this.m, pagedb_in);
AUCIdef();
this.conf.scr.ns == NotSecure; // FIXME: cleanup
callno == KOM_SMC_ENTER || callno == KOM_SMC_RESUME;
modifies // XXX: all saved and restored; not in fact "modified" on return!
spsr_fiq; spsr_irq; spsr_svc; spsr_abt; spsr_und; spsr_mon;
sp_usr; sp_fiq; sp_irq; sp_svc; sp_abt; sp_und;
lr_usr; lr_fiq; lr_irq; lr_svc; lr_abt; lr_und; sp;
modifies
mem; globals; scr; ttbr0; spsr_mon;
r0; r1; r2; r3; r4; r5; r6; r7; r8; r9; r10; r11; r12; lr_usr; sp_usr; lr;
ensures
EnterResumeSmcProcedureInvariant(old(this), this);
if old(callno) == KOM_SMC_ENTER then
smc_enter(old(this), pagedb_in, this, pagedb, old(disppg),
old(arg1), old(arg2), old(arg3))
else
smc_resume(old(this), pagedb_in, this, pagedb, old(disppg));
validPageDb(pagedb);
pageDbCorresponds(this.m, pagedb);
{
r5 := callno;
smc_enter_err(callno, disppg, pagedb_base, err, pagedb_in);
if (err != const(KOM_ERR_SUCCESS) ) {
pagedb := pagedb_in;
val := 0;
} else {
ghost var s0 := this;
pagedb := smc_enterresume_success(disppg, arg1, arg2, arg3, r5, pagedb_base,
err, val, pagedb_in, stack_bytes);
lemma_ValidEntryPre(old(this), s0, pagedb_in, this, pagedb, old(disppg),
old(arg1), old(arg2), old(arg3));
}
}