зеркало из https://github.com/microsoft/Komodo.git
934 строки
34 KiB
Plaintext
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));
|
|
}
|
|
}
|