Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@DieracDelta
Copy link
Contributor

@DieracDelta DieracDelta commented Jun 18, 2019

Pull Request Overview

This is an attempt to merge MIT's branch of Tock into upstream for RISC-V, and then presumably master.
The major additions were:

  • Corrected MMIO for PLIC (addresses seemed off when I compared to the memory map).
  • Modified the PLIC's clear_all_pending(). Prior implementation wrote to read only register. Now claims and completes all interrupts.
  • Integrated an abstraction over writing to the CSR register space. This allows us to write to the control/status registers in Rust and only need to specify the CSR location once--in its corresponding file. This makes the code both more readable and less bug prone. The library contains most of the m-mode and s-mode registers (u-mode ones shouldn't be difficult to implement), and registers necessary for the pmp. The source files are scattered throughout Tock, and used whever we write CSRs.
  • Added in CLINT implementation; code source in arch/rv32i/clint.rs. Includes:
    - MMIO
    - read/write for time/timecp
    - triggering software interrupts
    - Note that it seems like this memory region got replaced in the Arty-e21 board by just a timer, and you've abstracted over it with a machine timer. If we reduce the memory map for that machine timer (or distinguish it/include it as a parameter with different types), we could reuse the abstraction for both the clint and the machine-timer.
  • An initial stab at a PMP driver using this CSR abstraction. Code source in arch/rv32i/pmp.rs and arch/rv32i/chip.rs. A couple of things to note:
    - I've implemented the bitfields in the library, so this should make things easier, as we can rely on this abstraction.
    - I don't have any way of testing this outside of qemu (only have e310 board), so would appreciate some help from people with a board.
    - Do we want to have the PMP implement the same things as the MPU? Presumably on most implementations, we can have it provide similar functionality. As it stands right now, the PMP is implementing the kernel::mpu::MPU trait. Does it make sense to refactor with the MPU renamed to something more general--perhaps a MemoryProtector trait--and have the PMP and the cortex-ms have the MPU and PMP implement that trait?
  • Interrupt Handling. Added to the e310 board only (no way of testing the arty) a bit of pattern matching/more detailed debugging/error messages for exceptions and interrupts.
    - Claims and completes external interrupts.
    - Distinguishes each type of trap
    - Exceptions include debugging information.
  • Slightly revised boards/hifive1/src/main.rs. Now, completely disables interrupts by clearing the mie bit in the mstatus csr, and the msoft, mtimer, and mext bits in the mie csr before modifying plic. If the goal is to supress all interrupts, we could easily include that functionality by just making the threshold greater than all the priorities.

Testing Strategy

I was able to test on the Hifive1 board using the included Makefile. Oddly, was not able to get gdb to work with OpenOCD. Has anyone had success with this? I'd been running something along the lines of:

$PATH_TO_OPENOCD_RISCV_FORK -f $OPEN_OCD_CONFIG_FILE
$PATH_TO_RISCV_GDB $PATH_TO_HIFIVE1_ELF $PATH_TO_GDB_INIT_FILE

With a gdbinit file that looked like:

set remotetimeout 240
target extended-remote localhost:3333
monitor reset halt
monitor flash protect 0 64 off
load

It fails due to not having enough space to load into. Has anyone encountered this?

TODO or Help Wanted

See prior section.

Documentation Updated

  • Updated the relevant files in /docs, or no updates are required.

Formatting

  • Ran make formatall.

bradjc and others added 14 commits June 11, 2019 14:26
The initial version of the `UserlandKernelBoundary` trait abstracted
details about context switching on cortex-m platforms out of process.rs,
however, the trait was very much designed around the structure of how
cortex-m handles switching between privilege modes. After working on
RISC-V context switching, it is clear some of the interface does not map
to RISC-V very well.

To make `UserlandKernelBoundary` more generic, this commit makes two
general changes to the trait.

1. Rather than having a separate "getter" for retreiving which syscall
was actually called after learning that the process stopped running
because it called a syscall, we now return the syscall information
directly with the context switch reason. Arguably, this is how it should
have been done before, and this change actually has nothing to do with
making UKB more generic.

2. The Cortex-M context switch uses a special stack frame created by the
SVC call. This approach is not used in RISC-V. Therefore, the
terminology around "popping" and "pushing" stack frames doesn't make
sense in all cases. Also, having `pop_stack_frame()` at all was a bit of
blur between the kernel loop and the context switch interface details as
pop was only called with a yield().

What is generic is the idea that we want to be able to call a function in
the process that the process should execute when it starts (or resumes)
running. That function takes over the old "push" function. "pop" has
been removed. I believe that this new interface will be implementable in
RISC-V, as well as Cortex-M, and actually improves the boundary between
the kernel loop and the context switching code.

This commit also then updates process.rs and sched.rs to use this new
interface. This required some minor changes to process.rs as these
functions are essentially passed through process.rs from the kernel
sched loop to the UKB code.
For portability reasons the syscall interface has been updated, and this
commit implements it for cortex-m.

The major change is that we no longer remove the SVC stack frame after a
yield call. In fact, the API for popping stack frames has been removed.
Instead, when we want the process to run a new function, we re-use the
old SVC stack frame. In the case that the app has never run before we do
have to create the stack frame just like we used to.

In theory, this is actually more efficient since we save the effort of
removing the stack frame, but that wasn't very substancial so who knows.

The other change is we now set state.psr and state.yield_pc after every
syscall, and not just effectively after yield(). I don't think this has
any effect.
Obviously setting the baud rate for UART depends on the clock speed.
Make this a parameter because different boards use different settings.
We probably want a more formal way to do this, but this is simple and
works for now.
@DieracDelta DieracDelta changed the title Integrate MIT fork changes Integrate MIT fork into master Jun 18, 2019
Copy link
Contributor

@bradjc bradjc left a comment

Choose a reason for hiding this comment

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

Overall this looks quite reasonable, but I commented on some code that stuck out.

[dependencies]
kernel = { path = "../../kernel" }
tock_rt0 = { path = "../../libraries/tock-rt0" }
riscvregs = { git = "https://github.com/DieracDelta/riscvregs.git", rev = "c03eb6c" }
Copy link
Contributor

Choose a reason for hiding this comment

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

Unfortunately currently Tock doesn't support (i.e. Tock doesn't allow) external dependencies because we cannot track unsafe use in dependencies currently. So, to use a library like this it would either need to be included in the /library folder in Tock, or included as a module in the rv32i crate.

Copy link
Contributor Author

@DieracDelta DieracDelta Jun 18, 2019

Choose a reason for hiding this comment

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

Would it be reasonable to add this in as a git submodule under libraries? Or should I manually move it into the library folder and abandon its git history?

Copy link
Member

Choose a reason for hiding this comment

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

Ideally we'd avoid git submodules where possible (it's just a bigger hassle to develop with). Moving this into the repo would be great. You can technically do so without losing the git history with some git magic (git filter-branch i believe).

None => (),
Some(ext_interrupt_id) => {
debug!("interrupt triggered {}\n", ext_interrupt_id);
plic::complete(ext_interrupt_id);
Copy link
Contributor

Choose a reason for hiding this comment

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

This doesn't look quite right. The trap handler should only disable the interrupt, not mark it as finished. Or does this not work how I think?

Copy link
Contributor Author

@DieracDelta DieracDelta Jun 18, 2019

Choose a reason for hiding this comment

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

A couple of things to note here. Spec is best described in section 10.7 here. It's under-specified by the spec what happens if you claim the interrupt (read the claim/complete MMIO) multiple times. So at the very least I don't think we can rely on being able to handle the interrupt anytime after this. On the e310 board, from the testing I've done (for example, add in another claim before completing in this same spot), you get interrupt id 0 (no more pending interrupts). So we either need to claim, handle the interrupt, and complete the interrupt here, or have some global "in progress" state that we write the ID to and then read from in service_pending_interrupts to complete. This seemed easier at the time. With this added knowledge, do you think it's worth changing? If so, I can probably get a change out quickly.

You're right, I should disable the interrupt here.

Ideally, all interrupts should be suppressed after the reset_handler, so my intention was just to notify that the interrupt occurred (as it's not expected behavior), and then ignore it.

}
}

pub unsafe fn surpress_all() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Add comment.

:
: "volatile");
/// The trap handler is called on exceptions and for interrupts.
pub unsafe fn configure_trap_handler(mode: PermissionMode) {
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be nice to keep the comment about how this is a generic implementation that specific boards may not want to use.

@bradjc bradjc changed the title Integrate MIT fork into master RISC-V: Update support for HiFive1 board Jun 18, 2019
@DieracDelta
Copy link
Contributor Author

DieracDelta commented Jul 25, 2019

I added a couple of things to get c_hello to run (see most recent push):

  • arch/rv32i/src/syscall.rs: the inline assembly snippet in switch_to_process was overwriting syscall_args. Specifically, when I compiled this for hifive1, the objdump showed something along the lines of:
2041aaa2 <_ecall> li    a5,0
2041aaa4 <_ecall+0x2> lw    t0,124(a6)
2041aaa8 <_ecall+0x6> addi    t0,t0,4
2041aaaa <_ecall+0x8> sw    t0,124(a6)
2041aaae <_ecall+0xc> lw    t0,36(a6)
2041aab2 <_ecall+0x10> sw    t0,0(a5)
2041aab6 <_ecall+0x14> lw    t0,40(a6)
2041aaba <_ecall+0x18> sw    t0,4(a5)
2041aabe <_ecall+0x1c> lw    t0,44(a6)
2041aac2 <_ecall+0x20> sw    t0,8(a5)
2041aac6 <_ecall+0x24> lw    t0,48(a6)
2041aaca <_ecall+0x28> sw    t0,12(a5)
2041aace <_ecall+0x2c> lw    t0,52(a6)
2041aad2 <_ecall+0x30> sw    t0,16(a5)
2041aad6 <_ecall+0x34> lw    a6,4(a6)

for the end of the inline assembly snippet in switch_to_process using the code as it currently stands in the riscv-pr branch. This is bad because li a5,0 moves 0 in a5 but a5 should be holding syscall_args address. I'm not sure if this is a compiler bug, but it seems like one. The solution I'm proposing here is to simply save syscall_args and switch_reason on the stack before we mret and pop them off after. This fixed the issue when I compiled for the hifive1 board. To reproduce, clone the repo and build it for the hifive1 chip, objdump the elf, and grep for _ecall.

  • boards/hifive1/layout.ld
    I could not get tock to compile with reserved space for a userspace app. I think this comes down to the DTIM being too small on the hifive1 board. As a result, I artificially increased the DTIM on qemu and have been testing it on qemu. I'm going to include instructions on how to do this. Admittedly going forward, it's not ideal to be working on an emulator instead of the board. I also increased the space reserved for rom and prog.

  • arch/rv32i/src/lib.rs: When I compiled this for the hifive1 chip, the function had a prologue which pushed a register onto the stack. The #[naked] macro removed this prologue (thanks brad!). Additionally, in order to get the trap address written to mtvec I had to feed it in directly, not take the address of it.

  • boards/hifive1/src/main.rs: I've included the same abstractions as in the arty board.

  • chips/e310x/src/chip.rs: Replaced nullsyscall with the syscall from the arch/rv32i crate.

@bors bors bot closed this Aug 2, 2019
@bradjc
Copy link
Contributor

bradjc commented Aug 2, 2019

Hmm bors closed this...that was unexpected. @DieracDelta can you rebase this and re-open?

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.

3 participants