summaryrefslogtreecommitdiff
path: root/rvcontroller.lua
diff options
context:
space:
mode:
Diffstat (limited to 'rvcontroller.lua')
-rw-r--r--rvcontroller.lua135
1 files changed, 111 insertions, 24 deletions
diff --git a/rvcontroller.lua b/rvcontroller.lua
index 56b800b..8711a51 100644
--- a/rvcontroller.lua
+++ b/rvcontroller.lua
@@ -7,7 +7,7 @@
Emulated system specifications:
-Single RISC-V core, RV32IMACBZicntr_Zicond_Zicsr_Zifencei_Zihintpause_Zilsd_Zimop_Zabha_Zacas_Zalasr_Zawrs_Zcb_Zclsd_Zcmop_Zcmp_Zcmt_Zbkb_Zbkx_Xh3bextm instruction set, switchable endianness (little-endian default)
+Single RISC-V core, RV32IMACBZicfilp_Zicntr_Zicond_Zicsr_Zifencei_Zihintpause_Zilsd_Zimop_Zabha_Zacas_Zalasr_Zawrs_Zcb_Zclsd_Zcmop_Zcmp_Zcmt_Zbkb_Zbkx_Xh3bextm instruction set, switchable endianness (little-endian default)
65536 bytes of RAM (configurable in settings below) starting at base address 0
Intended to be compliant with the following specifications:
@@ -48,6 +48,7 @@ Intended to be compliant with the following specifications:
* "Zawrs" Extension for Wait-on-Reservation-Set instructions, Version 1.01
* Zbkb: Extension for Bit-manipulation for Cryptography, Version 1.0.0
* Zbkx: Extension for Crossbar permutations, Version 1.0.0
+* Zicfilp: Control Flow Integrity - Landing Pad
* Xh3bextm: Hazard3 bit extract multiple
- Note: Defined in the Hazard3 reference manual, available at https://github.com/Wren6991/Hazard3/releases/download/v1.1/hazard3.pdf
* Machine-Level ISA, Version 1.13
@@ -254,6 +255,7 @@ local traptypes = {
}
local fault --non-nil values are for deferred faults that will trap after the instruction finishes
+local fval
local function implodebits(bits,count,signed)
local negative = false
@@ -349,7 +351,8 @@ local function readram(address,bytes,instfetch)
local bigendian = mem.mbe and not instfetch --Instruction fetches must always be little-endian
if address > RAM_SIZE-1 and not (address >= mem.csr[0x801] and address <= mem.csr[0x801] + 17) then
fault = instfetch and 1 or 5
- return instfetch and 0x13 or 0 --0x13 = addi x0,x0,0
+ fval = address
+ return instfetch and 0x13 or 0 --0x13 = addi x0,x0,0 (nop)
end
local out = 0
for i=0,bytes-1 do
@@ -441,6 +444,7 @@ end
local function writeram(address,data,bytes,fake)
if address > RAM_SIZE-1 and not fake and not (address >= mem.csr[0x801] and address <= mem.csr[0x801] + 17) then
fault = 7
+ fval = address
return
end
if mem.reservationset then
@@ -610,12 +614,21 @@ local function readcsr(address)
elseif address == 0x310 then --mstatush
local bits = {}
bits[5] = mem.mbe
+ bits[9] = mem.mpelp
bits[10] = mem.mdt
return implodebits(bits,32)
+ elseif address == 0x747 then --mseccfg
+ local bits = {}
+ bits[10] = mem.mlpe
+ return implodebits(bits,32)
elseif not mem.csr[address] then
digiline_send("monitordisp",string.format("Attempted read\nfrom unknown\nCSR 0x%03X\nPC: %08X",address,mem.registers.pc))
fault = 2
+ fval = readram(mem.registers.pc,4)
return 0
+ elseif address == 0xb00 or address == 0xb02 or address == 0xb80 or address == 0xb82 then
+ --mcycle(h)/minstret(h) - redirect to cycle(h)/instret(h)
+ address = address + 0x100
end
return mem.csr[address]
end
@@ -627,10 +640,12 @@ local function writecsr(address,data)
elseif address == 0xc00 or address == 0xc01 or address == 0xc02 or address == 0xc80 or address == 0xc81 or address == 0xc82 then
--One of the read-only CSRs from Zicntr
fault = 2
+ fval = readram(mem.registers.pc,4)
return
elseif address == 0xf11 or address == 0xf12 or address == 0xf13 or address == 0xf14 or address == 0xf15 then
--Read-only machine information register
fault = 2
+ fval = readram(mem.registers.pc,4)
return
elseif address == 0x300 then
--mstatus
@@ -642,6 +657,7 @@ local function writecsr(address,data)
--mstatush
local bits = explodebits(data,32)
mem.mbe = bits[5]
+ mem.mpelp = bits[9]
mem.mdt = bits[10]
return
elseif address == 0x301 then
@@ -673,16 +689,26 @@ local function writecsr(address,data)
mem.trapmode = data%4
mem.trapbase = data-mem.trapmode
mem.mtvecwritten = true
+ return
+ elseif address == 0xb00 or address == 0xb02 or address == 0xb80 or address == 0xb82 then
+ --mcycle(h)/minstret(h) - redirect to cycle(h)/instret(h)
+ address = address + 0x100
+ elseif address == 0x747 then
+ --mseccfg then
+ local bits = explodebits(data,32)
+ mem.mlpe = bits[10]
+ return
end
if not mem.csr[address] then
digiline_send("monitordisp",string.format("Attempted write\nto unknown\nCSR 0x%03X\nPC: %08X",address,mem.registers.pc))
fault = 2
+ fval = readram(mem.registers.pc,4)
return
end
mem.csr[address] = data
end
-local function trap(reason)
+local function trap(reason,val)
local oldreason = reason
if mem.mdt then
writecsr(0x34b,reason) --mtval2
@@ -690,9 +716,11 @@ local function trap(reason)
end
writecsr(0x342,reason) --mcause
writecsr(0x341,mem.registers.pc) --mepc
- writecsr(0x343,0) --mtval
+ writecsr(0x343,val or 0) --mtval
mem.mpie = mem.mie
mem.mie = false
+ mem.mpelp = mem.mlpe and mem.elp
+ mem.elp = false
if not mem.mdt then
mem.mdt = true
if mem.trapmode == 1 then
@@ -710,9 +738,23 @@ local function trap(reason)
digiline_send("monitordisp","Unexpected trap!")
if not mem.mtvecwritten then
digiline_send("monitordisp",traptypes[oldreason] or "Unknown reason")
+ if oldreason == 18 then
+ local subtypes = {
+ [2] = "Landing pad fault",
+ [3] = "Shadow stack fault",
+ }
+ digiline_send("monitordisp",subtypes[val] or "Unknown cause")
+ end
digiline_send("monitordisp","(no handler present)")
else
digiline_send("monitordisp",traptypes[reason] or "Unknown reason")
+ if reason == 18 then
+ local subtypes = {
+ [2] = "Landing pad fault",
+ [3] = "Shadow stack fault",
+ }
+ digiline_send("monitordisp",subtypes[val] or "Unknown cause")
+ end
end
digiline_send("monitordisp",string.format("PC: %08X",mem.registers.pc))
digiline_send("monitordisp","System halted")
@@ -1166,7 +1208,7 @@ local operations = {
end
end,
ebreak = function()
- return trap(3)
+ return trap(3,0)
end,
csrrw = function(rd,rs1,imm)
setreg(rd,readcsr(imm))
@@ -1957,6 +1999,8 @@ local operations = {
mem.mdt = false
mem.mie = mem.mpie
mem.mpie = true
+ mem.elp = mem.mlpe and mem.mpelp
+ mem.mpelp = false
mem.mpp = 3
checkinterrupts()
return true
@@ -2071,7 +2115,7 @@ local function runinst(instruction)
elseif f3 == 0x7 and f7 == 0x7 then
operations.czeronez(rd,rs1,rs2)
else
- return trap(2)
+ return trap(2,instruction)
end
elseif opcode == 0x13 or opcode == 0x3 or opcode == 0x67 or opcode == 0x73 then
--I-type
@@ -2133,7 +2177,7 @@ local function runinst(instruction)
elseif f3 == 0x5 and imm == 0x8f then
operations.unzip(rd,rs1,imm)
else
- return trap(2)
+ return trap(2,instruction)
end
elseif opcode == 0x3 then
if f3 == 0x0 then
@@ -2149,13 +2193,13 @@ local function runinst(instruction)
elseif f3 == 0x5 then
operations.lhu(rd,rs1,imm)
else
- return trap(2)
+ return trap(2,instruction)
end
elseif opcode == 0x67 then
if f3 == 0x0 then
return operations.jalr(rd,rs1,imm)
else
- return trap(2)
+ return trap(2,instruction)
end
elseif opcode == 0x73 then
if f3 == 0x0 then
@@ -2172,7 +2216,7 @@ local function runinst(instruction)
elseif imm == 0x105 and rd == 0 and rs1 == 0 then
return operations.wfi()
else
- return trap(2)
+ return trap(2,instruction)
end
elseif f3 == 0x1 then
operations.csrrw(rd,rs1,imm)
@@ -2193,7 +2237,7 @@ local function runinst(instruction)
elseif f3 == 0x7 then
operations.csrrci(rd,rs1,imm)
else
- return trap(2)
+ return trap(2,instruction)
end
end
elseif opcode == 0x23 then
@@ -2215,7 +2259,7 @@ local function runinst(instruction)
elseif f3 == 0x3 then
operations.sd(rs1,rs2,imm)
else
- return trap(2)
+ return trap(2,instruction)
end
elseif opcode == 0x63 then
--B-type
@@ -2240,7 +2284,7 @@ local function runinst(instruction)
elseif f3 == 0x7 then
return operations.bgeu(rs1,rs2,imm)
else
- return trap(2)
+ return trap(2,instruction)
end
elseif opcode == 0x37 or opcode == 0x17 then
--U-type
@@ -2254,7 +2298,7 @@ local function runinst(instruction)
elseif opcode == 0x17 then
operations.auipc(rd,imm)
else
- return trap(2)
+ return trap(2,instruction)
end
elseif opcode == 0x6f then
--J-type
@@ -2318,14 +2362,14 @@ local function runinst(instruction)
--The normal sw instruction implementation already meets these requirements
operations.sw(rs1,rs2,0)
else
- return trap(2)
+ return trap(2,instruction)
end
elseif f3 == 3 then
if f5 == 5 then
--amocas.d
operations.amocasd(rd,rs1,rs2)
else
- return trap(2)
+ return trap(2,instruction)
end
elseif f3 == 0 then
if f5 == 1 then
@@ -2367,7 +2411,7 @@ local function runinst(instruction)
--The normal sb instruction implementation already meets these requirements
operations.sb(rs1,rs2,0)
else
- return trap(2)
+ return trap(2,instruction)
end
elseif f3 == 1 then
if f5 == 1 then
@@ -2409,7 +2453,7 @@ local function runinst(instruction)
--The normal sh instruction implementation already meets these requirements
operations.sh(rs1,rs2,0)
else
- return trap(2)
+ return trap(2,instruction)
end
end
elseif opcode == 0x0f then
@@ -2431,10 +2475,10 @@ local function runinst(instruction)
elseif f3 == 4 then
operations.h3bextmi(rd,rs1,rs2,xh3bextmsize)
else
- return trap(2)
+ return trap(2,instruction)
end
else
- trap(2)
+ trap(2,instruction)
end
end
@@ -2555,7 +2599,7 @@ local function runcinst(instruction)
--c.mop
operations.cmop(rd,imm)
else
- return trap(2)
+ return trap(2,instruction%2^16)
end
elseif opcode == 1 and f3 == 0 then
--c.addi (CI)
@@ -2748,10 +2792,34 @@ local function runcinst(instruction)
return true
end
else
- return trap(2)
+ return trap(2,instruction%2^16)
end
end
+local function checklpe(instruction)
+ if not mem.mlpe then
+ return false
+ elseif instruction%4 == 3 then
+ --Uncompressed
+ local opcode = instruction%0x100
+ local bits = explodebits(instruction,32)
+ if opcode == 0x67 and not (bits[12] or bits[13] or bits[14]) then
+ --jalr
+ local rs1 = math.floor(instruction/2^15)%2^5
+ return (rs1 ~= 1 and rs1 ~= 5 and rs1 ~= 7)
+ end
+ else
+ --Compressed
+ local f4 = math.floor(instruction/2^12)
+ if instruction %4 == 2 and (f4 == 8 or f4 == 9) then
+ --c.jr or c.jalr
+ local rs1 = math.floor(instruction/2^7)%2^5
+ return (rs1 ~= 1 and rs1 ~= 5 and rs1 ~= 7)
+ end
+ end
+ return false
+end
+
local function run(limit)
--0xc00 = CYCLE
--0xc80 = CYCLEH
@@ -2778,6 +2846,17 @@ local function run(limit)
end
first = false
local instruction = readram(mem.registers.pc,4,true)
+ if mem.lpe and mem.mlpe then
+ if instruction%2^12 == 0x017 and mem.registers.pc%4 == 0 and (math.floor(instruction/2^12) == mem.lplabel or math.floor(instruction/2^12) == 0) then
+ mem.lpe = false
+ else
+ trap(18,2)
+ end
+ end
+ mem.lpe = mem.mlpe and checklpe(instruction)
+ if mem.lpe then
+ mem.lplabel = math.floor(getreg(7)/2^12)
+ end
if instruction%4 == 3 or not mem.isa.c then
if mem.registers.pc%4 ~= 0 and not mem.isa.c then
local _,stop = trap(0)
@@ -2785,14 +2864,14 @@ local function run(limit)
end
local jumped,stop = runinst(instruction)
if fault then
- jumped,stop = trap(fault)
+ jumped,stop = trap(fault,fval)
end
if not jumped then mem.registers.pc = (mem.registers.pc + 4) % 2^32 end
if stop then break end
else
local jumped,stop = runcinst(instruction%2^16)
if fault then
- jumped,stop = trap(fault)
+ jumped,stop = trap(fault,fval)
end
if not jumped then mem.registers.pc = (mem.registers.pc + 2) % 2^32 end
if stop then break end
@@ -2872,6 +2951,11 @@ local csraliases = {
mip = 0x344,
mtinst = 0x34a,
mtval2 = 0x34b,
+ mcycle = 0xb00,
+ minstret = 0xb02,
+ mcycleh = 0xb80,
+ minstreth = 0xb82,
+ mseccfg = 0x747,
}
if event.type == "program" or event.iid == "reset" then
@@ -2944,6 +3028,9 @@ if event.type == "program" or event.iid == "reset" then
mem.mtimecmphigh = 0
mem.mtimehigh = 0
mem.interruptwaiting = false
+ mem.mlpe = false
+ mem.elp = false
+ mem.mpelp = false
elseif event.type == "on" or event.type == "off" then
local pname = string.lower(event.pin.name)
local pstate = pin[pname]