Repository navigation
fix: make vi (and other full-screen apps) usable, plus assorted CPU/bus correctness fixes - #18
Merged
Merged
Conversation
…l port The host line discipline kept canonical mode, echo and signal generation enabled, so keystrokes only reached the guest once a newline was typed and Ctrl-C terminated the emulator instead of being delivered to the guest. Line oriented commands happened to work, but full screen applications such as vi were unusable and died together with the emulator on the first Ctrl-C. Since ISIG is now off, Ctrl-A x is needed to leave the emulator. The terminal is restored on drop and from a panic hook so it is never left unusable. Bytes are also written to stdout untouched rather than being re-encoded as UTF-8, which corrupted everything the guest emitted above 0x7f. Co-authored-by: Cursor <cursoragent@cursor.com>
…or latch Found while tracking down the vi failure: - DIV performed an unsigned division, so -1 / 1000 yielded 4294967 instead of 0. It is executed constantly by the guest's poll() timeout conversion. - Loads assigned self.x[rd] directly, letting a load with rd == 0 clobber x0. - Out-of-range RAM accesses indexed the backing Vec and panicked, taking the emulator down on any stray guest pointer. They now raise an access fault so the guest can handle it. Instruction fetch reports an access fault as well instead of misreporting it as a misaligned address. - The CLINT was handed an unmasked address on 8 and 16 bit reads, which hit unreachable!(). - DLAB was not modelled, so the 8250 driver's baud divisor writes were printed as characters. This put stray bytes on the terminal every time the guest called tcsetattr, which vi does on entry and exit. - pc and mepc arithmetic wraps, and MRET masks MPP to two bits so a stale mstatus bit cannot leave the CPU in the reserved privilege mode. Co-authored-by: Cursor <cursoragent@cursor.com>
`rust-toolchain` pinned 1.80.0, but `Cargo.lock` is gitignored so CI always resolves the latest clap, which now needs edition2024 and fails to even parse its manifest on 1.80.0. Bumping the pin exposes a second problem: the workspace has a local crate literally named `core`, so clap's derive expansion of `core::env!` resolved to it instead of the built-in crate. Alias the dependency as `r2_core` in `app`, which is the only crate using derive macros. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Running
viinside the guest killed the emulator, while non-interactive commands such aslsworked fine.The host terminal was left in canonical mode for the entire session. That has two consequences:
vinever saw the individual keystrokes it waits for.Ctrl-Cwas interpreted by the host line discipline and raisedSIGINTagainst the emulator process itself, so the very first attempt to interrupt something inside the guest terminated the emulator.While tracking this down, a few unrelated correctness bugs in the CPU and the bus turned up, and they are fixed here too.
Changes
Terminal handling
devices::terminal::RawMode, a guard that puts the host terminal into raw mode viacfmakerawand restores the savedtermioson drop. It also installs a panic hook so an unexpected panic does not leave the user with a broken terminal.Uartowns aRawMode, so raw mode is active exactly while the guest owns the serial port.Ctrl-Cnow belongs to the guest, add aCtrl-Aescape prefix:Ctrl-A xquits the emulator cleanly (restoring the terminal first) andCtrl-A Ctrl-Asends a literalCtrl-Ato the guest.char::from_u32, which mangled anything the guest emitted that was not valid UTF-8 on its own.8250 divisor latch
The UART model ignored the DLAB bit in the LCR. When the guest's 8250 driver programmed the baud rate divisor, those writes to registers 0 and 1 were treated as characters and showed up as stray output on the console. The bus now tracks
lcranddivisorand routes register 0/1 accesses to the divisor latches while DLAB is set.CPU and bus correctness
DIVperformed unsigned division; the operands are now sign-extended before dividing.x[rd], so a load targetingx0could clobber the zero register. They go throughwrite_backnow.LoadAccessFault/StoreAccessFaultso the guest can handle them.InstructionAddressMisalignedinstead ofInstructionAccessFault.pcandmepcarithmetic used plain+/-, which panics on overflow in debug builds. These arewrapping_*now.MRETdid not mask the MPP field, so amstatuswith higher bits set could transition the hart intoPrivilegeMode::Reserved.CI
CI was already red on
main.rust-toolchainpinned 1.80.0 whileCargo.lockis gitignored, so every run resolved the newest clap, which requiresedition2024and cannot even be parsed by Cargo 1.80.0. The pin moves to 1.96.1.That bump exposed a second issue: the workspace contains a local crate literally named
core, which shadows the built-incorecrate inside macro expansions — clap's derive emitscore::env!and failed to resolve it. The dependency is aliased asr2_coreinapp, the only crate that uses derive macros.Testing
cargo checkandcargo build -p app(the two CI steps) pass locally on 1.96.1.fixtures/linux.binunder a pty, logged in, ranls /and confirmed the colour output is clean, then openedvi /etc/passwdand confirmed the full-screen UI draws correctly and:q!returns to the shell.Ctrl-A xexits and leaves the host terminal in its original state.Note
One issue found during the investigation is deliberately left alone: after
cating certain files the console can be flooded with NULs. This traces back toftruncate64in the guest image misreading its 64-bit argument and producing a file of size22 << 32. The same behaviour reproduces on the referencemini-rv32imaemulator, so it is an image bug rather than an emulator bug.