Skip to content

Commit 6aecdeb

Browse files
committed
Refactor "src/system.c" to enhence reusability
1 parent 819000a commit 6aecdeb

File tree

3 files changed

+187
-134
lines changed

3 files changed

+187
-134
lines changed

Makefile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ CFLAGS = -std=gnu99 $(OPT_LEVEL) -Wall -Wextra -Werror
1313
CFLAGS += -Wno-unused-label
1414
CFLAGS += -include src/common.h -Isrc/
1515

16+
OBJS_EXT :=
17+
1618
# In the system test suite, the executable is an ELF file (e.g., MMU).
1719
# However, the Linux kernel emulation includes the Image, DT, and
1820
# root filesystem (rootfs). Therefore, the test suite needs this
@@ -36,6 +38,10 @@ $(call set-feature, LOG_COLOR)
3638
ENABLE_SYSTEM ?= 0
3739
$(call set-feature, SYSTEM)
3840

41+
ifeq ($(call has, SYSTEM), 1)
42+
OBJS_EXT += system.o
43+
endif
44+
3945
# Definition that bridges:
4046
# Device Tree(initrd, memory range)
4147
# src/io.c(memory init)
@@ -96,8 +102,6 @@ endif
96102
# Disable Intel's Control-flow Enforcement Technology (CET)
97103
CFLAGS += $(CFLAGS_NO_CET)
98104

99-
OBJS_EXT :=
100-
101105
# Integer Multiplication and Division instructions
102106
ENABLE_EXT_M ?= 1
103107
$(call set-feature, EXT_M)

src/system.c

Lines changed: 6 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,11 @@
33
* "LICENSE" for information on usage and redistribution of this file.
44
*/
55

6-
#if !RV32_HAS(SYSTEM)
7-
#error "Do not manage to build this file unless you enable system support."
8-
#endif
9-
106
#include <assert.h>
117

12-
#include "devices/plic.h"
13-
#include "devices/uart.h"
14-
#include "devices/virtio.h"
15-
#include "riscv_private.h"
16-
17-
#define R 1
18-
#define W 0
8+
#include "system.h"
199

20-
#if RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER)
10+
#if !RV32_HAS(ELF_LOADER)
2111
void emu_update_uart_interrupts(riscv_t *rv)
2212
{
2313
vm_attr_t *attr = PRIV(rv);
@@ -29,7 +19,7 @@ void emu_update_uart_interrupts(riscv_t *rv)
2919
plic_update_interrupts(attr->plic);
3020
}
3121

32-
static void emu_update_vblk_interrupts(riscv_t *rv)
22+
void emu_update_vblk_interrupts(riscv_t *rv)
3323
{
3424
vm_attr_t *attr = PRIV(rv);
3525
if (attr->vblk->interrupt_status)
@@ -38,112 +28,6 @@ static void emu_update_vblk_interrupts(riscv_t *rv)
3828
attr->plic->active &= ~IRQ_VBLK_BIT;
3929
plic_update_interrupts(attr->plic);
4030
}
41-
/*
42-
* Linux kernel might create signal frame when returning from trap
43-
* handling, which modifies the SEPC CSR. Thus, the fault instruction
44-
* cannot always redo. For example, invalid memory access causes SIGSEGV.
45-
*/
46-
extern bool need_handle_signal;
47-
#define CHECK_PENDING_SIGNAL(rv, signal_flag) \
48-
do { \
49-
signal_flag = (rv->csr_sepc != rv->last_csr_sepc); \
50-
} while (0)
51-
52-
#define MMIO_R 1
53-
#define MMIO_W 0
54-
55-
enum SUPPORTED_MMIO {
56-
MMIO_PLIC,
57-
MMIO_UART,
58-
MMIO_VIRTIOBLK,
59-
};
60-
61-
/* clang-format off */
62-
#define MMIO_OP(io, rw) \
63-
switch(io){ \
64-
case MMIO_PLIC: \
65-
IIF(rw)( /* read */ \
66-
mmio_read_val = plic_read(PRIV(rv)->plic, addr & 0x3FFFFFF); \
67-
plic_update_interrupts(PRIV(rv)->plic); \
68-
return mmio_read_val; \
69-
, /* write */ \
70-
plic_write(PRIV(rv)->plic, addr & 0x3FFFFFF, val); \
71-
plic_update_interrupts(PRIV(rv)->plic); \
72-
return; \
73-
) \
74-
break; \
75-
case MMIO_UART: \
76-
IIF(rw)( /* read */ \
77-
mmio_read_val = u8250_read(PRIV(rv)->uart, addr & 0xFFFFF); \
78-
emu_update_uart_interrupts(rv); \
79-
return mmio_read_val; \
80-
, /* write */ \
81-
u8250_write(PRIV(rv)->uart, addr & 0xFFFFF, val); \
82-
emu_update_uart_interrupts(rv); \
83-
return; \
84-
) \
85-
break; \
86-
case MMIO_VIRTIOBLK: \
87-
IIF(rw)( /* read */ \
88-
mmio_read_val = virtio_blk_read(PRIV(rv)->vblk, addr & 0xFFFFF); \
89-
emu_update_vblk_interrupts(rv); \
90-
return mmio_read_val; \
91-
, /* write */ \
92-
virtio_blk_write(PRIV(rv)->vblk, addr & 0xFFFFF, val); \
93-
emu_update_vblk_interrupts(rv); \
94-
return; \
95-
) \
96-
break; \
97-
default: \
98-
rv_log_error("Unknown MMIO type %d", io); \
99-
break; \
100-
}
101-
/* clang-format on */
102-
103-
#define MMIO_READ() \
104-
do { \
105-
uint32_t mmio_read_val; \
106-
if ((addr >> 28) == 0xF) { /* MMIO at 0xF_______ */ \
107-
/* 256 regions of 1MiB */ \
108-
switch ((addr >> 20) & MASK(8)) { \
109-
case 0x0: \
110-
case 0x2: /* PLIC (0 - 0x3F) */ \
111-
MMIO_OP(MMIO_PLIC, MMIO_R); \
112-
break; \
113-
case 0x40: /* UART */ \
114-
MMIO_OP(MMIO_UART, MMIO_R); \
115-
break; \
116-
case 0x42: /* Virtio-blk */ \
117-
MMIO_OP(MMIO_VIRTIOBLK, MMIO_R); \
118-
break; \
119-
default: \
120-
__UNREACHABLE; \
121-
break; \
122-
} \
123-
} \
124-
} while (0)
125-
126-
#define MMIO_WRITE() \
127-
do { \
128-
if ((addr >> 28) == 0xF) { /* MMIO at 0xF_______ */ \
129-
/* 256 regions of 1MiB */ \
130-
switch ((addr >> 20) & MASK(8)) { \
131-
case 0x0: \
132-
case 0x2: /* PLIC (0 - 0x3F) */ \
133-
MMIO_OP(MMIO_PLIC, MMIO_W); \
134-
break; \
135-
case 0x40: /* UART */ \
136-
MMIO_OP(MMIO_UART, MMIO_W); \
137-
break; \
138-
case 0x42: /* Virtio-blk */ \
139-
MMIO_OP(MMIO_VIRTIOBLK, MMIO_W); \
140-
break; \
141-
default: \
142-
__UNREACHABLE; \
143-
break; \
144-
} \
145-
} \
146-
} while (0)
14731
#endif
14832

14933
static bool ppn_is_valid(riscv_t *rv, uint32_t ppn)
@@ -221,8 +105,8 @@ pte_t *mmu_walk(riscv_t *rv, const uint32_t vaddr, uint32_t *level)
221105
#define MMU_FAULT_CHECK(op, rv, pte, vaddr, access_bits) \
222106
mmu_##op##_fault_check(rv, pte, vaddr, access_bits)
223107
#define MMU_FAULT_CHECK_IMPL(op, pgfault) \
224-
static bool mmu_##op##_fault_check(riscv_t *rv, pte_t *pte, \
225-
uint32_t vaddr, uint32_t access_bits) \
108+
bool mmu_##op##_fault_check(riscv_t *rv, pte_t *pte, uint32_t vaddr, \
109+
uint32_t access_bits) \
226110
{ \
227111
uint32_t scause; \
228112
uint32_t stval = vaddr; \
@@ -285,16 +169,6 @@ MMU_FAULT_CHECK_IMPL(ifetch, pagefault_insn)
285169
MMU_FAULT_CHECK_IMPL(read, pagefault_load)
286170
MMU_FAULT_CHECK_IMPL(write, pagefault_store)
287171

288-
uint32_t ppn;
289-
uint32_t offset;
290-
#define get_ppn_and_offset() \
291-
do { \
292-
assert(pte); \
293-
ppn = *pte >> (RV_PG_SHIFT - 2) << RV_PG_SHIFT; \
294-
offset = level == 1 ? vaddr & MASK((RV_PG_SHIFT + 10)) \
295-
: vaddr & MASK(RV_PG_SHIFT); \
296-
} while (0)
297-
298172
/* The IO handler that operates when the Memory Management Unit (MMU)
299173
* is enabled during system emulation is responsible for managing
300174
* input/output operations. These callbacks are designed to implement
@@ -449,7 +323,7 @@ static void mmu_write_b(riscv_t *rv, const uint32_t vaddr, const uint8_t val)
449323
* TODO: dTLB can be introduced here to
450324
* cache the gVA to gPA tranlation.
451325
*/
452-
static uint32_t mmu_translate(riscv_t *rv, uint32_t vaddr, bool rw)
326+
uint32_t mmu_translate(riscv_t *rv, uint32_t vaddr, bool rw)
453327
{
454328
if (!rv->csr_satp)
455329
return vaddr;

src/system.h

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/*
2+
* rv32emu is freely redistributable under the MIT License. See the file
3+
* "LICENSE" for information on usage and redistribution of this file.
4+
*/
5+
6+
#pragma once
7+
8+
#if !RV32_HAS(SYSTEM)
9+
#error "Do not manage to build this file unless you enable system support."
10+
#endif
11+
12+
#include "devices/plic.h"
13+
#include "devices/uart.h"
14+
#include "riscv_private.h"
15+
16+
#define R 1
17+
#define W 0
18+
19+
#if !RV32_HAS(ELF_LOADER)
20+
21+
#define MMIO_R 1
22+
#define MMIO_W 0
23+
24+
enum SUPPORTED_MMIO {
25+
MMIO_PLIC,
26+
MMIO_UART,
27+
MMIO_VIRTIOBLK,
28+
};
29+
30+
/* clang-format off */
31+
#define MMIO_OP(io, rw) \
32+
switch(io){ \
33+
case MMIO_PLIC: \
34+
IIF(rw)( /* read */ \
35+
mmio_read_val = plic_read(PRIV(rv)->plic, addr & 0x3FFFFFF); \
36+
plic_update_interrupts(PRIV(rv)->plic); \
37+
return mmio_read_val; \
38+
, /* write */ \
39+
plic_write(PRIV(rv)->plic, addr & 0x3FFFFFF, val); \
40+
plic_update_interrupts(PRIV(rv)->plic); \
41+
return; \
42+
) \
43+
break; \
44+
case MMIO_UART: \
45+
IIF(rw)( /* read */ \
46+
mmio_read_val = u8250_read(PRIV(rv)->uart, addr & 0xFFFFF); \
47+
emu_update_uart_interrupts(rv); \
48+
return mmio_read_val; \
49+
, /* write */ \
50+
u8250_write(PRIV(rv)->uart, addr & 0xFFFFF, val); \
51+
emu_update_uart_interrupts(rv); \
52+
return; \
53+
) \
54+
break; \
55+
case MMIO_VIRTIOBLK: \
56+
IIF(rw)( /* read */ \
57+
mmio_read_val = virtio_blk_read(PRIV(rv)->vblk, addr & 0xFFFFF); \
58+
emu_update_vblk_interrupts(rv); \
59+
return mmio_read_val; \
60+
, /* write */ \
61+
virtio_blk_write(PRIV(rv)->vblk, addr & 0xFFFFF, val); \
62+
emu_update_vblk_interrupts(rv); \
63+
return; \
64+
) \
65+
break; \
66+
default: \
67+
rv_log_error("unknown MMIO type %d\n", io); \
68+
break; \
69+
}
70+
/* clang-format on */
71+
72+
#define MMIO_READ() \
73+
do { \
74+
uint32_t mmio_read_val; \
75+
if ((addr >> 28) == 0xF) { /* MMIO at 0xF_______ */ \
76+
/* 256 regions of 1MiB */ \
77+
switch ((addr >> 20) & MASK(8)) { \
78+
case 0x0: \
79+
case 0x2: /* PLIC (0 - 0x3F) */ \
80+
MMIO_OP(MMIO_PLIC, MMIO_R); \
81+
break; \
82+
case 0x40: /* UART */ \
83+
MMIO_OP(MMIO_UART, MMIO_R); \
84+
break; \
85+
case 0x42: /* Virtio-blk */ \
86+
MMIO_OP(MMIO_VIRTIOBLK, MMIO_R); \
87+
break; \
88+
default: \
89+
__UNREACHABLE; \
90+
break; \
91+
} \
92+
} \
93+
} while (0)
94+
95+
#define MMIO_WRITE() \
96+
do { \
97+
if ((addr >> 28) == 0xF) { /* MMIO at 0xF_______ */ \
98+
/* 256 regions of 1MiB */ \
99+
switch ((addr >> 20) & MASK(8)) { \
100+
case 0x0: \
101+
case 0x2: /* PLIC (0 - 0x3F) */ \
102+
MMIO_OP(MMIO_PLIC, MMIO_W); \
103+
break; \
104+
case 0x40: /* UART */ \
105+
MMIO_OP(MMIO_UART, MMIO_W); \
106+
break; \
107+
case 0x42: /* Virtio-blk */ \
108+
MMIO_OP(MMIO_VIRTIOBLK, MMIO_W); \
109+
break; \
110+
default: \
111+
__UNREACHABLE; \
112+
break; \
113+
} \
114+
} \
115+
} while (0)
116+
117+
void emu_update_uart_interrupts(riscv_t *rv);
118+
void emu_update_vblk_interrupts(riscv_t *rv);
119+
120+
/*
121+
* Linux kernel might create signal frame when returning from trap
122+
* handling, which modifies the SEPC CSR. Thus, the fault instruction
123+
* cannot always redo. For example, invalid memory access causes SIGSEGV.
124+
*/
125+
extern bool need_handle_signal;
126+
127+
#define CHECK_PENDING_SIGNAL(rv, signal_flag) \
128+
do { \
129+
signal_flag = (rv->csr_sepc != rv->last_csr_sepc); \
130+
} while (0)
131+
132+
#endif
133+
134+
/* Walk through page tables and get the corresponding PTE by virtual address if
135+
* exists
136+
* @rv: RISC-V emulator
137+
* @addr: virtual address
138+
* @level: the level of which the PTE is located
139+
* @return: NULL if a not found or fault else the corresponding PTE
140+
*/
141+
uint32_t *mmu_walk(riscv_t *rv, const uint32_t addr, uint32_t *level);
142+
143+
/* Verify the PTE and generate corresponding faults if needed
144+
* @op: the operation
145+
* @rv: RISC-V emulator
146+
* @pte: to be verified pte
147+
* @addr: the corresponding virtual address to cause fault
148+
* @return: false if a any fault is generated which caused by violating the
149+
* access permission else true
150+
*/
151+
/* FIXME: handle access fault, addr out of range check */
152+
#define MMU_FAULT_CHECK_DECL(op) \
153+
bool mmu_##op##_fault_check(riscv_t *rv, uint32_t *pte, uint32_t vaddr, \
154+
uint32_t access_bits);
155+
156+
MMU_FAULT_CHECK_DECL(ifetch);
157+
MMU_FAULT_CHECK_DECL(read);
158+
MMU_FAULT_CHECK_DECL(write);
159+
160+
/*
161+
* TODO: dTLB can be introduced here to
162+
* cache the gVA to gPA tranlation.
163+
*/
164+
uint32_t mmu_translate(riscv_t *rv, uint32_t vaddr, bool rw);
165+
166+
uint32_t *mmu_walk(riscv_t *rv, const uint32_t addr, uint32_t *level);
167+
168+
#define get_ppn_and_offset() \
169+
uint32_t ppn; \
170+
uint32_t offset; \
171+
do { \
172+
ppn = *pte >> (RV_PG_SHIFT - 2) << RV_PG_SHIFT; \
173+
offset = level == 1 ? vaddr & MASK((RV_PG_SHIFT + 10)) \
174+
: vaddr & MASK(RV_PG_SHIFT); \
175+
} while (0)

0 commit comments

Comments
 (0)