Commit 7fb756c2 authored by Roberto Hexsel's avatar Roberto Hexsel

handler for TLBmodified

parent 23de04fe
Pipeline #4103 skipped
# interrupt handlers
.include "cMIPS.s"
.text
.set noreorder
.set noreorder # do not reorder instructions
.set noat # do not use register $1 as $at
.align 2
.set M_StatusIEn,0x0000ff11 # STATUS.intEn=1, user mode
#----------------------------------------------------------------
#================================================================
# interrupt handler for external counter attached to IP5=HW3
# for extCounter address see vhdl/packageMemory.vhd
.bss
.align 2
.set noreorder
.global _counter_val # accumulate number of interrupts
.comm _counter_val 4
.comm _counter_saves 8*4 # area to save up to 8 registers
......@@ -67,12 +67,11 @@ extCounter:
#----------------------------------------------------------------
#----------------------------------------------------------------
#================================================================
# interrupt handler for UART attached to IP6=HW4
.bss
.align 2
.set noreorder
.global Ud
Ud: .comm rx_queue 16 # reception queue and pointers
.comm rx_hd 4
......@@ -138,15 +137,16 @@ UARTret:
#----------------------------------------------------------------
#----------------------------------------------------------------
#================================================================
# handler for COUNT-COMPARE registers -- IP7=HW5
.text
.set noreorder
.equ num_cycles, 64
.global countCompare
.ent countCompare
countCompare:
mfc0 $k1,c0_count # read COMPARE and clear IRQ
addiu $k1,$k1,64 # set next interrupt in 64 ticks
addiu $k1,$k1,num_cycles # set next interrupt in so many ticks
mtc0 $k1,c0_compare
mfc0 $k0, c0_status # Read STATUS register
......@@ -160,7 +160,7 @@ countCompare:
#----------------------------------------------------------------
#----------------------------------------------------------------
#================================================================
# functions to enable and disable interrupts, both return STATUS
.text
.set noreorder
......@@ -188,7 +188,234 @@ disableInterr:
#----------------------------------------------------------------
#----------------------------------------------------------------
#================================================================
## TLB handlers
## page table entry is { EntryLo0 int0 EntryLo1 int1 }
## int{0,1} is
## { fill_31..6, Modified_5, Used_4, Writable_3, eXecutable_2,
## Status_10 },
## Status is 00=unmapped, 01=mapped, 10=secondary_storage
#================================================================
#================================================================
# handle TLB Modified exception (store to page with bit dirty=0)
#
.global _excp_saves
.global _excp_0180ret
.global handle_Mod
.set noreorder
.ent handle_Mod
handle_Mod: # EntryHi points to offending TLB entry
tlbp # what is the offender's index?
lui $k1, %hi(_excp_saves)
ori $k1, $k1, %lo(_excp_saves)
sw $a0, 9*4($k1) # save registers
sw $a1, 10*4($k1)
sw $a2, 11*4($k1)
mfc0 $a0, c0_badvaddr
andi $a0, $a0, 0x1000 # check if even or odd page
beq $a0, $zero, M_even
mfc0 $a0, c0_context
M_odd: addi $a2, $a0, 12 # address for odd entry
mfc0 $k0, c0_entrylo1
ori $k0, $k0, 0x0004 # mark TLB entry as dirty/writable
j M_test
mtc0 $k0, c0_entrylo1
M_even: addi $a2, $a0, 4 # address for even entry
mfc0 $k0, c0_entrylo0
ori $k0, $k0, 0x0004 # mark TLB entry as dirty/writable
mtc0 $k0, c0_entrylo0
M_test: lw $a1, 0($a2) # read PT[badVAddr]
mfc0 $k0, c0_badvaddr # get faulting address
andi $a0, $a1, 0x0003 # check if page is mapped
beq $a0, $zero, M_seg_fault # no, abort simulation
nop
andi $a0, $a1, 0x0008 # check if page is writable
beq $a0, $zero, M_prot_viol # no, abort simulation
nop
andi $a0, $a1, 0x0002 # check if page is in secondary memory
bne $a0, $zero, M_sec_mem
nop
mfc0 $a0, c0_epc # check if fault is on an instruction
beq $a0, $k0, M_prot_viol # k0 is badVAddr
nop
ori $a1, $a1, 0x0030 # mark PT entry as modified, used
sw $a1, 0($a2)
tlbwi # write entry with dirty bit=1 back to TLB
lw $a0, 9*4($k1) # restore saved registers and return
lw $a1, 10*4($k1)
j _excp_0180ret
lw $a2, 11*4($k1)
M_seg_fault: # print message and abort simulation
la $k1, x_IO_BASE_ADDR
sw $k0, 0($k1)
jal cmips_kmsg
la $k1, 3 # segmentation fault
nop
nop
nop
wait 0x41
M_prot_viol: # print message and abort simulation
la $k1, x_IO_BASE_ADDR
sw $k0, 0($k1)
jal cmips_kmsg
la $k1, 2 # protection violation
nop
nop
nop
wait 0x41
M_sec_mem: # print message and abort simulation
la $k1, x_IO_BASE_ADDR
sw $k0, 0($k1)
jal cmips_kmsg
la $k1, 4 # secondary memory
nop
nop
nop
wait 0x41
.end handle_Mod
#----------------------------------------------------------------
#================================================================
# handle TLB Load exception: double-fault caused by a TLB miss
# to the Page Table -- mapping pointing to PT is not on TLB
# so, fix the TLB by storing mapping onto TLB[4]
.global _excp_saves
.global _excp_0180ret
.global handle_TLBL
.global _PT
.set MIDDLE_RAM, (x_DATA_BASE_ADDR + (x_DATA_MEM_SZ/2))
.ent handle_TLBL
handle_TLBL: # EntryHi points to offending TLB entry
tlbp # what is the offender's index?
lui $k1, %hi(_excp_saves)
ori $k1, $k1, %lo(_excp_saves)
sw $a0, 9*4($k1)
sw $a1, 10*4($k1)
sw $a2, 11*4($k1)
mfc0 $a0, c0_badvaddr
la $a1, (_PT + (x_INST_BASE_ADDR >>13)*16)
slt $a2, $a0, $a1 # a2 <- badVAddr <= PageTable
bne $a2, $zero, L_chks
nop
# this is same code as in start.s
# get physical page number for two pages at the bottom of PageTable
la $a0, ( MIDDLE_RAM >>13 )<<13
mtc0 $a0, c0_entryhi # tag for bottom double-page
la $a0, ( (MIDDLE_RAM + 0*4096) >>12 )<<6
ori $a1, $a0, 0b00000000000000000000000000000111 # ccc=0, d,v,g1
mtc0 $a1, c0_entrylo0 # bottom page (even)
la $a0, ( (MIDDLE_RAM + 1*4096) >>12 )<<6
ori $a1, $a0, 0b00000000000000000000000000000111 # ccc=0, d,v,g1
mtc0 $a1, c0_entrylo1 # bottom page + 1 (odd)
# and write it to TLB[4]
li $k0, 4
mtc0 $k0, c0_index
tlbwi
j L_ret # all work done, return
nop
L_chks: andi $a0, $a0, 0x1000 # check if even or odd page
beq $a0, $zero, L_even
mfc0 $a0, c0_context
L_odd: j L_test
addi $a2, $a0, 12 # address for odd entry
L_even: addi $a2, $a0, 4 # address for even entry
L_test: lw $a1, 0($a2)
mfc0 $k0, c0_badvaddr # get faulting address
andi $a0, $a1, 0x0003 # check if page is mapped
beq $a0, $zero, M_seg_fault
nop
andi $a0, $a1, 0x0002 # check if page is in secondary memory
bne $a0, $zero, M_sec_mem
nop
ori $a1, $a1, 0x0010 # mark PT entry as used
sw $a1, 0($a2)
L_ret: lw $a0, 9*4($k1) # nothing else to do, return??
lw $a1, 10*4($k1)
j _excp_0180ret
lw $a2, 11*4($k1)
.end handle_TLBL
#----------------------------------------------------------------
#================================================================
# purge an entry from the TLB
# int TLB_purge(void *V_addr)
# returns 0 if V_addr purged, 1 if V_addr not in TLB (probe failure)
#
.text
.global TLB_purge
.set noreorder
.ent TLB_purge
TLB_purge:
srl $a0, $a0, 13 # clear out in-page address bits
sll $a0, $a0, 13 #
mtc0 $a0, c0_entryhi
nop
tlbp # probe the TLB
nop
mfc0 $a0, c0_index # check for hit
srl $a0, $a0, 31 # keeo only MSbit
bne $a0, $zero, pu_miss # address not in TLB
move $v0, $a0 # V_addr not in TLB
tlbr # read the entry
li $a0, -8192 # -8192 = 0xffff.e000
mtc0 $a0, c0_entryhi # and write an un-mapped address to tag
addi $v0, $zero, -3 # -3 = 0xffff.fffd to clear valid bit
mfc0 $a0, c0_entrylo0 # invalidate the mappings
and $a0, $v0, $a0
mtc0 $a0, c0_entrylo0
mfc0 $a0, c0_entrylo1
and $a0, $v0, $a0
mtc0 $a0, c0_entrylo1
move $v0, $zero # V_addr was purged from TLB
tlbwi # write invalid mappings to TLB
pu_miss: jr $ra
nop
.end TLB_purge
##---------------------------------------------------------------
#================================================================
# delays processing by approx 4*$a0 processor cycles
.text
.set noreorder
......@@ -205,7 +432,7 @@ cmips_delay:
#----------------------------------------------------------------
#----------------------------------------------------------------
#================================================================
# print a message from within "the kernel"
# void cmips_kmsg( $k1 )
# this function preserves registers other than k0,k1
......@@ -219,10 +446,10 @@ cmips_delay:
.align 2
.set noreorder
.set noat
.global cmips_kmsg
.ent cmips_kmsg
.equ stdout,(x_IO_BASE_ADDR + 1*x_IO_ADDR_RANGE);
.global cmips_kmsg
.ent cmips_kmsg
cmips_kmsg:
lui $k0, %hi(_kmsg_saves)
ori $k0, $k0, %lo(_kmsg_saves)
......@@ -257,13 +484,18 @@ k_for: lbu $a0, 0($a1)
.data
.align 2
_kmsg_interr: .asciiz "\n\tinterrupt\n\n"
_kmsg_excep: .asciiz "\n\texception\n\n"
_kmsg_interr: .asciiz "\n\t00 - interrupt\n\n"
_kmsg_excep: .asciiz "\n\t01 - exception\n\n"
_kmsg_prot_viol: .asciiz "\n\t02 - protection violation\n\n"
_kmsg_seg_fault: .asciiz "\n\t03 - segmentation fault\n\n"
_kmsg_sec_mem: .asciiz "\n\t04 - in secondary memory\n\n"
.global _kmsg_list
.data
.align 2
_kmsg_list: .word _kmsg_interr,_kmsg_excep
.align 2
_kmsg_list:
.word _kmsg_interr,_kmsg_excep, _kmsg_prot_viol, _kmsg_seg_fault
.word _kmsg_sec_mem
#----------------------------------------------------------------
......@@ -155,12 +155,13 @@ _excp_0000:
.set noat
mfc0 $k1, c0_context
lw $k0, 0($k1) # k0 <- TP[context.lo]
lw $k1, 8($k1) # k1 <- TP[context.hi]
mtc0 $k0, c0_entrylo0 # EntryLo0 <- k0 = even element
mtc0 $k1, c0_entrylo1 # EntryLo1 <- k1 = odd element
lw $k0, 0($k1) # k0 <- TP[context.lo]
lw $k1, 8($k1) # k1 <- TP[context.hi]
mtc0 $k0, c0_entrylo0 # EntryLo0 <- k0 = even element
mtc0 $k1, c0_entrylo1 # EntryLo1 <- k1 = odd element
ehb
tlbwr # update TLB
tlbwr # update TLB
mfc0 $zero, c0_cause # clear excCode in Cause
eret
.end _excp_0000
......@@ -201,6 +202,9 @@ _excp_0100:
.text
.set noreorder
.set noat
.global _excp_saves, _excp_0180ret
.global handle_Mod, handle_TLBL, handle_TLBS
.org x_EXCEPTION_0180,0 # exception vector_180
.ent _excp_0180
_excp_0180:
......@@ -223,10 +227,10 @@ excp_tbl: # see Table 8-25, pg 95,96
wait 0x02 # interrupt, should never get here, abort simulation
nop
j h_Mod # 1
j handle_Mod # 1
nop
j h_TLBL # 2
j handle_TLBL # 2
nop
j h_TLBS # 3
......@@ -266,8 +270,6 @@ excp_tbl: # see Table 8-25, pg 95,96
nop
h_Mod:
h_TLBL:
h_TLBS:
h_syscall:
h_breakpoint:
......@@ -275,7 +277,7 @@ h_RI:
h_CpU:
h_Ov:
excp_0180ret:
_excp_0180ret:
lui $k1, %hi(_excp_saves) # Read previous contents of STATUS
ori $k1, $k1, %lo(_excp_saves)
lw $k0, 1*4($k1)
......@@ -390,75 +392,103 @@ _excp_BFC0:
##---------------------------------------------------------------
##
#================================================================
# modify the page table:
# void PT_update(void *V_addr, int component, int new_value)
# component is in {0=entrylo0, 1-int0, 2=entrylo1, 3=int1}
.text
.global PT_update
.set noreorder
.ent PT_update
PT_update:
srl $a0, $a0, 9 # (_PT + (V_addr >>13)*16)
la $v0, PTbase
add $a0, $v0, $a0
andi $a1, $a1, 0x0003 # make sure component is in range
sll $a1, $a1, 2 # component * 4
add $a0, $a0, $a1 # (_PT + (V_addr >>13)*16).component
jr $ra
sw $a2, 0($a0) # write to PT[V_addr].component
.end PT_update
##---------------------------------------------------------------
##===============================================================
## Page Table
##
## See EntryLo, pg 63
##
## intLo0 and intLo1 are:
## nil_31..6 Modified_5 Used_4 Writable_3 eXecutable_2 Status_1,0
## Status: 00=unmapped, 01=mapped, 10=in_secondary_storage, 11=undef
##
.section .PT,"aw",@progbits
.global _PT
## ( ( (x_INST_BASE_ADDR + n*4096) >>12 )<<6 ) || 0b000011 d,v,g
_PT:
##
## ROM mappings
## ROM mappings, intLo{01} = U=M=W=0, X=1, S=10 (or 00) = 6 (or 4)
##
.org (_PT + (x_INST_BASE_ADDR >>13)*16)
# PT[0], ROM
.word ( (x_INST_BASE_ADDR + 0*4096) >>6) | 0b000011
.word 0
PTbase: .word ( (x_INST_BASE_ADDR + 0*4096) >>6) | 0b000011
.word 0x00000006
.word ( (x_INST_BASE_ADDR + 1*4096) >>6) | 0b000011
.word 0
.word 0x00000006
# PT[1]
.word ( (x_INST_BASE_ADDR + 2*4096) >>6) | 0b000011
.word 0
.word 0x00000006
.word ( (x_INST_BASE_ADDR + 3*4096) >>6) | 0b000011
.word 0
.word 0x00000006
# PT[2]
# PT[2] -- not mapped for simulation
.word ( (x_INST_BASE_ADDR + 4*4096) >>6) | 0b000011
.word 0
.word 0x00000004
.word ( (x_INST_BASE_ADDR + 5*4096) >>6) | 0b000011
.word 0
.word 0x00000004
# PT[3]
# PT[3] -- not mapped for simulation
.word ( (x_INST_BASE_ADDR + 6*4096) >>6) | 0b000011
.word 0
.word 0x00000004
.word ( (x_INST_BASE_ADDR + 7*4096) >>6) | 0b000011
.word 0
.word 0x00000004
# PT[4] -- not mapped for simulation
.word ( (x_INST_BASE_ADDR + 8*4096) >>6) | 0b000001
.word 0
.word 0x00000004
.word ( (x_INST_BASE_ADDR + 9*4096) >>6) | 0b000001
.word 0
.word 0x00000004
# PT[5] -- not mapped for simulation
.word ( (x_INST_BASE_ADDR + 10*4096) >>6) | 0b000001
.word 0
.word 0x00000004
.word ( (x_INST_BASE_ADDR + 11*4096) >>6) | 0b000001
.word 0
.word 0x00000004
# PT[6] -- not mapped for simulation
.word ( (x_INST_BASE_ADDR + 12*4096) >>6) | 0b000001
.word 0
.word 0x00000004
.word ( (x_INST_BASE_ADDR + 13*4096) >>6) | 0b000001
.word 0
.word 0x00000004
# PT[7] -- not mapped for simulation
.word ( (x_INST_BASE_ADDR + 14*4096) >>6) | 0b000001
.word 0
.word 0x00000004
.word ( (x_INST_BASE_ADDR + 15*4096) >>6) | 0b000001
.word 0
.word 0x00000004
## all remaining ROM entries are invalid
## all remaining ROM entries are invalid and unmapped
##
## RAM mappings
## RAM mappings, intLo{01} = U=M=0, W=1, X=0, S=10 (or 00) = a (or 8)
##
.org (_PT + (x_DATA_BASE_ADDR >>13)*16)
......@@ -466,54 +496,54 @@ _PT:
# PT[ram+0], RAM
.word ( (x_DATA_BASE_ADDR + 0*4096) >>6) | 0b000111
.word 0
.word 0x0000000a
.word ( (x_DATA_BASE_ADDR + 1*4096) >>6) | 0b000111
.word 0
.word 0x0000000a
# PT[ram+1]
.word ( (x_DATA_BASE_ADDR + 2*4096) >>6) | 0b000111
.word 0
.word 0x0000000a
.word ( (x_DATA_BASE_ADDR + 3*4096) >>6) | 0b000111
.word 0
.word 0x0000000a
# PT[ram+2]
.word ( (x_DATA_BASE_ADDR + 4*4096) >>6) | 0b000111
.word 0
.word 0x0000000a
.word ( (x_DATA_BASE_ADDR + 5*4096) >>6) | 0b000111
.word 0
.word 0x0000000a
# PT[ram+3]
.word ( (x_DATA_BASE_ADDR + 6*4096) >>6) | 0b000111
.word 0
.word 0x0000000a
.word ( (x_DATA_BASE_ADDR + 7*4096) >>6) | 0b000111
.word 0
.word 0x0000000a
# PT[ram+4]
.word ( (x_DATA_BASE_ADDR + 8*4096) >>6) | 0b000111
.word 0
.word 0x0000000a
.word ( (x_DATA_BASE_ADDR + 9*4096) >>6) | 0b000111
.word 0
.word 0x0000000a
# PT[ram+5]
.word ( (x_DATA_BASE_ADDR + 10*4096) >>6) | 0b000111
.word 0
.word 0x0000000a
.word ( (x_DATA_BASE_ADDR + 11*4096) >>6) | 0b000111
.word 0
.word 0x0000000a
# PT[ram+6]
.word ( (x_DATA_BASE_ADDR + 12*4096) >>6) | 0b000111
.word 0
.word 0x0000000a
.word ( (x_DATA_BASE_ADDR + 13*4096) >>6) | 0b000111
.word 0
.word 0x0000000a
# PT[ram+7]
.word ( (x_DATA_BASE_ADDR + 14*4096) >>6) | 0b000111
.word 0
.word 0x0000000a
.word ( (x_DATA_BASE_ADDR + 15*4096) >>6) | 0b000111
.word 0
.word 0x0000000a
## all remaining RAM entries are invalid
## all remaining RAM entries are invalid and unmapped
_endPT:
......@@ -15,8 +15,24 @@
// With this much memory, simulations run slowly. No free lunch.
//-----------------------------------------------------------------------
// decide on the tests to run
#define WALK_THE_PT 0
#define TLB_MODIFIED 1
#define DOUBLE_FAULT 0
// these will abort the simulation when the fault is detected/handled
#define PROT_VIOL 0
#define SEG_FAULT 0
//-----------------------------------------------------------------------
#include "cMIPS.h"
extern void PT_update(void *V_addr, int component, int new_value);
extern int TLB_purge(void *V_addr);
static void print_str(char *);
#define FALSE (0==1)
#define TRUE !FALSE
......@@ -25,11 +41,19 @@
#define NUM_RAM_PAGES ( (x_DATA_MEM_SZ / 2) / 4096 )
void main(void) {
int i;
int i, rsp, new_value;
int *walker;
// write to the middle of all pages
#if WALK_THE_PT
//-----------------------------------------------------------------
// write to the middle of all datapages
// this will cause some TLB refill exceptions, which should be
// handled smoothly by the handler at excp_0000 (include/start/s)
//-----------------------------------------------------------------
walker = (int *)(x_DATA_BASE_ADDR + 1024);
for( i = 0 ; i < NUM_RAM_PAGES; i++){
......@@ -44,11 +68,180 @@ void main(void) {
print( *walker );
walker = (int *)((int)walker + 4096);
}
to_stdout('\n');
to_stdout('o');
to_stdout('k');
to_stdout('\n');
print_str("\twalked\n");
#endif
#define PG_NUM 10
#if TLB_MODIFIED
//-------------------------------------------------------------------
// let's change a mapping to cause a TLB-Modified exception
//
// in fact, there will be TWO exceptions:
// (1) a TLB-Refill will copu the write-protected mapping from the PT
// and retry the instruction;
// (2) when the sw is retried, it will cause a TLB-Modified
// exception, which checks PT's protection bits, then fixes the
// dirty bit in the TLB, and retries again, succeeding this time.
//-------------------------------------------------------------------
// ( ( (x_DATA_BASE_ADDR + n*4096) >>12 )<<6 ) || 0b000111 d,v,g
walker = (int *)(x_DATA_BASE_ADDR + PG_NUM*4096);
// first, remove V_addr from the TLB, to ensure the PT is searched
if ( TLB_purge((void *)walker) == 0 ) {
print_str("\n\tTLB entry purged\n");
} else {
print_str("\n\tTLB miss\n");
}
new_value = ( ((x_DATA_BASE_ADDR + PG_NUM*4096)>>12) <<6) | 0b000011; // d=0
PT_update( (int *)walker, 0, new_value);
new_value = 0x00000009; // writable, mapped
PT_update( (int *)walker, 1, new_value);
*walker = 0x99; // cause a TLBrefill, then a TLBmod
if ( *walker == 0x99 ) { // this load is optimized away by gcc
print( *walker );
print_str("\tMod ok\n");
} else {
print_str("\tMod err\n");
}
#endif
#if DOUBLE_FAULT
//--------------------------------------------------------------------
// let's remove a PT's mapping from the TLB and cause a double-fault:
//
// (1) on the 1st reference, TLB-refill does not find a mapping for
// the PT on the TLB; this causes a TLBL (load) exception;
// (2) routine handle_TLBL writes a new mapping on the PT, refills
// the TLB, then retries the reference, which succeeds this time.
//--------------------------------------------------------------------
// this is the base of the page table
walker = (int *)(x_DATA_BASE_ADDR + (x_DATA_MEM_SZ/2));
// remove the TLB entry which points to the PT
if ( TLB_purge((void *)walker) == 0 ) {
print_str("\n\tPT purged from TLB\n");
} else {
print_str("\n\twtf?\n");
}
// now reference a mapped page, to cause the double fault
walker = (int *)(x_DATA_BASE_ADDR + PG_NUM*4096 + 1024);
*walker = 0x77; // cause a TLBrefill then a TLBload
if ( *walker == 0x77 ) { // this load is optimized away by gcc
print( *walker );
print_str("\tdouble ok\n");
} else {
print_str("\tdouble err\n");
}
#endif
#if PROT_VIOL
//----------------------------------------------------------------------
// let's cause a protection violation -- write to a write-protected page
// this will abort the simulation
//----------------------------------------------------------------------
walker = (int *)(x_DATA_BASE_ADDR + PG_NUM*4096);
// first, remove V_addr from the TLB, to ensure the PT is searched
if ( TLB_purge((void *)walker) == 0 ) {
print_str("\n\tpurged\n");
} else {
print_str("\t\tTLB miss\n");
}
// change a PT element so it is data, NON-writable, page is mapped in PT
new_value = ( ((x_DATA_BASE_ADDR + PG_NUM*4096)>>12) <<6) | 0b000011; // d=0
PT_update( (int *)walker, 0, new_value);
new_value = 0x00000001; // NOT-writable, mapped
PT_update( (int *)walker, 1, new_value);
*walker = 0x88;
// will never get here -- protection violation on the store
if ( *walker == 0x88 ) { // this load is optimized away by gcc
print( *walker );
print_str("\tprot viol not ok\n");
} else {
print_str("\tprot viol err\n");
}
#endif
#if SEG_FAULT
//-----------------------------------------------------------------
// let's cause a segmentation fault -- reference to page not mapped
// this will abort the simulation
//-----------------------------------------------------------------
#define PG_UNMAPPED 20