resources: Add resources to run riscv full system simulations

Change-Id: Ibbce553b8868ea3a12da04e993007ee4bd922488
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5-resources/+/42241
Reviewed-by: Bobby R. Bruce <bbruce@ucdavis.edu>
Maintainer: Bobby R. Bruce <bbruce@ucdavis.edu>
Tested-by: Bobby R. Bruce <bbruce@ucdavis.edu>
diff --git a/README.md b/README.md
index b58d692..0c7d6e3 100644
--- a/README.md
+++ b/README.md
@@ -402,7 +402,7 @@
 processor included in the GCN_X86 build of gem5.
 
 The example command extracts the kernel's completion signal from the domain
-of the command processor and the GPU's dispatcher. Initially this was a 
+of the command processor and the GPU's dispatcher. Initially this was a
 workaround for the hipDeviceSynchronize bug, now fixed. The method of
 waiting on a signal can be applied to other agent packet commands though.
 
@@ -742,6 +742,17 @@
 
 The instructions to build the boot-tests disk image (`boot-exit`), the Linux binaries, and how to use gem5 run scripts to run boot-tests are available in this [README](src/boot-tests/README.md) file.
 
+# Resource: RISCV Full System Test
+
+This resource refers to a simple setup for a riscv based full system simulation of Linux kernel.
+
+Main components include:
+- a disk image
+- a riscv boot loader with linux kernel as payload and a device tree compiled in
+- gem5 run/config scripts
+
+The instructions to build a riscv disk image, a riscv boot loader (`berkeley bootloader (bbl)`) and how to use gem5 scripts to run riscv Linux full system simulations are available in this [README](src/riscv-fs/README.md) file.
+
 # Resource: Insttest
 
 The Insttests test SPARC instructions.
diff --git a/src/riscv-fs/README.md b/src/riscv-fs/README.md
new file mode 100644
index 0000000..507e45a
--- /dev/null
+++ b/src/riscv-fs/README.md
@@ -0,0 +1,254 @@
+# RISCV Full System
+
+This document provides instructions to create a riscv disk image, a riscv boot loader (`berkeley bootloader (bbl)`) and also points to the associated gem5 scripts to run riscv Linux full system simulations.
+The boot loader `bbl` is compiled with a Linux kernel and a device tree as well.
+
+The used disk image is based on [busybox](https://busybox.net/) and [UCanLinux](https://github.com/UCanLinux/). It is built using the instructions, mostly from [here](https://github.com/UCanLinux/riscv64-sample).
+
+All components are cross compiled on an x86 host using a riscv tool chain.
+
+We assume the following directory structure while following the instructions in this README file:
+
+```
+riscv-fs/
+  |___ gem5/                                   # gem5 source code (to be cloned here)
+  |
+  |___ riscv-disk                              # built disk image will go here
+  |
+  |___ device.dts                              # device tree file to use with bbl
+  |
+  |___ riscv-gnu-toolchain                     # riscv tool chain for cross compilation
+  |
+  |___ riscv64-sample                          # UCanLinux source
+  |       |__linux                             # linux source
+  |       |__busybox                           # busybox source
+  |       |__riscv-pk                          # riscv proxy kernel source (bbl)
+  |       |__RootFS                            # root file system for disk image
+  |
+  |
+  |___ configs-riscv-fs
+  |      |___ system                           # gem5 system config files
+  |      |___ run_riscv.py                     # gem5 run script
+  |
+  |
+  |___ README.md                               # This README file
+```
+
+## RISCV Toolchain
+
+We use `RISC-V GNU Compiler Toolchain`. To build the toolchain, follow the following instructions, assuming you are in the `riscv-fs` directory.
+
+```sh
+# install required libraries
+sudo apt-get install -y autoconf automake autotools-dev curl python3 libmpc-dev libmpfr-dev libgmp-dev gawk build-essential bison flex texinfo gperf libtool patchutils bc zlib1g-dev libexpat-dev
+
+# clone riscv gnu toolchain source
+git clone https://github.com/riscv/riscv-gnu-toolchain
+cd riscv-gnu-toolchain
+
+# change the prefix to your directory
+# of choice for installation of the
+# toolchain
+./configure --prefix=/opt/riscv
+
+# build the toolchain
+make linux -j$(nproc)
+```
+
+Update the `PATH` environment variable so that the following instructions can figure out where to find the riscv toolchain.
+
+```sh
+export PATH=$PATH:/opt/riscv/bin/
+```
+
+***Note:** The above step is necessary and might cause errors while cross compiling different components for riscv if other methods are used to point to the toolchain.
+
+## UCanLinux Source
+
+Clone the `UCanLinux source.`
+
+```sh
+# going back to base riscv-fs directory
+cd ../
+
+git clone https://github.com/UCanLinux/riscv64-sample
+```
+
+This source contains already built bootloader and disk images as well. Though the given disk image might be usable with gem5, the `bbl` (bootloader image) will not work with gem5 and we need to compile `bbl` with an input device tree (`.dts`) file separately. The following sections provide instructions to build both `bbl` and disk images.
+
+## Linux Kernel
+
+Clone the latest LTS Linux kernel (v5.10):
+
+```sh
+cd riscv64-sample/
+git clone --depth 1 --branch v5.10 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
+```
+
+To configure and compile the kernel:
+
+```sh
+cd linux
+
+# copy the kernel config from the riscv64-sample
+# directory (cloned previously)
+
+cp ../kernel.config .config
+
+# configure the kernel and build it
+make ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- menuconfig
+make ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu-  all -j$(nproc)
+```
+
+This should generate a `vmlinux` image in the `linux` directory.
+
+## Bootloader (bbl)
+
+To build the bootloader, clone the RISCV proxy kernel (`pk`) source, which is an application execution environment and contains the bbl source as well.
+
+```sh
+# going back to base riscv64-sample directory
+cd ../
+git clone https://github.com/riscv/riscv-pk.git
+
+cd riscv-pk
+
+mkdir build
+cd build
+
+apt-get install device-tree-compiler
+
+# copy the device tree file from riscv-fs
+cp ../../../device.dts .
+
+../configure --host=riscv64-unknown-linux-gnu --with-payload=../../linux/vmlinux --prefix=/opt/riscv/ --with-dts=device.dts
+make -j$(nproc)
+
+chmod 755 bbl
+
+# optional: strip the bbl binary
+riscv64-unknown-linux-gnu-strip bbl
+```
+
+This will produce a `bbl` bootloader binary with linux kernel in `riscv-pk/build` directory.
+
+## Busy Box
+
+Clone and compile the busybox:
+
+```sh
+# going back to riscv64-sample directory
+cd ../..
+git clone git://busybox.net/busybox.git
+cd busybox
+git checkout 1_30_stable  # checkout the latest stable branch
+make menuconfig
+cp ../sample/busybox.config .config  # optional
+make menuconfig
+make CROSS_COMPILE=riscv64-unknown-linux-gnu- all -j$(nproc)
+make CROSS_COMPILE=riscv64-unknown-linux-gnu- install
+```
+
+## Root File System for Disk Image
+
+Next, we will be setting up a root file system:
+
+```sh
+# going back to riscv64-sample directory
+cd ../..
+
+mkdir RootFS
+cd RootFS
+cp -a ../skeleton/* .
+
+# copy linux tools/binaries from busbybox (created above)
+cp -a ../busybox/_install/* .
+
+# install modules from linux kernel compiled above
+cd ../linux/
+make ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- INSTALL_MOD_PATH=../RootFS modules_install
+
+# install libraries from the toolchain built above
+cd ../RootFS
+cp -a /opt/riscv/sysroot/lib  .
+
+# create empty directories
+mkdir dev home mnt proc sys tmp var
+cd etc/network
+mkdir if-down.d  if-post-down.d  if-pre-up.d  if-up.d
+
+# build m5 util for riscv and move
+# it to the root file system as well
+cd ../../../
+cd gem5/util/m5
+scons -C util/m5 build/riscv/out/m5
+cp build/riscv/out/m5 ../../../RootFS/sbin/
+```
+
+## Disk Image
+
+Create a disk of 512MB size.
+
+```sh
+cd ../../../
+dd if=/dev/zero of=riscv_disk bs=1M count=512
+```
+
+Making and mounting a root file system on the disk:
+
+```sh
+mkfs.ext2 -L riscv-rootfs riscv_disk
+
+sudo mkdir /mnt/rootfs
+sudo mount riscv_disk /mnt/rootfs
+
+sudo cp -a RootFS/* /mnt/rootfs
+
+sudo chown -R -h root:root /mnt/rootfs/
+df /mnt/rootfs
+# make sure you are in riscv64-sample dir
+cd ../riscv64-sample
+sudo umount /mnt/rootfs
+```
+
+The disk image `riscv_disk` is ready to use.
+
+**Note:** If you need to resize the disk image once it is created, you can do the following:
+
+```sh
+e2fsck -f riscv_disk
+resize2fs ./riscv_disk 512M
+```
+
+Also, if it is required to change the contents of the disk image, it can be mounted as:
+
+```sh
+mount -o loop riscv_disk [some mount directory]
+```
+
+## gem5 Run Scripts
+
+gem5 scripts which can configure a riscv full system and run simulation are available in configs-riscv-fs/.
+The main script `run_riscv.py` expects following arguments:
+
+**bbl:** path to the bbl (berkeley bootloader) binary with kernel payload.
+
+**disk:** path to the disk image to use.
+
+**cpu_type:** cpu model (`atomic`, `simple`).
+
+**num_cpus:** number of cpu cores.
+
+An example use of this script is the following:
+
+```sh
+[gem5 binary] -re configs/run_exit.py [path to bbl] [path to the disk image] atomic 4
+```
+
+To interact with the simulated system's console:
+
+```sh
+telnet localhost 3457 (this port number comes from `simerr` file)
+```
+
+The default linux system based on this README, has both `login` and `password` set as `root`.
diff --git a/src/riscv-fs/configs-riscv-fs/run_riscv.py b/src/riscv-fs/configs-riscv-fs/run_riscv.py
new file mode 100755
index 0000000..1ca8665
--- /dev/null
+++ b/src/riscv-fs/configs-riscv-fs/run_riscv.py
@@ -0,0 +1,84 @@
+# Copyright (c) 2021 The Regents of the University of California.
+# All Rights Reserved
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""
+This script is supposed to run full system simulation for RISCV targets.
+It has been tested with classic memory system and Atomic
+and TimingSimpleCPU so far.
+"""
+
+import time
+import argparse
+
+import m5
+import m5.ticks
+from m5.objects import *
+
+from system import *
+
+def parse_options():
+    parser = argparse.ArgumentParser(description='Runs Linux fs test with'
+                'RISCV.')
+    parser.add_argument("bbl", help='Path to the bbl (berkeley bootloader)'
+                                        'binary with kernel payload')
+    parser.add_argument("disk", help="Path to the disk image to boot")
+    parser.add_argument("cpu_type", help="The type of CPU in the system")
+    parser.add_argument("num_cpus", type=int, help="Number of CPU cores")
+
+    return parser.parse_args()
+
+if __name__ == "__m5_main__":
+
+    args = parse_options()
+
+    # create the system we are going to simulate
+
+    system = RiscvSystem(args.bbl, args.disk, args.cpu_type, args.num_cpus)
+
+    # set up the root SimObject and start the simulation
+    root = Root(full_system = True, system = system)
+
+    # Uncomment for long-running jobs
+    # and when the user does not
+    # need to interact with the
+    # simulated sytem
+
+    # m5.disableAllListeners()
+
+    # instantiate all of the objects we've created above
+    m5.instantiate()
+
+    globalStart = time.time()
+
+    print("Running the simulation")
+    exit_event = m5.simulate()
+
+    if exit_event.getCause() == "m5_exit instruction encountered":
+        print("The user has terminated the simulation using m5")
+        exit(0)
+    else:
+        print("Simulation terminated without using m5")
+        exit(1)
diff --git a/src/riscv-fs/configs-riscv-fs/system/__init__.py b/src/riscv-fs/configs-riscv-fs/system/__init__.py
new file mode 100755
index 0000000..e15a8e8
--- /dev/null
+++ b/src/riscv-fs/configs-riscv-fs/system/__init__.py
@@ -0,0 +1,28 @@
+# Copyright (c) 2021 The Regents of the University of California.
+# All Rights Reserved
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from .system import RiscvSystem
+
diff --git a/src/riscv-fs/configs-riscv-fs/system/system.py b/src/riscv-fs/configs-riscv-fs/system/system.py
new file mode 100755
index 0000000..5032664
--- /dev/null
+++ b/src/riscv-fs/configs-riscv-fs/system/system.py
@@ -0,0 +1,228 @@
+# Copyright (c) 2021 The Regents of the University of California.
+# All Rights Reserved
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met: redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer;
+# redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution;
+# neither the name of the copyright holders nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import m5
+from m5.objects import *
+from m5.util import convert
+
+'''
+This class creates a bare bones RISCV full system.
+
+The targeted system is  based on SiFive FU540-C000.
+Reference:
+[1] https://sifive.cdn.prismic.io/sifive/b5e7a29c-
+d3c2-44ea-85fb-acc1df282e21_FU540-C000-v1p3.pdf
+'''
+
+class RiscvSystem(System):
+
+    def __init__(self, bbl, disk, cpu_type, num_cpus):
+        super(RiscvSystem, self).__init__()
+
+        # Set up the clock domain and the voltage domain
+        self.clk_domain = SrcClockDomain()
+        self.clk_domain.clock = '3GHz'
+        self.clk_domain.voltage_domain = VoltageDomain()
+
+        # DDR memory range starts from base address 0x80000000
+        # based on [1]
+        self.mem_ranges = [AddrRange(start=0x80000000, size='1GB')]
+
+        # Create the main memory bus
+        # This connects to main memory
+        self.membus = SystemXBar(width = 64) # 64-byte width
+
+        # Set up the system port for functional access from the simulator
+        self.system_port = self.membus.cpu_side_ports
+
+        # Create the CPUs for our system.
+        self.createCPU(cpu_type, num_cpus)
+
+        # using RISCV bare metal as the base full system workload
+        self.workload = RiscvBareMetal()
+
+        # this is user passed berkeley boot loader binary
+        # currently the Linux kernel payload is compiled into this
+        # as well
+        self.workload.bootloader = bbl
+
+        # HiFive platform
+        # This is based on a HiFive RISCV board and has
+        # only a limited number of devices so far i.e.
+        # PLIC, CLINT, UART, VirtIOMMIO
+        self.platform = HiFive()
+
+        # Next, create and intialize devices
+        # currently supported for RISCV
+
+        # add a disk image
+        self.attachDisk(disk)
+
+        # set up core and platform
+        # level interrupt controllers
+        self.setupIntrCtrl()
+
+        # set up PMA checker
+        self.pmaChecker()
+
+        # attach off and on chip IO
+        self.attachIO(self.membus)
+
+        # Create the cache heirarchy for the system.
+        self.createCacheHierarchy()
+
+        # Create the memory controller
+        self.createMemoryControllerDDR3()
+
+        self.setupInterrupts()
+
+    def createCPU(self, cpu_type, num_cpus):
+        if cpu_type == "atomic":
+            self.cpu = [AtomicSimpleCPU(cpu_id = i)
+                              for i in range(num_cpus)]
+            self.mem_mode = 'atomic'
+        elif cpu_type == "simple":
+            self.cpu = [TimingSimpleCPU(cpu_id = i)
+                        for i in range(num_cpus)]
+            self.mem_mode = 'timing'
+        else:
+            m5.fatal("No CPU type {}".format(cpu_type))
+
+        for cpu in self.cpu:
+            cpu.createThreads()
+
+
+    def createCacheHierarchy(self):
+        class L1Cache(Cache):
+            """Simple L1 Cache with default values"""
+
+            assoc = 8
+            size = '32kB'
+            tag_latency = 1
+            data_latency = 1
+            response_latency = 1
+            mshrs = 16
+            tgts_per_mshr = 20
+            writeback_clean = True
+
+            def __init__(self):
+                super(L1Cache, self).__init__()
+
+        for cpu in self.cpu:
+            # Create a very simple cache hierarchy
+
+            # Create an L1 instruction, data and mmu cache
+            cpu.icache = L1Cache()
+            cpu.dcache = L1Cache()
+            cpu.mmucache = L1Cache()
+
+            # Connecting icache and dcache to memory bus and cpu
+            cpu.icache.mem_side = self.membus.cpu_side_ports
+            cpu.dcache.mem_side = self.membus.cpu_side_ports
+
+            cpu.icache.cpu_side = cpu.icache_port
+            cpu.dcache.cpu_side = cpu.dcache_port
+
+            # Need a new crossbar for mmucache
+
+            cpu.mmucache.mmubus = L2XBar()
+
+            cpu.mmucache.cpu_side = cpu.mmucache.mmubus.mem_side_ports
+            cpu.mmucache.mem_side = self.membus.cpu_side_ports
+
+            # Connect the itb and dtb to mmucache
+            cpu.mmu.connectWalkerPorts(
+                cpu.mmucache.mmubus.cpu_side_ports, cpu.mmucache.mmubus.cpu_side_ports)
+
+
+    def setupInterrupts(self):
+        for cpu in self.cpu:
+            # create the interrupt controller CPU and connect to the membus
+            cpu.createInterruptController()
+
+
+    def createMemoryControllerDDR3(self):
+        self.mem_cntrls = [
+            MemCtrl(dram = DDR3_1600_8x8(range = self.mem_ranges[0]),
+                    port = self.membus.mem_side_ports)
+        ]
+
+    def attachIO(self, membus):
+        self.iobus = IOXBar()
+
+        self.bridge = Bridge(delay='50ns')
+        self.bridge.master = self.iobus.slave
+        self.bridge.slave = self.membus.master
+        self.bridge.ranges = self.platform._off_chip_ranges()
+
+        # Connecting on chip and off chip IO to the mem
+        # and IO bus
+        self.platform.attachOnChipIO(self.membus)
+        self.platform.attachOffChipIO(self.iobus)
+
+    def setupIntrCtrl(self):
+        self.intrctrl = IntrControl()
+
+        # Set the frequency of RTC (real time clock) used by
+        # CLINT (core level interrupt controller).
+        # This frequency is 1MHz in SiFive's U54MC.
+        # Setting it to 100MHz for faster simulation (from riscv/fs_linux.py)
+        self.platform.rtc = RiscvRTC(frequency=Frequency("100MHz"))
+
+        # RTC sends the clock signal to CLINT via an interrupt pin.
+        self.platform.clint.int_pin = self.platform.rtc.int_pin
+
+        # Attach the PLIC (platform level interrupt controller)
+        # to the platform. This initializes the PLIC with
+        # interrupt sources coming from off chip devices
+        self.platform.attachPlic()
+
+    def pmaChecker(self):
+        # From riscv/fs_linux.py
+        uncacheable_range = [
+            *self.platform._on_chip_ranges(),
+            *self.platform._off_chip_ranges()
+        ]
+        # PMA (physical memory attribute) checker is a hardware structure
+        # that ensures that physical addresses follow the memory permissions
+
+        # PMA checker can be defined at system-level (system.pma_checker)
+        # or MMU-level (system.cpu[0].mmu.pma_checker). It will be resolved
+        # by RiscvTLB's Parent.any proxy
+
+        self.pma_checker =  PMAChecker(uncacheable=uncacheable_range)
+
+    def attachDisk(self, disk):
+        # VirtIOMMIO
+        image = CowDiskImage(child=RawDiskImage(read_only=True), read_only=False)
+        image.child.image_file = disk
+        # using reserved memory space
+        self.platform.disk = MmioVirtIO(
+            vio=VirtIOBlock(image=image),
+            interrupt_id=0x8,
+            pio_size = 4096,
+            pio_addr=0x10008000
+        )
diff --git a/src/riscv-fs/device.dts b/src/riscv-fs/device.dts
new file mode 100644
index 0000000..7181c6c
--- /dev/null
+++ b/src/riscv-fs/device.dts
@@ -0,0 +1,80 @@
+/dts-v1/;
+
+/ {
+	#address-cells = <0x2>;
+	#size-cells = <0x2>;
+	compatible = "riscv-virtio";
+	model = "riscv-virtio,qemu";
+
+	chosen {
+		bootargs = "root=/dev/vda ro console=ttyS0";
+		stdout-path = "/soc/uart@10000000";
+	};
+
+	memory@80000000 {
+		device_type = "memory";
+		reg = <0x0 0x80000000 0x0 0x8000000>;
+	};
+
+	cpus {
+		#address-cells = <0x1>;
+		#size-cells = <0x0>;
+		timebase-frequency = <0x989680>;
+
+		cpu@0 {
+			phandle = <0x1>;
+			device_type = "cpu";
+			reg = <0x0>;
+			status = "okay";
+			compatible = "riscv";
+			riscv,isa = "rv64imafdcsu";
+			mmu-type = "riscv,sv48";
+
+			interrupt-controller {
+				#interrupt-cells = <0x1>;
+				interrupt-controller;
+				compatible = "riscv,cpu-intc";
+				phandle = <0x2>;
+			};
+		};
+	};
+
+	soc {
+		#address-cells = <0x2>;
+		#size-cells = <0x2>;
+		compatible = "simple-bus";
+		ranges;
+
+		uart@10000000 {
+			interrupts = <0xa>;
+			interrupt-parent = <0x3>;
+			clock-frequency = <0x384000>;
+			reg = <0x0 0x10000000 0x0 0x008>;
+			compatible = "ns8250";
+		};
+
+		plic@c000000 {
+			phandle = <0x3>;
+			riscv,ndev = <0xa>;
+			reg = <0x0 0xc000000 0x0 0x210000>;
+			interrupts-extended = <0x2 0xb 0x2 0x9>;
+			interrupt-controller;
+			compatible = "riscv,plic0";
+			#interrupt-cells = <0x1>;
+			#address-cells = <0x0>;
+		};
+
+		virtio_mmio@10008000 {
+			interrupts = <0x8>;
+			interrupt-parent = <0x3>;
+			reg = <0x0 0x10008000 0x0 0x1000>;
+			compatible = "virtio,mmio";
+		};
+
+		clint@2000000 {
+			interrupts-extended = <0x2 0x3 0x2 0x7>;
+			reg = <0x0 0x2000000 0x0 0x10000>;
+			compatible = "riscv,clint0";
+		};
+	};
+};