diff --git a/src/main/scala/vexriscv/demo/GenSecure.scala b/src/main/scala/vexriscv/demo/GenSecure.scala new file mode 100644 index 0000000..2835b44 --- /dev/null +++ b/src/main/scala/vexriscv/demo/GenSecure.scala @@ -0,0 +1,86 @@ +package vexriscv.demo + +import vexriscv.plugin._ +import vexriscv.ip.{DataCacheConfig, InstructionCacheConfig} +import vexriscv.{plugin, VexRiscv, VexRiscvConfig} +import spinal.core._ + +object GenSecure extends App { + def cpu() = new VexRiscv( + config = VexRiscvConfig( + plugins = List( + new IBusCachedPlugin( + resetVector = 0x80000000l, + prediction = STATIC, + config = InstructionCacheConfig( + cacheSize = 4096, + bytePerLine = 32, + wayCount = 1, + addressWidth = 32, + cpuDataWidth = 32, + memDataWidth = 32, + catchIllegalAccess = true, + catchAccessFault = true, + asyncTagMemory = false, + twoCycleRam = true, + twoCycleCache = true + ) + ), + new DBusCachedPlugin( + config = new DataCacheConfig( + cacheSize = 4096, + bytePerLine = 32, + wayCount = 1, + addressWidth = 32, + cpuDataWidth = 32, + memDataWidth = 32, + catchAccessError = true, + catchIllegal = true, + catchUnaligned = true + ) + ), + new PmpPlugin( + regions = 16, + ioRange = _(31 downto 28) === 0xf + ), + new DecoderSimplePlugin( + catchIllegalInstruction = true + ), + new RegFilePlugin( + regFileReadyKind = plugin.SYNC, + zeroBoot = false + ), + new IntAluPlugin, + new SrcPlugin( + separatedAddSub = false, + executeInsertion = true + ), + new FullBarrelShifterPlugin, + new HazardSimplePlugin( + bypassExecute = true, + bypassMemory = true, + bypassWriteBack = true, + bypassWriteBackBuffer = true, + pessimisticUseSrc = false, + pessimisticWriteRegFile = false, + pessimisticAddressMatch = false + ), + new MulDivIterativePlugin( + genMul = true, + genDiv = true, + mulUnrollFactor = 1, + divUnrollFactor = 1 + ), + new CsrPlugin(CsrPluginConfig.secure(0x00000020l)), + new DebugPlugin(ClockDomain.current.clone(reset = Bool().setName("debugReset"))), + new BranchPlugin( + earlyBranch = false, + catchAddressMisaligned = true + ), + new YamlPlugin("cpu0.yaml") + ) + ) + ) + + SpinalVerilog(cpu()) +} diff --git a/src/main/scala/vexriscv/plugin/CsrPlugin.scala b/src/main/scala/vexriscv/plugin/CsrPlugin.scala index 545c5fe..315d3e5 100644 --- a/src/main/scala/vexriscv/plugin/CsrPlugin.scala +++ b/src/main/scala/vexriscv/plugin/CsrPlugin.scala @@ -264,6 +264,31 @@ object CsrPluginConfig{ uinstretAccess = CsrAccess.NONE ) + def secure(mtvecInit : BigInt) = CsrPluginConfig( + catchIllegalAccess = true, + mvendorid = 1, + marchid = 2, + mimpid = 3, + mhartid = 0, + misaExtensionsInit = 0x101064, // RV32GCFMU + misaAccess = CsrAccess.READ_WRITE, + mtvecAccess = CsrAccess.READ_WRITE, + mtvecInit = mtvecInit, + mepcAccess = CsrAccess.READ_WRITE, + mscratchGen = true, + mcauseAccess = CsrAccess.READ_WRITE, + mbadaddrAccess = CsrAccess.READ_WRITE, + mcycleAccess = CsrAccess.READ_WRITE, + minstretAccess = CsrAccess.READ_WRITE, + ucycleAccess = CsrAccess.READ_ONLY, + uinstretAccess = CsrAccess.READ_ONLY, + wfiGenAsWait = true, + ecallGen = true, + userGen = true, + medelegAccess = CsrAccess.READ_WRITE, + midelegAccess = CsrAccess.READ_WRITE + ) + } case class CsrWrite(that : Data, bitOffset : Int) case class CsrRead(that : Data , bitOffset : Int) diff --git a/src/main/scala/vexriscv/plugin/PmpPlugin.scala b/src/main/scala/vexriscv/plugin/PmpPlugin.scala new file mode 100644 index 0000000..f232ee4 --- /dev/null +++ b/src/main/scala/vexriscv/plugin/PmpPlugin.scala @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2020 Samuel Lindemer + * + * SPDX-License-Identifier: MIT + */ + +package vexriscv.plugin + +import vexriscv.{VexRiscv, _} +import spinal.core._ +import spinal.lib._ +import scala.collection.mutable.ArrayBuffer + +/* Each 32-bit pmpcfg# register contains four 8-bit configuration sections. + * These section numbers contain flags which apply to regions defined by the + * corresponding pmpaddr# register. + * + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | pmp3cfg | pmp2cfg | pmp1cfg | pmp0cfg | pmpcfg0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | pmp7cfg | pmp6cfg | pmp5cfg | pmp4cfg | pmpcfg2 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 7 6 5 4 3 2 1 0 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | L | 0 | A | X | W | R | pmp#cfg + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + * L: locks configuration until system reset (including M-mode) + * 0: hardwired to zero + * A: 0 = OFF (null region / disabled) + * 1 = TOR (top of range) + * 2 = NA4 (naturally aligned four-byte region) + * 3 = NAPOT (naturally aligned power-of-two region, > 7 bytes) + * X: execute + * W: write + * R: read + * + * TOR: Each 32-bit pmpaddr# register defines the upper bound of the pmp region + * right-shifted by two bits. The lower bound of the region is the previous + * pmpaddr# register. In the case of pmpaddr0, the lower bound is address 0x0. + * + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | address[33:2] | pmpaddr# + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * NAPOT: Each 32-bit pmpaddr# register defines the region address and the size + * of the pmp region. The number of concurrent 1s begging at the LSB indicates + * the size of the region as a power of two (e.g. 0x...0 = 8-byte, 0x...1 = + * 16-byte, 0x...11 = 32-byte, etc.). + * + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | address[33:2] |0|1|1|1|1| pmpaddr# + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * NA4: This is essentially an edge case of NAPOT where the entire pmpaddr# + * register defines a 4-byte wide region. + */ + +case class PmpRegister(previous : PmpRegister) extends Area { + + def OFF = 0 + def TOR = 1 + def NA4 = 2 + def NAPOT = 3 + + // Software-accessible CSR interface + val csr = new Area { + val r, w, x = Reg(Bool) + val l = RegInit(False) + val a = Reg(UInt(2 bits)) init(0) + val addr = Reg(UInt(32 bits)) + } + + // Active region bounds and permissions (internal) + val region = new Area { + val r, w, x = Reg(Bool) + val l, valid = RegInit(False) + val start, end = Reg(UInt(32 bits)) + } + + when(~region.l) { + region.r := csr.r + region.w := csr.w + region.x := csr.x + region.l := csr.l + + val shifted = csr.addr |<< 2 + region.valid := True + + switch(csr.a) { + + is(TOR) { + if (previous == null) { + region.start := 0 + } else { + region.start := previous.region.end + } + if (csr.l == True) { + previous.region.l := True + } + region.end := shifted + } + + is(NA4) { + region.start := shifted + region.end := shifted + 4 + } + + is(NAPOT) { + val mask = csr.addr & ~(csr.addr + 1) + val masked = (csr.addr & ~mask) |<< 2 + region.start := masked + region.end := masked + ((mask + 1) |<< 3) + } + + default { + region.end := shifted + region.valid := False + } + + } + } +} + +case class ProtectedMemoryTranslatorPort(bus : MemoryTranslatorBus) + +class PmpPlugin(regions : Int, ioRange : UInt => Bool) extends Plugin[VexRiscv] with MemoryTranslator { + + // Each pmpcfg# CSR configures four regions. + assert((regions % 4) == 0) + + val pmps = ArrayBuffer[PmpRegister]() + val portsInfo = ArrayBuffer[ProtectedMemoryTranslatorPort]() + + override def newTranslationPort(priority : Int, args : Any): MemoryTranslatorBus = { + val port = ProtectedMemoryTranslatorPort(MemoryTranslatorBus()) + portsInfo += port + port.bus + } + + override def build(pipeline: VexRiscv): Unit = { + import pipeline.config._ + import pipeline._ + import Riscv._ + + val csrService = pipeline.service(classOf[CsrInterface]) + val privilegeService = pipeline.service(classOf[PrivilegeService]) + + val core = pipeline plug new Area { + + // Instantiate pmpaddr0 ... pmpaddr# CSRs. + for (i <- 0 until regions) { + if (i == 0) { + pmps += PmpRegister(null) + } else { + pmps += PmpRegister(pmps.last) + } + csrService.rw(0x3b0 + i, pmps(i).csr.addr) + } + + // Instantiate pmpcfg0 ... pmpcfg# CSRs. + for (i <- 0 until (regions / 4)) { + csrService.rw(0x3a0 + i, + 31 -> pmps((i * 4) + 3).csr.l, 23 -> pmps((i * 4) + 2).csr.l, + 15 -> pmps((i * 4) + 1).csr.l, 7 -> pmps((i * 4) ).csr.l, + 27 -> pmps((i * 4) + 3).csr.a, 26 -> pmps((i * 4) + 3).csr.x, + 25 -> pmps((i * 4) + 3).csr.w, 24 -> pmps((i * 4) + 3).csr.r, + 19 -> pmps((i * 4) + 2).csr.a, 18 -> pmps((i * 4) + 2).csr.x, + 17 -> pmps((i * 4) + 2).csr.w, 16 -> pmps((i * 4) + 2).csr.r, + 11 -> pmps((i * 4) + 1).csr.a, 10 -> pmps((i * 4) + 1).csr.x, + 9 -> pmps((i * 4) + 1).csr.w, 8 -> pmps((i * 4) + 1).csr.r, + 3 -> pmps((i * 4) ).csr.a, 2 -> pmps((i * 4) ).csr.x, + 1 -> pmps((i * 4) ).csr.w, 0 -> pmps((i * 4) ).csr.r + ) + } + + // Connect memory ports to PMP logic. + val ports = for ((port, portId) <- portsInfo.zipWithIndex) yield new Area { + + val address = port.bus.cmd.virtualAddress + port.bus.rsp.physicalAddress := address + + // Only the first matching PMP region applies. + val hits = pmps.map(pmp => pmp.region.valid && + pmp.region.start <= address && + pmp.region.end > address && + (pmp.region.l || ~privilegeService.isMachine())) + + // M-mode has full access by default, others have none. + when(CountOne(hits) === 0) { + port.bus.rsp.allowRead := privilegeService.isMachine() + port.bus.rsp.allowWrite := privilegeService.isMachine() + port.bus.rsp.allowExecute := privilegeService.isMachine() + } otherwise { + port.bus.rsp.allowRead := MuxOH(OHMasking.first(hits), pmps.map(_.region.r)) + port.bus.rsp.allowWrite := MuxOH(OHMasking.first(hits), pmps.map(_.region.w)) + port.bus.rsp.allowExecute := MuxOH(OHMasking.first(hits), pmps.map(_.region.x)) + } + + port.bus.rsp.isIoAccess := ioRange(port.bus.rsp.physicalAddress) + port.bus.rsp.exception := False + port.bus.rsp.refilling := False + port.bus.busy := False + + } + } + } +} + diff --git a/src/test/cpp/raw/pmp/build/pmp.asm b/src/test/cpp/raw/pmp/build/pmp.asm new file mode 100644 index 0000000..294e964 --- /dev/null +++ b/src/test/cpp/raw/pmp/build/pmp.asm @@ -0,0 +1,192 @@ + +build/pmp.elf: file format elf32-littleriscv + + +Disassembly of section .crt_section: + +80000000 <_start>: +80000000: 00000097 auipc ra,0x0 +80000004: 01008093 addi ra,ra,16 # 80000010 +80000008: 30509073 csrw mtvec,ra +8000000c: 00c0006f j 80000018 + +80000010 : +80000010: 341f1073 csrw mepc,t5 +80000014: 30200073 mret + +80000018 : +80000018: 00000e13 li t3,0 +8000001c: 00000f17 auipc t5,0x0 +80000020: 250f0f13 addi t5,t5,592 # 8000026c +80000024: 800000b7 lui ra,0x80000 +80000028: 80008237 lui tp,0x80008 +8000002c: deadc137 lui sp,0xdeadc +80000030: eef10113 addi sp,sp,-273 # deadbeef +80000034: 0020a023 sw sp,0(ra) # 80000000 +80000038: 00222023 sw sp,0(tp) # 80008000 +8000003c: 0000a183 lw gp,0(ra) +80000040: 22311663 bne sp,gp,8000026c +80000044: 00022183 lw gp,0(tp) # 0 <_start-0x80000000> +80000048: 22311263 bne sp,gp,8000026c +8000004c: 071202b7 lui t0,0x7120 +80000050: 3a029073 csrw pmpcfg0,t0 +80000054: 191f02b7 lui t0,0x191f0 +80000058: 30428293 addi t0,t0,772 # 191f0304 <_start-0x66e0fcfc> +8000005c: 3a129073 csrw pmpcfg1,t0 +80000060: 000f02b7 lui t0,0xf0 +80000064: 50628293 addi t0,t0,1286 # f0506 <_start-0x7ff0fafa> +80000068: 3a229073 csrw pmpcfg2,t0 +8000006c: 0f1e22b7 lui t0,0xf1e2 +80000070: 90028293 addi t0,t0,-1792 # f1e1900 <_start-0x70e1e700> +80000074: 3a329073 csrw pmpcfg3,t0 +80000078: 200002b7 lui t0,0x20000 +8000007c: 3b029073 csrw pmpaddr0,t0 +80000080: fff00293 li t0,-1 +80000084: 3b129073 csrw pmpaddr1,t0 +80000088: 200022b7 lui t0,0x20002 +8000008c: 3b229073 csrw pmpaddr2,t0 +80000090: 200042b7 lui t0,0x20004 +80000094: fff28293 addi t0,t0,-1 # 20003fff <_start-0x5fffc001> +80000098: 3b329073 csrw pmpaddr3,t0 +8000009c: 200042b7 lui t0,0x20004 +800000a0: fff28293 addi t0,t0,-1 # 20003fff <_start-0x5fffc001> +800000a4: 3b429073 csrw pmpaddr4,t0 +800000a8: 200042b7 lui t0,0x20004 +800000ac: fff28293 addi t0,t0,-1 # 20003fff <_start-0x5fffc001> +800000b0: 3b529073 csrw pmpaddr5,t0 +800000b4: 200022b7 lui t0,0x20002 +800000b8: fff28293 addi t0,t0,-1 # 20001fff <_start-0x5fffe001> +800000bc: 3b629073 csrw pmpaddr6,t0 +800000c0: 200062b7 lui t0,0x20006 +800000c4: fff28293 addi t0,t0,-1 # 20005fff <_start-0x5fffa001> +800000c8: 3b729073 csrw pmpaddr7,t0 +800000cc: 2000c2b7 lui t0,0x2000c +800000d0: 3b829073 csrw pmpaddr8,t0 +800000d4: 2000d2b7 lui t0,0x2000d +800000d8: 3b929073 csrw pmpaddr9,t0 +800000dc: fff00293 li t0,-1 +800000e0: 3ba29073 csrw pmpaddr10,t0 +800000e4: 00000293 li t0,0 +800000e8: 3bb29073 csrw pmpaddr11,t0 +800000ec: 00000293 li t0,0 +800000f0: 3bc29073 csrw pmpaddr12,t0 +800000f4: 00000293 li t0,0 +800000f8: 3bd29073 csrw pmpaddr13,t0 +800000fc: 00000293 li t0,0 +80000100: 3be29073 csrw pmpaddr14,t0 +80000104: 00000293 li t0,0 +80000108: 3bf29073 csrw pmpaddr15,t0 +8000010c: 00c10137 lui sp,0xc10 +80000110: fee10113 addi sp,sp,-18 # c0ffee <_start-0x7f3f0012> +80000114: 0020a023 sw sp,0(ra) +80000118: 00222023 sw sp,0(tp) # 0 <_start-0x80000000> +8000011c: 0000a183 lw gp,0(ra) +80000120: 14311663 bne sp,gp,8000026c +80000124: 00000193 li gp,0 +80000128: 00022183 lw gp,0(tp) # 0 <_start-0x80000000> +8000012c: 14311063 bne sp,gp,8000026c + +80000130 : +80000130: 00100e13 li t3,1 +80000134: 00000f17 auipc t5,0x0 +80000138: 138f0f13 addi t5,t5,312 # 8000026c +8000013c: 079212b7 lui t0,0x7921 +80000140: 80828293 addi t0,t0,-2040 # 7920808 <_start-0x786df7f8> +80000144: 3a029073 csrw pmpcfg0,t0 +80000148: 800080b7 lui ra,0x80008 +8000014c: deadc137 lui sp,0xdeadc +80000150: eef10113 addi sp,sp,-273 # deadbeef +80000154: 0020a023 sw sp,0(ra) # 80008000 +80000158: 00000f17 auipc t5,0x0 +8000015c: 010f0f13 addi t5,t5,16 # 80000168 +80000160: 0000a183 lw gp,0(ra) +80000164: 1080006f j 8000026c + +80000168 : +80000168: 00200e13 li t3,2 +8000016c: 00000f17 auipc t5,0x0 +80000170: 100f0f13 addi t5,t5,256 # 8000026c +80000174: 071202b7 lui t0,0x7120 +80000178: 3a029073 csrw pmpcfg0,t0 +8000017c: 800080b7 lui ra,0x80008 +80000180: deadc137 lui sp,0xdeadc +80000184: eef10113 addi sp,sp,-273 # deadbeef +80000188: 0020a023 sw sp,0(ra) # 80008000 +8000018c: 00000f17 auipc t5,0x0 +80000190: 010f0f13 addi t5,t5,16 # 8000019c +80000194: 0000a183 lw gp,0(ra) +80000198: 0d40006f j 8000026c + +8000019c : +8000019c: 00300e13 li t3,3 +800001a0: 00000f17 auipc t5,0x0 +800001a4: 0ccf0f13 addi t5,t5,204 # 8000026c +800001a8: 00000117 auipc sp,0x0 +800001ac: 01010113 addi sp,sp,16 # 800001b8 +800001b0: 34111073 csrw mepc,sp +800001b4: 30200073 mret + +800001b8 : +800001b8: 00400e13 li t3,4 +800001bc: 00000f17 auipc t5,0x0 +800001c0: 0b0f0f13 addi t5,t5,176 # 8000026c +800001c4: deadc137 lui sp,0xdeadc +800001c8: eef10113 addi sp,sp,-273 # deadbeef +800001cc: 800080b7 lui ra,0x80008 +800001d0: 0020a023 sw sp,0(ra) # 80008000 +800001d4: 00000f17 auipc t5,0x0 +800001d8: 010f0f13 addi t5,t5,16 # 800001e4 +800001dc: 0000a183 lw gp,0(ra) +800001e0: 08c0006f j 8000026c + +800001e4 : +800001e4: 00500e13 li t3,5 +800001e8: deadc137 lui sp,0xdeadc +800001ec: eef10113 addi sp,sp,-273 # deadbeef +800001f0: 800000b7 lui ra,0x80000 +800001f4: 0020a023 sw sp,0(ra) # 80000000 +800001f8: 0000a183 lw gp,0(ra) +800001fc: 06311863 bne sp,gp,8000026c + +80000200 : +80000200: 00600e13 li t3,6 +80000204: 800100b7 lui ra,0x80010 +80000208: 0000a183 lw gp,0(ra) # 80010000 +8000020c: 00000f17 auipc t5,0x0 +80000210: 06cf0f13 addi t5,t5,108 # 80000278 +80000214: 0030a023 sw gp,0(ra) +80000218: 0540006f j 8000026c + +8000021c : +8000021c: 00700e13 li t3,7 +80000220: 00000f17 auipc t5,0x0 +80000224: 04cf0f13 addi t5,t5,76 # 8000026c +80000228: deadc137 lui sp,0xdeadc +8000022c: eef10113 addi sp,sp,-273 # deadbeef +80000230: 800300b7 lui ra,0x80030 +80000234: ff808093 addi ra,ra,-8 # 8002fff8 +80000238: 00222023 sw sp,0(tp) # 0 <_start-0x80000000> +8000023c: 00000f17 auipc t5,0x0 +80000240: fa8f0f13 addi t5,t5,-88 # 800001e4 +80000244: 00022183 lw gp,0(tp) # 0 <_start-0x80000000> +80000248: 0240006f j 8000026c + +8000024c : +8000024c: 00800e13 li t3,8 +80000250: 800400b7 lui ra,0x80040 +80000254: ff808093 addi ra,ra,-8 # 8003fff8 +80000258: 0000a183 lw gp,0(ra) +8000025c: 00000f17 auipc t5,0x0 +80000260: 01cf0f13 addi t5,t5,28 # 80000278 +80000264: 0030a023 sw gp,0(ra) +80000268: 0040006f j 8000026c + +8000026c : +8000026c: f0100137 lui sp,0xf0100 +80000270: f2410113 addi sp,sp,-220 # f00fff24 +80000274: 01c12023 sw t3,0(sp) + +80000278 : +80000278: f0100137 lui sp,0xf0100 +8000027c: f2010113 addi sp,sp,-224 # f00fff20 +80000280: 00012023 sw zero,0(sp) diff --git a/src/test/cpp/raw/pmp/build/pmp.elf b/src/test/cpp/raw/pmp/build/pmp.elf new file mode 100755 index 0000000..75d1010 Binary files /dev/null and b/src/test/cpp/raw/pmp/build/pmp.elf differ diff --git a/src/test/cpp/raw/pmp/build/pmp.hex b/src/test/cpp/raw/pmp/build/pmp.hex new file mode 100644 index 0000000..e3fdeab --- /dev/null +++ b/src/test/cpp/raw/pmp/build/pmp.hex @@ -0,0 +1,44 @@ +:0200000480007A +:100000009700000093800001739050306F00C00093 +:1000100073101F3473002030130E0000170F000000 +:10002000130F0F25B70000803782008037C1ADDE87 +:100030001301F1EE23A020002320220083A1000061 +:10004000631631228321020063123122B7021207A4 +:100050007390023AB7021F19938242307390123A9A +:10006000B7020F00938262507390223AB7221E0F9C +:10007000938202907390323AB70200207390023B51 +:100080009302F0FF7390123BB72200207390223B43 +:10009000B74200209382F2FF7390323BB7420020B8 +:1000A0009382F2FF7390423BB74200209382F2FFAB +:1000B0007390523BB72200209382F2FF7390623B11 +:1000C000B76200209382F2FF7390723BB7C20020A8 +:1000D0007390823BB7D200207390923B9302F0FF63 +:1000E0007390A23B930200007390B23B9302000016 +:1000F0007390C23B930200007390D23B93020000C6 +:100100007390E23B930200007390F23B3701C10011 +:100110001301E1FE23A020002320220083A1000080 +:10012000631631149301000083210200631031141F +:10013000130E1000170F0000130F8F13B712920742 +:10014000938282807390023AB780008037C1ADDE1F +:100150001301F1EE23A02000170F0000130F0F0171 +:1001600083A100006F008010130E2000170F000005 +:10017000130F0F10B70212077390023AB780008076 +:1001800037C1ADDE1301F1EE23A02000170F0000F0 +:10019000130F0F0183A100006F00400D130E3000FC +:1001A000170F0000130FCF0C1701000013010101FE +:1001B0007310113473002030130E4000170F00002D +:1001C000130F0F0B37C1ADDE1301F1EEB7800080C6 +:1001D00023A02000170F0000130F0F0183A10000C0 +:1001E0006F00C008130E500037C1ADDE1301F1EEF1 +:1001F000B700008023A0200083A10000631831060F +:10020000130E6000B700018083A10000170F0000EB +:10021000130FCF0623A030006F004005130E7000AF +:10022000170F0000130FCF0437C1ADDE1301F1EE3D +:10023000B7000380938080FF23202200170F000067 +:10024000130F8FFA832102006F004002130E80000B +:10025000B7000480938080FF83A10000170F000087 +:10026000130FCF0123A030006F004000370110F0C2 +:10027000130141F22320C101370110F0130101F2F3 +:040280002320010036 +:040000058000000077 +:00000001FF diff --git a/src/test/cpp/raw/pmp/build/pmp.map b/src/test/cpp/raw/pmp/build/pmp.map new file mode 100644 index 0000000..1ba7c62 --- /dev/null +++ b/src/test/cpp/raw/pmp/build/pmp.map @@ -0,0 +1,35 @@ + +Memory Configuration + +Name Origin Length Attributes +onChipRam 0x0000000080000000 0x0000000000020000 w !xr +*default* 0x0000000000000000 0xffffffffffffffff + +Linker script and memory map + +LOAD build/src/crt.o +LOAD /opt/riscv/lib/gcc/riscv64-unknown-elf/10.2.0/libgcc.a +START GROUP +LOAD /opt/riscv/lib/gcc/riscv64-unknown-elf/10.2.0/../../../../riscv64-unknown-elf/lib/libc.a +LOAD /opt/riscv/lib/gcc/riscv64-unknown-elf/10.2.0/../../../../riscv64-unknown-elf/lib/libgloss.a +END GROUP +LOAD /opt/riscv/lib/gcc/riscv64-unknown-elf/10.2.0/libgcc.a + +.crt_section 0x0000000080000000 0x284 + 0x0000000080000000 . = ALIGN (0x4) + *crt.o(.text) + .text 0x0000000080000000 0x284 build/src/crt.o + 0x0000000080000000 _start + 0x0000000080000010 trap +OUTPUT(build/pmp.elf elf32-littleriscv) + +.data 0x0000000080000284 0x0 + .data 0x0000000080000284 0x0 build/src/crt.o + +.bss 0x0000000080000284 0x0 + .bss 0x0000000080000284 0x0 build/src/crt.o + +.riscv.attributes + 0x0000000000000000 0x1e + .riscv.attributes + 0x0000000000000000 0x1e build/src/crt.o diff --git a/src/test/cpp/raw/pmp/makefile b/src/test/cpp/raw/pmp/makefile new file mode 100644 index 0000000..0069df4 --- /dev/null +++ b/src/test/cpp/raw/pmp/makefile @@ -0,0 +1,3 @@ +PROJ_NAME=pmp + +include ../common/asm.mk diff --git a/src/test/cpp/raw/pmp/src/crt.S b/src/test/cpp/raw/pmp/src/crt.S new file mode 100644 index 0000000..47d36b7 --- /dev/null +++ b/src/test/cpp/raw/pmp/src/crt.S @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2020 Samuel Lindemer + * + * SPDX-License-Identifier: MIT + */ + +#define TEST_ID x28 +#define TRAP_RA x30 + +#define PMPCFG0 0x07120000 +#define PMPCFG0_ 0x07920808 // locked +#define PMPCFG1 0x191f0304 +#define PMPCFG2 0x000f0506 +#define PMPCFG3 0x0f1e1900 + +#define PMPADDR0 0x20000000 // OFF +#define PMPADDR1 0xffffffff // OFF +#define PMPADDR2 0x20002000 // NA4 W +#define PMPADDR3 0x20003fff // OFF RWX +#define PMPADDR4 0x20003fff // OFF X +#define PMPADDR5 0x20003fff // OFF RW +#define PMPADDR6 0x20001fff // NAPOT RWX +#define PMPADDR7 0x20005fff // NAPOT R +#define PMPADDR8 0x2000c000 // TOR W +#define PMPADDR9 0x2000d000 // TOR R +#define PMPADDR10 0xffffffff // TOR RWX +#define PMPADDR11 0x00000000 // OFF +#define PMPADDR12 0x00000000 // OFF +#define PMPADDR13 0x00000000 // NAPOT R +#define PMPADDR14 0x00000000 // NAPOT WX +#define PMPADDR15 0x00000000 // TOR RWX + +.global _start +_start: + la x1, trap + csrw mtvec, x1 + j test0 + +.global trap +trap: + csrw mepc, TRAP_RA + mret + +// configure PMP, attempt read/write from machine mode +test0: + li TEST_ID, 0 + la TRAP_RA, fail + + li x1, 0x80000000 + li x4, 0x80008000 + li x2, 0xdeadbeef + sw x2, 0x0(x1) + sw x2, 0x0(x4) + lw x3, 0x0(x1) + bne x2, x3, fail + lw x3, 0x0(x4) + bne x2, x3, fail + + li x5, PMPCFG0 + csrw pmpcfg0, x5 + li x5, PMPCFG1 + csrw pmpcfg1, x5 + li x5, PMPCFG2 + csrw pmpcfg2, x5 + li x5, PMPCFG3 + csrw pmpcfg3, x5 + li x5, PMPADDR0 + csrw pmpaddr0, x5 + li x5, PMPADDR1 + csrw pmpaddr1, x5 + li x5, PMPADDR2 + csrw pmpaddr2, x5 + li x5, PMPADDR3 + csrw pmpaddr3, x5 + li x5, PMPADDR4 + csrw pmpaddr4, x5 + li x5, PMPADDR5 + csrw pmpaddr5, x5 + li x5, PMPADDR6 + csrw pmpaddr6, x5 + li x5, PMPADDR7 + csrw pmpaddr7, x5 + li x5, PMPADDR8 + csrw pmpaddr8, x5 + li x5, PMPADDR9 + csrw pmpaddr9, x5 + li x5, PMPADDR10 + csrw pmpaddr10, x5 + li x5, PMPADDR11 + csrw pmpaddr11, x5 + li x5, PMPADDR12 + csrw pmpaddr12, x5 + li x5, PMPADDR13 + csrw pmpaddr13, x5 + li x5, PMPADDR14 + csrw pmpaddr14, x5 + li x5, PMPADDR15 + csrw pmpaddr15, x5 + + li x2, 0xc0ffee + sw x2, 0x0(x1) + sw x2, 0x0(x4) + lw x3, 0x0(x1) + bne x2, x3, fail + li x3, 0x0 + lw x3, 0x0(x4) + bne x2, x3, fail + +// lock region 2, attempt read/write from machine mode +test1: + li TEST_ID, 1 + la TRAP_RA, fail + li x5, PMPCFG0_ + csrw pmpcfg0, x5 // lock region 2 + li x1, 0x80008000 + li x2, 0xdeadbeef + sw x2, 0x0(x1) // should be OK (write 0x80008000) + la TRAP_RA, test2 + lw x3, 0x0(x1) // should fault (read 0x80008000) + j fail + +// "unlock" region 2, attempt read/write from machine mode +test2: + li TEST_ID, 2 + la TRAP_RA, fail + li x5, PMPCFG0 + csrw pmpcfg0, x5 // "unlock" region 2 + li x1, 0x80008000 + li x2, 0xdeadbeef + sw x2, 0x0(x1) // should still be OK (write 0x80008000) + la TRAP_RA, test3 + lw x3, 0x0(x1) // should still fault (read 0x80008000) + j fail + +// jump into user mode +test3: + li TEST_ID, 3 + la TRAP_RA, fail + la x2, test4 + csrw mepc, x2 + mret + +// attempt to read/write region 2 from user mode +test4: + li TEST_ID, 4 + la TRAP_RA, fail + li x2, 0xdeadbeef + li x1, 0x80008000 + sw x2, 0x0(x1) // should be OK (write 0x80008000) + la TRAP_RA, test5 + lw x3, 0x0(x1) // should fault (read 0x80008000) + j fail + +// attempt to read/write other regions from user mode +test5: + li TEST_ID, 5 + li x2, 0xdeadbeef + li x1, 0x80000000 + sw x2, 0x0(x1) + lw x3, 0x0(x1) + bne x2, x3, fail // should be OK (read/write 0x80000000) + +test6: + li TEST_ID, 6 + li x1, 0x80010000 + lw x3, 0x0(x1) // should be OK (read 0x80010000) + la TRAP_RA, pass + sw x3, 0x0(x1) // should fault (write 0x80010000) + j fail + +test7: + li TEST_ID, 7 + la TRAP_RA, fail + li x2, 0xdeadbeef + li x1, 0x8002fff8 + sw x2, 0x0(x4) // should be OK (write 0x8002fff8) + la TRAP_RA, test5 + lw x3, 0x0(x4) // should fault (read 0x8002fff8) + j fail + +test8: + li TEST_ID, 8 + li x1, 0x8003fff8 + lw x3, 0x0(x1) // should be OK (read 0x8003fff8) + la TRAP_RA, pass + sw x3, 0x0(x1) // should fault (write 0x8003fff8) + j fail + +fail: + li x2, 0xf00fff24 + sw TEST_ID, 0(x2) + +pass: + li x2, 0xf00fff20 + sw x0, 0(x2) diff --git a/src/test/cpp/raw/pmp/src/ld b/src/test/cpp/raw/pmp/src/ld new file mode 100644 index 0000000..93d8de8 --- /dev/null +++ b/src/test/cpp/raw/pmp/src/ld @@ -0,0 +1,16 @@ +OUTPUT_ARCH( "riscv" ) + +MEMORY { + onChipRam (W!RX)/*(RX)*/ : ORIGIN = 0x80000000, LENGTH = 128K +} + +SECTIONS +{ + + .crt_section : + { + . = ALIGN(4); + *crt.o(.text) + } > onChipRam + +} diff --git a/src/test/cpp/regression/main.cpp b/src/test/cpp/regression/main.cpp index d983e8e..d5788f5 100644 --- a/src/test/cpp/regression/main.cpp +++ b/src/test/cpp/regression/main.cpp @@ -3870,6 +3870,10 @@ int main(int argc, char **argv, char **env) { redo(REDO,WorkspaceRegression("lrsc").withRiscvRef()->loadHex(string(REGRESSION_PATH) + "../raw/lrsc/build/lrsc.hex")->bootAt(0x00000000u)->run(10e3);); #endif + #ifdef PMP + redo(REDO,WorkspaceRegression("pmp").loadHex(string(REGRESSION_PATH) + "../raw/pmp/build/pmp.hex")->bootAt(0x80000000u)->run(10e3);); + #endif + #ifdef AMO redo(REDO,WorkspaceRegression("amo").withRiscvRef()->loadHex(string(REGRESSION_PATH) + "../raw/amo/build/amo.hex")->bootAt(0x00000000u)->run(10e3);); #endif diff --git a/src/test/cpp/regression/makefile b/src/test/cpp/regression/makefile index 61fe9d1..5f64034 100644 --- a/src/test/cpp/regression/makefile +++ b/src/test/cpp/regression/makefile @@ -15,6 +15,7 @@ CSR_SKIP_TEST?=no EBREAK?=no FENCEI?=no MMU?=yes +PMP?=no SEED?=no LRSC?=no AMO?=no @@ -217,6 +218,10 @@ ifeq ($(MMU),yes) ADDCFLAGS += -CFLAGS -DMMU endif +ifeq ($(PMP),yes) + ADDCFLAGS += -CFLAGS -DPMP +endif + ifeq ($(MUL),yes) ADDCFLAGS += -CFLAGS -DMUL endif diff --git a/src/test/scala/vexriscv/TestIndividualFeatures.scala b/src/test/scala/vexriscv/TestIndividualFeatures.scala index 047c719..75d3405 100644 --- a/src/test/scala/vexriscv/TestIndividualFeatures.scala +++ b/src/test/scala/vexriscv/TestIndividualFeatures.scala @@ -46,8 +46,10 @@ abstract class VexRiscvPosition(name: String) extends ConfigPosition[VexRiscvCo class VexRiscvUniverse extends ConfigUniverse object VexRiscvUniverse{ + val CACHE_ALL = new VexRiscvUniverse val CATCH_ALL = new VexRiscvUniverse val MMU = new VexRiscvUniverse + val PMP = new VexRiscvUniverse val FORCE_MULDIV = new VexRiscvUniverse val SUPERVISOR = new VexRiscvUniverse val NO_WRITEBACK = new VexRiscvUniverse @@ -321,9 +323,10 @@ class IBusDimension(rvcRate : Double) extends VexRiscvDimension("IBus") { override def randomPositionImpl(universes: Seq[ConfigUniverse], r: Random) = { val catchAll = universes.contains(VexRiscvUniverse.CATCH_ALL) + val cacheAll = universes.contains(VexRiscvUniverse.CACHE_ALL) val mmuConfig = if(universes.contains(VexRiscvUniverse.MMU)) MmuPortConfig( portTlbSize = 4) else null - if(r.nextDouble() < 0.5){ + if(r.nextDouble() < 0.5 && !cacheAll){ val latency = r.nextInt(5) + 1 val compressed = r.nextDouble() < rvcRate val injectorStage = r.nextBoolean() || latency == 1 @@ -402,13 +405,14 @@ class DBusDimension extends VexRiscvDimension("DBus") { override def randomPositionImpl(universes: Seq[ConfigUniverse], r: Random) = { val catchAll = universes.contains(VexRiscvUniverse.CATCH_ALL) + val cacheAll = universes.contains(VexRiscvUniverse.CACHE_ALL) val mmuConfig = if(universes.contains(VexRiscvUniverse.MMU)) MmuPortConfig( portTlbSize = 4) else null val noMemory = universes.contains(VexRiscvUniverse.NO_MEMORY) val noWriteBack = universes.contains(VexRiscvUniverse.NO_WRITEBACK) - if(r.nextDouble() < 0.4 || noMemory){ + if((r.nextDouble() < 0.4 || noMemory) && !cacheAll){ val withLrSc = catchAll val earlyInjection = r.nextBoolean() && !universes.contains(VexRiscvUniverse.NO_WRITEBACK) new VexRiscvPosition("Simple" + (if(earlyInjection) "Early" else "Late")) { @@ -468,12 +472,12 @@ class DBusDimension extends VexRiscvDimension("DBus") { } -class MmuDimension extends VexRiscvDimension("DBus") { +class MmuPmpDimension extends VexRiscvDimension("DBus") { override def randomPositionImpl(universes: Seq[ConfigUniverse], r: Random) = { if(universes.contains(VexRiscvUniverse.MMU)) { new VexRiscvPosition("WithMmu") { - override def testParam = "MMU=yes" + override def testParam = "MMU=yes PMP=no" override def applyOn(config: VexRiscvConfig): Unit = { config.plugins += new MmuPlugin( @@ -481,9 +485,20 @@ class MmuDimension extends VexRiscvDimension("DBus") { ) } } + } else if (universes.contains(VexRiscvUniverse.PMP)) { + new VexRiscvPosition("WithPmp") { + override def testParam = "MMU=no PMP=yes" + + override def applyOn(config: VexRiscvConfig): Unit = { + config.plugins += new PmpPlugin( + regions = 16, + ioRange = _ (31 downto 28) === 0xF + ) + } + } } else { - new VexRiscvPosition("NoMmu") { - override def testParam = "MMU=no" + new VexRiscvPosition("NoMemProtect") { + override def testParam = "MMU=no PMP=no" override def applyOn(config: VexRiscvConfig): Unit = { config.plugins += new StaticMemoryTranslatorPlugin( @@ -502,6 +517,7 @@ trait CatchAllPosition class CsrDimension(freertos : String, zephyr : String, linux : String) extends VexRiscvDimension("Csr") { override def randomPositionImpl(universes: Seq[ConfigUniverse], r: Random) = { + val pmp = universes.contains(VexRiscvUniverse.PMP) val catchAll = universes.contains(VexRiscvUniverse.CATCH_ALL) val supervisor = universes.contains(VexRiscvUniverse.SUPERVISOR) if(supervisor){ @@ -509,10 +525,15 @@ class CsrDimension(freertos : String, zephyr : String, linux : String) extends V override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new CsrPlugin(CsrPluginConfig.linuxFull(0x80000020l)) override def testParam = s"FREERTOS=$freertos ZEPHYR=$zephyr LINUX_REGRESSION=$linux SUPERVISOR=yes" } + } else if(pmp){ + new VexRiscvPosition("Secure") with CatchAllPosition{ + override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new CsrPlugin(CsrPluginConfig.secure(0x80000020l)) + override def testParam = s"CSR=yes CSR_SKIP_TEST=yes FREERTOS=$freertos ZEPHYR=$zephyr" + } } else if(catchAll){ new VexRiscvPosition("MachineOs") with CatchAllPosition{ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new CsrPlugin(CsrPluginConfig.all(0x80000020l)) - override def testParam = s"CSR=yes FREERTOS=$freertos ZEPHYR=$zephyr" + override def testParam = s"CSR=yes CSR_SKIP_TEST=yes FREERTOS=$freertos ZEPHYR=$zephyr" } } else if(r.nextDouble() < 0.3){ new VexRiscvPosition("AllNoException") with CatchAllPosition{ @@ -640,6 +661,7 @@ class TestIndividualFeatures extends MultithreadedFunSuite { val rvcRate = sys.env.getOrElse("VEXRISCV_REGRESSION_CONFIG_RVC_RATE", "0.5").toDouble val linuxRate = sys.env.getOrElse("VEXRISCV_REGRESSION_CONFIG_LINUX_RATE", "0.3").toDouble val machineOsRate = sys.env.getOrElse("VEXRISCV_REGRESSION_CONFIG_MACHINE_OS_RATE", "0.5").toDouble + val secureRate = sys.env.getOrElse("VEXRISCV_REGRESSION_CONFIG_SECURE_RATE", "0.5").toDouble val linuxRegression = sys.env.getOrElse("VEXRISCV_REGRESSION_LINUX_REGRESSION", "yes") val coremarkRegression = sys.env.getOrElse("VEXRISCV_REGRESSION_COREMARK", "yes") val zephyrCount = sys.env.getOrElse("VEXRISCV_REGRESSION_ZEPHYR_COUNT", "4") @@ -661,7 +683,7 @@ class TestIndividualFeatures extends MultithreadedFunSuite { new CsrDimension(/*sys.env.getOrElse("VEXRISCV_REGRESSION_FREERTOS_COUNT", "1")*/ "0", zephyrCount, linuxRegression), //Freertos old port software is broken new DecoderDimension, new DebugDimension, - new MmuDimension + new MmuPmpDimension ) var clockCounter = 0l @@ -746,6 +768,13 @@ class TestIndividualFeatures extends MultithreadedFunSuite { if(demwRate < rand.nextDouble()){ universe += VexRiscvUniverse.NO_WRITEBACK } + } else if (secureRate > rand.nextDouble()) { + universe += VexRiscvUniverse.CACHE_ALL + universe += VexRiscvUniverse.CATCH_ALL + universe += VexRiscvUniverse.PMP + if(demwRate < rand.nextDouble()){ + universe += VexRiscvUniverse.NO_WRITEBACK + } } else { if(machineOsRate > rand.nextDouble()) { universe += VexRiscvUniverse.CATCH_ALL @@ -776,4 +805,4 @@ class TestIndividualFeatures extends MultithreadedFunSuite { val clockPerSecond = (clockCounter/time*1e-3).toLong println(s"Duration=${(time/60).toInt}mn clocks=${(clockCounter*1e-6).toLong}M clockPerSecond=${clockPerSecond}K") } -} \ No newline at end of file +}