1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
//! The interrupt module contains all the interrupt kinds and the function to handle interrupts.
use crate::{
cpu::{Cpu, Mode},
csr::*,
};
/// All the interrupt kinds.
#[derive(Debug)]
pub enum Interrupt {
UserSoftwareInterrupt,
SupervisorSoftwareInterrupt,
MachineSoftwareInterrupt,
UserTimerInterrupt,
SupervisorTimerInterrupt,
MachineTimerInterrupt,
UserExternalInterrupt,
SupervisorExternalInterrupt,
MachineExternalInterrupt,
}
impl Interrupt {
fn exception_code(&self) -> u64 {
match self {
Interrupt::UserSoftwareInterrupt => 0,
Interrupt::SupervisorSoftwareInterrupt => 1,
Interrupt::MachineSoftwareInterrupt => 3,
Interrupt::UserTimerInterrupt => 4,
Interrupt::SupervisorTimerInterrupt => 5,
Interrupt::MachineTimerInterrupt => 7,
Interrupt::UserExternalInterrupt => 8,
Interrupt::SupervisorExternalInterrupt => 9,
Interrupt::MachineExternalInterrupt => 11,
}
}
/// Update CSRs and the program counter depending on an interrupt.
pub fn take_trap(&self, cpu: &mut Cpu) {
// 1.2 Privilege Levels
// "Traps that increase privilege level are termed vertical traps, while traps that remain
// at the same privilege level are termed horizontal traps."
cpu.idle = false;
let exception_pc = cpu.pc;
cpu.prev_mode = cpu.mode;
let cause = self.exception_code();
// 3.1.8 Machine Trap Delegation Registers (medeleg and mideleg)
// "By default, all traps at any privilege level are handled in machine mode To increase
// performance, implementations can provide individual read/write bits within medeleg and
// mideleg to indicate that certain exceptions and interrupts should be processed directly
// by a lower privilege level."
//
// "mideleg holds trap delegation bits for individual interrupts, with the layout of bits
// matching those in the mip register (i.e., STIP interrupt delegation control is located
// in bit 5)."
// TODO: Why should a M-mode timer interrupt be taken in M-mode?
if cpu.mode <= Mode::Supervisor
&& (((cpu.state.read(MIDELEG) & 0xffff) >> cause) & 1) == 1
&& cause != Interrupt::MachineTimerInterrupt.exception_code()
{
// Handle the trap in S-mode.
cpu.mode = Mode::Supervisor;
// Set the program counter to the supervisor trap-handler base address (stvec)
// depending on the mode.
let vector = match cpu.state.read_bit(STVEC, 0) {
1 => 4 * cause, // vectored mode
_ => 0, // direct mode
};
cpu.pc = ((cpu.state.read(STVEC) & !1) + vector) as u64;
// 4.1.9 Supervisor Exception Program Counter (sepc)
// "The low bit of sepc (sepc[0]) is always zero."
// "When a trap is taken into S-mode, sepc is written with the virtual address of
// the instruction that was interrupted or that encountered the exception.
// Otherwise, sepc is never written by the implementation, though it may be
// explicitly written by software."
cpu.state.write(SEPC, exception_pc & !1);
// 4.1.10 Supervisor Cause Register (scause)
// "When a trap is taken into S-mode, scause is written with a code indicating
// the event that caused the trap. Otherwise, scause is never written by the
// implementation, though it may be explicitly written by software."
cpu.state.write(SCAUSE, 1 << 63 | cause);
// 4.1.11 Supervisor Trap Value (stval) Register
// "When a trap is taken into S-mode, stval is written with exception-specific
// information to assist software in handling the trap. Otherwise, stval is never
// written by the implementation, though it may be explicitly written by software."
// "When a hardware breakpoint is triggered, or an instruction-fetch, load, or
// store address-misaligned, access, or page-fault exception occurs, stval is
// written with the faulting virtual address. On an illegal instruction trap,
// stval may be written with the first XLEN or ILEN bits of the faulting
// instruction as described below. For other exceptions, stval is set to zero."
cpu.state.write(STVAL, 0);
// Set a privious interrupt-enable bit for supervisor mode (SPIE, 5) to the value
// of a global interrupt-enable bit for supervisor mode (SIE, 1).
cpu.state
.write_bit(SSTATUS, 5, cpu.state.read_bit(SSTATUS, 1));
// Set a global interrupt-enable bit for supervisor mode (SIE, 1) to 0.
cpu.state.write_bit(SSTATUS, 1, 0);
// 4.1.1 Supervisor Status Register (sstatus)
// "When a trap is taken, SPP is set to 0 if the trap originated from user mode, or
// 1 otherwise."
match cpu.prev_mode {
Mode::User => cpu.state.write_bit(SSTATUS, 8, 0),
_ => cpu.state.write_bit(SSTATUS, 8, 1),
}
} else {
// Handle the trap in M-mode.
cpu.mode = Mode::Machine;
// Set the program counter to the machine trap-handler base address (mtvec)
// depending on the mode.
let vector = match cpu.state.read_bit(MTVEC, 0) {
1 => 4 * cause, // vectored mode
_ => 0, // direct mode
};
cpu.pc = ((cpu.state.read(MTVEC) & !1) + vector) as u64;
// 3.1.15 Machine Exception Program Counter (mepc)
// "The low bit of mepc (mepc[0]) is always zero."
// "When a trap is taken into M-mode, mepc is written with the virtual address of
// the instruction that was interrupted or that encountered the exception.
// Otherwise, mepc is never written by the implementation, though it may be
// explicitly written by software."
cpu.state.write(MEPC, exception_pc & !1);
// 3.1.16 Machine Cause Register (mcause)
// "When a trap is taken into M-mode, mcause is written with a code indicating
// the event that caused the trap. Otherwise, mcause is never written by the
// implementation, though it may be explicitly written by software."
cpu.state.write(MCAUSE, 1 << 63 | cause);
// 3.1.17 Machine Trap Value (mtval) Register
// "When a trap is taken into M-mode, mtval is either set to zero or written with
// exception-specific information to assist software in handling the trap.
// Otherwise, mtval is never written by the implementation, though it may be
// explicitly written by software."
// "When a hardware breakpoint is triggered, or an instruction-fetch, load, or
// store address-misaligned, access, or page-fault exception occurs, mtval is
// written with the faulting virtual address. On an illegal instruction trap,
// mtval may be written with the first XLEN or ILEN bits of the faulting
// instruction as described below. For other traps, mtval is set to zero."
cpu.state.write(MTVAL, 0);
// Set a privious interrupt-enable bit for supervisor mode (MPIE, 7) to the value
// of a global interrupt-enable bit for supervisor mode (MIE, 3).
cpu.state
.write_bit(MSTATUS, 7, cpu.state.read_bit(MSTATUS, 3));
// Set a global interrupt-enable bit for supervisor mode (MIE, 3) to 0.
cpu.state.write_bit(MSTATUS, 3, 0);
// When a trap is taken from privilege mode y into privilege mode x, xPIE is set
// to the value of x IE; x IE is set to 0; and xPP is set to y.
match cpu.prev_mode {
Mode::User => cpu.state.write_bits(MSTATUS, 11..13, 0b00),
Mode::Supervisor => cpu.state.write_bits(MSTATUS, 11..13, 0b01),
Mode::Machine => cpu.state.write_bits(MSTATUS, 11..13, 0b11),
_ => panic!("previous privilege mode is invalid"),
}
}
}
}