Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preliminary support for MMU emulation #438

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

ChinYikMing
Copy link
Collaborator

@ChinYikMing ChinYikMing commented May 12, 2024

The purpose of this commit is to boot 32-bit RISC-V Linux in the future. The virtual memory scheme to support is Sv32. There are one change to original code base to adapt the MMU:

The prototype of riscv_io_t interface needs to be changed.
Particularly, add a RISC-V instance(riscv_t) as the first parameter.
MMU related callbacks require to access the satp CSR to perform a
page table walk during virtual memory translation but satp CSR is
stored in RISC-V instance(riscv_t), thus it should have a way to
access the satp CSR. The trivial solution is adding RISC-V
instance(riscv_t) to the prototype of riscv_io_t interface.

After this change, we can reuse riscv_io_t for system emulation afterward.

The rest of changes are implementing the Sv32 virtual memory scheme. For every memory access, it has to walk through the page table to get the corresponding PTE. Depends on the retrieval of PTE, there are several page faults to be handled if necessary, so there are three exceptions handlers have been introduced which are insn_pgfault, load_pgfault, and store_pgfault and they are used in MMU_CHECK_FAULT. In this commit, the access fault are not handled well since they are related to PMA and PMP and they might not the must to boot 32-bit RISC-V Linux (tested on semu). More PTE, S-mode, M-mode CSR helper macro are introduced as well.

Related: #310

@ChinYikMing
Copy link
Collaborator Author

This PR is not fully ready to be merged since testing is not yet fully designed. PR earlier to get some feedbacks for further design.

Copy link
Contributor

@jserv jserv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmarks

Benchmark suite Current: 66c2b9d Previous: 05cfc40 Ratio
Dhrystone 1755.22 Average DMIPS over 10 runs 1596.88 Average DMIPS over 10 runs 0.91
Coremark 1502.888 Average iterations/sec over 10 runs 1474.188 Average iterations/sec over 10 runs 0.98

This comment was automatically generated by workflow using github-action-benchmark.

@ChinYikMing
Copy link
Collaborator Author

The initial design mentioned in here does not fully consider the CSR such as satp CSR needs to be accessed during MMU translation. During implementation, the interface shall be changed to adapt MMU translation.

The purpose of this commit is to boot 32-bit RISC-V Linux in the future.
The virtual memory scheme to support is Sv32. There are one change to
original code base to adapt the MMU:
   The prototype of riscv_io_t interface needs to be changed.
   Particularly, add a RISC-V instance(riscv_t) as the first parameter.
   MMU related callbacks require to access the satp CSR to perform a
   page table walk during virtual memory translation but satp CSR is
   stored in RISC-V instance(riscv_t), thus it should have a way to
   access the satp CSR. The trivial solution is adding RISC-V
   instance(riscv_t) to the prototype of riscv_io_t interface.
After this change, we can reuse riscv_io_t for system emulation
afterward.

The rest of changes are implementing the Sv32 virtual memory scheme. For
every memory access, it has to walk through the page table to get the
corresponding PTE. Depends on the retrieval of PTE, there are several
page faults to be handled if necessary, so there are three exceptions
handlers have been introduced which are insn_pgfault, load_pgfault, and
store_pgfault and they are used in MMU_CHECK_FAULT. In this commit, the
access fault are not handled well since they are related to PMA and PMP
and they might not the must to boot 32-bit RISC-V Linux (tested on
semu). Some S-mode CSRs are added to riscv_internal to support S-mode.
PTE, S-mode and M-mode CSR helper macro are introduced as well.

Related: sysprog21#310
@@ -25,6 +25,8 @@

#define ARRAYS_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))

#define MASK(n) (~(1 << n))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps replacing "1" with "1U" to avoid unexpected signed integer overflow would be better?

@@ -604,16 +619,18 @@ static void block_translate(riscv_t *rv, block_t *block)
block->pc_start = block->pc_end = rv->PC;

rv_insn_t *prev_ir = NULL;
rv_insn_t *ir = mpool_calloc(rv->block_ir_mp);
rv_insn_t *ir = mpool_alloc(rv->block_ir_mp);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why should mpool_calloc() be replaced with mpool_alloc() and then execute memset()?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like a rebase problem for #437.

block->ir_head = ir;

/* translate the basic block */
while (true) {
memset(ir, 0, sizeof(rv_insn_t));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like a rebase problem for #437.

@@ -644,7 +661,7 @@ static void block_translate(riscv_t *rv, block_t *block)
break;
}

ir = mpool_calloc(rv->block_ir_mp);
ir = mpool_alloc(rv->block_ir_mp);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like a rebase problem for #437.

@@ -51,7 +52,10 @@ extern struct target_ops gdbstub_ops;
_(breakpoint, 3) /* Breakpoint */ \
_(load_misaligned, 4) /* Load address misaligned */ \
_(store_misaligned, 6) /* Store/AMO address misaligned */ \
_(ecall_M, 11) /* Environment call from M-mode */
_(ecall_M, 11) /* Environment call from M-mode */ \
_(insn_pgfault, 12) /* Instruction page fault */ \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to pagefault_insn, pagefault_load, and pagefault_store.

if (!pte && rv->csr_satp) { /* not found */ \
rv_except_##pgfault(rv, addr); \
return false; \
} else if (pte && \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace else if with if statement.

(!(*pte & PTE_V) || (!(*pte & PTE_R) && (*pte & PTE_W)))) { \
rv_except_##pgfault(rv, addr); \
return false; \
} else if (pte && (!(*pte & PTE_X) && (access_bits & PTE_X))) { \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto. No else is required for the sake of previous return.

@jserv jserv requested a review from qwe661234 May 13, 2024 01:48
@jserv jserv changed the title Support emulating memory management unit(MMU) Preliminary support for MMU emulation May 13, 2024
@jserv
Copy link
Contributor

jserv commented May 13, 2024

This PR is not fully ready to be merged since testing is not yet fully designed. PR earlier to get some feedbacks for further design.

How can we test the MMU specific operations?

@ChinYikMing
Copy link
Collaborator Author

ChinYikMing commented May 13, 2024

This PR is not fully ready to be merged since testing is not yet fully designed. PR earlier to get some feedbacks for further design.

How can we test the MMU specific operations?

The testing idea can be break down to following steps:

  1. Creating a simple userspace application and kernel supervisor
  2. Starts executing with the simple kernel supervisor. Read/write CSR register to install exception vector table to specific address for traps and root page table for MMU translation.
  3. After all CSR stuffs are done, switch to user mode and execute userspace application. At this point, I would like to design some scenario to testing all three types of page fault (instruction, load, store page fault). For every userspace memory access, dump the page table could be beneficial for verification or debugging.

If I am at the wrong path, please correct me.

It take times to design this testing. So, I would try to support other peripherals emulation at the same time such as PLIC.

@jserv
Copy link
Contributor

jserv commented May 13, 2024

  1. Creating a simple userspace application and kernel supervisor
  2. Starts executing with the simple kernel supervisor. Read/write CSR register to install exception vector table to specific address for traps and root page table for MMU translation.
  3. After all CSR stuffs are done, switch to user mode and execute userspace application. At this point, I would like to design some scenario to testing all three types of page fault (instruction, load, store page fault). For every userspace memory access, dump the page table could be beneficial for verification or debugging.

The above sound great. I expect the lean and reasonably straightforward approach as following:

ChinYikMing added a commit to ChinYikMing/rv32emu that referenced this pull request May 26, 2024
sret instruction is used for returning from a trap when trap occurs in
S-mode level. Thus, the execution flow will not be sequential. During
basic block translation, the sret instruction should be considered as
can_branch instruction.

Moreover, the existing system instruction decoder does not support
decoding the sret instruction. Thus, the ir->opcode should be set
correctly to support decoding the sret instruction. The implementation
of sret instruction is simply returning false for now, the improved
implementation will be completed and tested in sysprog21#438.
ChinYikMing added a commit to ChinYikMing/rv32emu that referenced this pull request May 26, 2024
sret instruction is used for returning from a trap when trap occurs in
S-mode level. Thus, the execution flow will not be sequential. During
basic block translation, the sret instruction should be considered as
can_branch instruction.

Moreover, the existing system instruction decoder does not support
decoding the sret instruction. Thus, the ir->opcode should be set
correctly to support decoding the sret instruction. The implementation
of sret instruction is simply returning false for now, the improved
implementation will be completed and tested in sysprog21#438 since the sret
instruction involves privilege mode changing.
ChinYikMing added a commit to ChinYikMing/rv32emu that referenced this pull request May 27, 2024
sret instruction is used for returning from a trap when trap occurs in
S-mode level. Thus, the execution flow will not be sequential. During
basic block translation, the sret instruction should be considered as
can_branch instruction.

Moreover, the existing system instruction decoder does not support
decoding the sret instruction. Thus, the ir->opcode should be set
correctly to support decoding the sret instruction. The implementation
of sret instruction is simply returning false for now, the improved
implementation will be completed and tested in sysprog21#438 since the sret
instruction involves privilege mode changing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants