rvemu/
bus.rs

1//! The bus module contains the system bus which can access the memroy or memory-mapped peripheral
2//! devices.
3
4use crate::devices::{clint::Clint, plic::Plic, uart::Uart, virtio_blk::Virtio};
5use crate::dram::{Dram, DRAM_SIZE};
6use crate::exception::Exception;
7use crate::rom::Rom;
8
9// QEMU virt machine:
10// https://github.com/qemu/qemu/blob/master/hw/riscv/virt.c#L46-L63
11
12/// The address which the mask ROM starts.
13pub const MROM_BASE: u64 = 0x1000;
14/// The address which the mask ROM ends.
15const MROM_END: u64 = MROM_BASE + 0xf000;
16
17/// The address which the core-local interruptor (CLINT) starts. It contains the timer and generates
18/// per-hart software interrupts and timer interrupts.
19pub const CLINT_BASE: u64 = 0x200_0000;
20/// The address which the core-local interruptor (CLINT) ends.
21const CLINT_END: u64 = CLINT_BASE + 0x10000;
22
23/// The address which the platform-level interrupt controller (PLIC) starts. The PLIC connects all
24/// external interrupts in the system to all hart contexts in the system, via the external interrupt
25/// source in each hart.
26pub const PLIC_BASE: u64 = 0xc00_0000;
27/// The address which the platform-level interrupt controller (PLIC) ends.
28const PLIC_END: u64 = PLIC_BASE + 0x208000;
29
30/// The address which UART starts. QEMU puts UART registers here in physical memory.
31pub const UART_BASE: u64 = 0x1000_0000;
32/// The size of UART.
33pub const UART_SIZE: u64 = 0x100;
34/// The address which UART ends.
35const UART_END: u64 = UART_BASE + 0x100;
36
37/// The address which virtio starts.
38pub const VIRTIO_BASE: u64 = 0x1000_1000;
39/// The address which virtio ends.
40const VIRTIO_END: u64 = VIRTIO_BASE + 0x1000;
41
42/// The address which DRAM starts.
43pub const DRAM_BASE: u64 = 0x8000_0000;
44/// The address which DRAM ends.
45const DRAM_END: u64 = DRAM_BASE + DRAM_SIZE;
46
47/// The system bus.
48pub struct Bus {
49    pub clint: Clint,
50    plic: Plic,
51    pub uart: Uart,
52    pub virtio: Virtio,
53    dram: Dram,
54    rom: Rom,
55}
56
57impl Bus {
58    /// Create a new bus object.
59    pub fn new() -> Bus {
60        Self {
61            clint: Clint::new(),
62            plic: Plic::new(),
63            uart: Uart::new(),
64            virtio: Virtio::new(),
65            dram: Dram::new(),
66            rom: Rom::new(),
67        }
68    }
69
70    /// Set the binary data to the memory.
71    pub fn initialize_dram(&mut self, data: Vec<u8>) {
72        self.dram.initialize(data);
73    }
74
75    /// Set the binary data to the virtIO disk.
76    pub fn initialize_disk(&mut self, data: Vec<u8>) {
77        self.virtio.initialize(data);
78    }
79
80    /// Load a `size`-bit data from the device that connects to the system bus.
81    pub fn read(&mut self, addr: u64, size: u8) -> Result<u64, Exception> {
82        match addr {
83            MROM_BASE..=MROM_END => self.rom.read(addr, size),
84            CLINT_BASE..=CLINT_END => self.clint.read(addr, size),
85            PLIC_BASE..=PLIC_END => self.plic.read(addr, size),
86            UART_BASE..=UART_END => self.uart.read(addr, size),
87            VIRTIO_BASE..=VIRTIO_END => self.virtio.read(addr, size),
88            DRAM_BASE..=DRAM_END => self.dram.read(addr, size),
89            _ => Err(Exception::LoadAccessFault),
90        }
91    }
92
93    /// Store a `size`-bit data to the device that connects to the system bus.
94    pub fn write(&mut self, addr: u64, value: u64, size: u8) -> Result<(), Exception> {
95        match addr {
96            CLINT_BASE..=CLINT_END => self.clint.write(addr, value, size),
97            PLIC_BASE..=PLIC_END => self.plic.write(addr, value, size),
98            UART_BASE..=UART_END => self.uart.write(addr, value as u8, size),
99            VIRTIO_BASE..=VIRTIO_END => self.virtio.write(addr, value as u32, size),
100            DRAM_BASE..=DRAM_END => self.dram.write(addr, value, size),
101            _ => Err(Exception::StoreAMOAccessFault),
102        }
103    }
104}