summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--assembly/landingpad-test/Makefile24
-rw-r--r--assembly/landingpad-test/landingpad-test.S52
-rwxr-xr-xassembly/landingpad-test/landingpad-test.elfbin0 -> 5788 bytes
-rw-r--r--assembly/landingpad-test/landingpad-test.hex20
-rw-r--r--assembly/landingpad-test/landingpad-test.obin0 -> 1992 bytes
l---------assembly/landingpad-test/rvcontroller.ld1
-rwxr-xr-xc/gol/gol.elfbin0 -> 13812 bytes
-rw-r--r--rvcontroller.lua135
8 files changed, 208 insertions, 24 deletions
diff --git a/assembly/landingpad-test/Makefile b/assembly/landingpad-test/Makefile
new file mode 100644
index 0000000..139d2c3
--- /dev/null
+++ b/assembly/landingpad-test/Makefile
@@ -0,0 +1,24 @@
+MARCH ?= rv32imacbzicntr_zicond_zicsr_zifencei_zihintpause_zilsd_zclsd_zabha_zacas_zawrs_zbkb_zbkx_zcb_zcmp_zcmt_zicfilp
+
+.PHONY: all dump load clean
+
+all: landingpad-test.hex
+
+landingpad-test.o: landingpad-test.S
+ riscv32-none-elf-as -I../rvcontroller-libraries -march=${MARCH} -o landingpad-test.o landingpad-test.S
+
+landingpad-test.elf: landingpad-test.o
+ riscv32-none-elf-ld -T rvcontroller.ld --no-warn-rwx-segments -o landingpad-test.elf landingpad-test.o
+
+dump: landingpad-test.elf
+ riscv32-none-elf-objdump -d landingpad-test.elf
+
+landingpad-test.hex: landingpad-test.elf
+ riscv32-none-elf-objcopy -O ihex landingpad-test.elf landingpad-test.hex
+
+load: landingpad-test.hex
+ bash -c "wl-copy < landingpad-test.hex"
+
+clean:
+ rm -f landingpad-test.hex landingpad-test.elf landingpad-test.o
+
diff --git a/assembly/landingpad-test/landingpad-test.S b/assembly/landingpad-test/landingpad-test.S
new file mode 100644
index 0000000..21c1712
--- /dev/null
+++ b/assembly/landingpad-test/landingpad-test.S
@@ -0,0 +1,52 @@
+li t0,0x400
+csrw mseccfg,t0 # Turn on MLPE (enable Zicfilp)
+
+li t0,1
+csrw 0x800,t0 # Enable lightweight mode (will make each pause 1s long)
+
+li a7,4
+la a0,test1msg
+ecall # Display test 1 explanation
+
+pause
+pause
+
+la s0,test1jump
+jalr zero,s0,0 # This jump lands on a landing pad
+
+.word 0 # Invalid instruction just to make sure the jump really happened
+
+test1jump:
+lpad 0
+li a7,4
+la a0,test1done
+ecall # Tell user we got here
+
+pause
+pause
+
+li a7,4
+la a0,test2msg
+ecall # Display test 2 explanation
+
+pause
+pause
+
+la s0,test2jump
+jalr zero,s0,0 # This jump doesn't land on a landing pad
+
+.word 0 # Another invalid instruction to jump over
+
+test2jump:
+# No landing pad! This should crash
+li a7,4
+la a0,test2done
+ecall # Tell user we got here (we shouldn't have!)
+
+li a7,10
+ecall
+
+test1msg: .asciz "Testing OK jump\nThis should work\n\n\n\n"
+test1done: .asciz "OK jump jumped\nThis is expected\n\n\n\n"
+test2msg: .asciz "Testing bad jump\nThis should crash\n\n\n\n"
+test2done: .asciz "Bad jump jumped!\nThis shouldn't\nhappen\n\n\n"
diff --git a/assembly/landingpad-test/landingpad-test.elf b/assembly/landingpad-test/landingpad-test.elf
new file mode 100755
index 0000000..b08c3a0
--- /dev/null
+++ b/assembly/landingpad-test/landingpad-test.elf
Binary files differ
diff --git a/assembly/landingpad-test/landingpad-test.hex b/assembly/landingpad-test/landingpad-test.hex
new file mode 100644
index 0000000..4d8d8c5
--- /dev/null
+++ b/assembly/landingpad-test/landingpad-test.hex
@@ -0,0 +1,20 @@
+:10000000930200407390727485427390028091480D
+:100010001705000013058507730000000F0000019D
+:100020000F0000011704000013040401670004001E
+:10003000000000001700000091481705000013059C
+:100040003507730000000F0000010F000001914808
+:100050001705000013051508730000000F000001CC
+:100060000F000001170400001304040167000400DE
+:1000700000000000914817050000130525087300D3
+:100080000000A9487300000054657374696E67200E
+:100090004F4B206A756D700A546869732073686FDE
+:1000A000756C6420776F726B0A0A0A0A004F4B2046
+:1000B0006A756D70206A756D7065640A546869733D
+:1000C0002069732065787065637465640A0A0A0A9A
+:1000D0000054657374696E6720626164206A756D8F
+:1000E000700A546869732073686F756C642063725A
+:1000F0006173680A0A0A0A00426164206A756D70B9
+:10010000206A756D706564210A546869732073688C
+:100110006F756C646E27740A68617070656E0A0A88
+:020120000A00D3
+:00000001FF
diff --git a/assembly/landingpad-test/landingpad-test.o b/assembly/landingpad-test/landingpad-test.o
new file mode 100644
index 0000000..3534749
--- /dev/null
+++ b/assembly/landingpad-test/landingpad-test.o
Binary files differ
diff --git a/assembly/landingpad-test/rvcontroller.ld b/assembly/landingpad-test/rvcontroller.ld
new file mode 120000
index 0000000..bc01402
--- /dev/null
+++ b/assembly/landingpad-test/rvcontroller.ld
@@ -0,0 +1 @@
+../../rvcontroller.ld \ No newline at end of file
diff --git a/c/gol/gol.elf b/c/gol/gol.elf
new file mode 100755
index 0000000..e7192f5
--- /dev/null
+++ b/c/gol/gol.elf
Binary files differ
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]