1*** Variables ***
2${a0}                               0xa
3${a1}                               0xb
4${a2}                               0xc
5${PRIV_ALL}                         7
6${PRIV_READWRITE}                   3
7${PRIV_WRITE_ONLY}                  2
8${PRIV_EXEC_ONLY}                   4
9${PRIV_NONE}                        0
10${START_PC}                         0x0
11${-1u64}                            0xFFFFFFFFFFFFFFFF
12
13*** Keywords ***
14Create Platform
15    Execute Command                 using sysbus
16    Execute Command                 mach create "risc-v"
17
18    Execute Command                 machine LoadPlatformDescriptionFromString "clint: IRQControllers.CoreLevelInterruptor @ sysbus 0x44000000 { frequency: 66000000 }"
19    Execute Command                 machine LoadPlatformDescriptionFromString "cpu: CPU.RiscV32 @ sysbus { timeProvider: clint; cpuType: \\"rv32gc\\" }"
20    Execute Command                 machine LoadPlatformDescriptionFromString "mem: Memory.MappedMemory @ sysbus 0x0 { size: 0x100000 }"
21
22    Execute Command                 cpu PC ${START_PC}
23
24Expect Value Read From Address
25    [Arguments]                     ${address}  ${value}
26    Execute Command                 cpu PC ${START_PC}
27    Execute Command                 cpu SetRegister ${a0} ${address}
28    Execute Command                 sysbus WriteDoubleWord ${START_PC} 0x52583  # lw a1, 0(a0)
29
30    Execute Command                 cpu Step
31
32    ${val}=                         Execute Command   cpu GetRegister ${a1}
33    Should Be Equal As Integers     ${val}  ${value}
34
35Write Range With Doublewords
36    [Arguments]                     ${start_addr}  ${length}  ${value}
37    ${end_addr}=                    Evaluate  ${start_addr}+${length}
38    ${bytesPerDoubleword}=          Evaluate  4
39    FOR   ${addr}  IN RANGE         ${start_addr}  ${end_addr}  ${bytesPerDoubleWord}
40        Execute Command             sysbus WriteDoubleWord ${addr} ${value}
41    END
42
43Define Window Using CPU API
44    [Arguments]                     ${start_addr}  ${end_addr}  ${addend}  ${priv}
45    Execute Command                 cpu EnableExternalWindowMmu true
46    ${window_index}=                Execute Command  cpu AcquireExternalMmuWindow ${PRIV_ALL}
47    Execute Command                 cpu SetMmuWindowStart ${window_index} ${start_addr}
48    Execute Command                 cpu SetMmuWindowEnd ${window_index} ${end_addr}
49    Execute Command                 cpu SetMmuWindowAddend ${window_index} ${addend}
50    Execute Command                 cpu SetMmuWindowPrivileges ${window_index} ${priv}
51    Return From Keyword             ${window_index}
52
53Define Typed Window Using CPU API
54    [Arguments]                     ${start_addr}  ${end_addr}  ${addend}  ${priv}  ${type}
55    Execute Command                 cpu EnableExternalWindowMmu true
56    ${window_index}=                Execute Command  cpu AcquireExternalMmuWindow ${type}
57    Execute Command                 cpu SetMmuWindowStart ${window_index} ${start_addr}
58    Execute Command                 cpu SetMmuWindowEnd ${window_index} ${end_addr}
59    Execute Command                 cpu SetMmuWindowAddend ${window_index} ${addend}
60    Execute Command                 cpu SetMmuWindowPrivileges ${window_index} ${priv}
61    Return From Keyword             ${window_index}
62
63Define Window In Peripheral
64    [Arguments]                     ${periph}  ${window_index}  ${start_addr}  ${end_addr}  ${addend}  ${priv}
65    ${offset}=                      Evaluate  4 * ${window_index}
66    ${start_register}=              Evaluate  0x0 + ${offset}
67    ${end_register}=                Evaluate  0x400 + ${offset}
68    ${addend_register}=             Evaluate  0x800 + ${offset}
69    ${priv_register}=               Evaluate  0xC00 + ${offset}
70    Execute Command                 ${periph} WriteDoubleWord ${start_register} ${start_addr}
71    Execute Command                 ${periph} WriteDoubleWord ${end_register} ${end_addr}
72    Execute Command                 ${periph} WriteDoubleWord ${addend_register} ${addend}
73    Execute Command                 ${periph} WriteDoubleWord ${priv_register} ${priv}
74
75*** Test Cases ***
76Setting MMU Window Parameters Before Enabling Throws
77    Create Platform
78    Run Keyword And Expect Error    KeywordException: *There was an error executing command 'cpu SetMmuWindowAddend 0 256'External MMU not enabled*
79    ...  Execute Command                 cpu SetMmuWindowAddend 0 0x100
80
81Using Too High MMU Window Index Throws
82    Create Platform
83    Execute Command                 cpu EnableExternalWindowMmu true
84    Run Keyword And Expect Error    KeywordException: *There was an error executing command 'cpu SetMmuWindowAddend 256 256'Window index to high, maximum number: 255, got 256*
85    ...  Execute Command            cpu SetMmuWindowAddend 256 0x100
86
87Read/Write From Address Outside The Defined MMU Windows Throws
88    Create Platform
89    Execute Command                 cpu EnableExternalWindowMmu true
90    Create Log Tester               0
91    Execute Command                 sysbus WriteWord 0x2000 0x1234
92    Expect Value Read From Address  0x10000  0x0
93    Wait For Log Entry              MMU fault - the address 0x0 is not specified in any of the existing ranges
94
95Window Can Handle Only One Type Of Access
96    Create Platform
97    Execute Command                 cpu EnableExternalWindowMmu true
98    Create Log Tester               0
99    Define Typed Window Using CPU API    0x0000  0x1000  0x0  ${PRIV_EXEC_ONLY}  ${PRIV_EXEC_ONLY}
100    Define Typed Window Using CPU API    0x0000  0x1000  0x1000  ${PRIV_READWRITE}  ${PRIV_READWRITE}
101    Execute Command                 sysbus WriteWord 0x1000 0x0124
102    Expect Value Read From Address  0x0  0x0124
103
104Is Able To Retrive The Properties
105    Create Platform
106
107    ${window}=                      Define Window Using CPU API  0x1000  0x2000  0x100  ${PRIV_ALL}
108    ${range_start}=                 Execute Command   cpu GetMmuWindowStart ${window}
109    ${range_end}=                   Execute Command   cpu GetMmuWindowEnd ${window}
110    ${addend}=                      Execute Command   cpu GetMmuWindowAddend ${window}
111    ${priv}=                        Execute Command   cpu GetMmuWindowPrivileges ${window}
112    Should Be Equal As Integers     0x1000  ${range_start}
113    Should Be Equal As Integers     0x2000  ${range_end}
114    Should Be Equal As Integers     0x100   ${addend}
115    Should Be Equal As Integers     ${PRIV_ALL}  ${priv}
116
117Can Reset The Windows
118    Create Platform
119
120    ${window1}=                     Define Window Using CPU API     0x1000  0x2000  0x100  ${PRIV_ALL}
121    ${window2}=                     Define Window Using CPU API     0x2000  0x3000  0x100  ${PRIV_ALL}
122    ${window3}=                     Define Window Using CPU API     0x3000  0x4000  0x100  ${PRIV_ALL}
123    Execute Command                 cpu ResetMmuWindow ${window2}
124
125    # Removed window
126    ${range_start}=                 Execute Command    cpu GetMmuWindowStart ${window2}
127    ${range_end}=                   Execute Command    cpu GetMmuWindowEnd ${window2}
128    ${addend}=                      Execute Command    cpu GetMmuWindowAddend ${window2}
129    ${priv}=                        Execute Command    cpu GetMmuWindowPrivileges ${window2}
130    Should Be Equal As Integers     0  ${range_start}  "Range start incorrect"
131    Should Be Equal As Integers     0  ${range_end}  "Range end incorrect"
132    Should Be Equal As Integers     0  ${addend}  "Range adden incorrect"
133    Should Be Equal As Integers     0  ${priv}  "Range privileges incorrect"
134
135    # Surrounding windows should remain untouched
136    ${range_start}=                 Execute Command    cpu GetMmuWindowStart ${window1}
137    ${range_end}=                   Execute Command    cpu GetMmuWindowEnd ${window1}
138    ${addend}=                      Execute Command    cpu GetMmuWindowAddend ${window1}
139    ${priv}=                        Execute Command    cpu GetMmuWindowPrivileges ${window1}
140    Should Be Equal As Integers     0x1000  ${range_start}
141    Should Be Equal As Integers     0x2000  ${range_end}
142    Should Be Equal As Integers     0x100  ${addend}
143    Should Be Equal As Integers     ${PRIV_ALL}  ${priv}
144
145    ${range_start}=                 Execute Command    cpu GetMmuWindowStart ${window3}
146    ${range_end}=                   Execute Command    cpu GetMmuWindowEnd ${window3}
147    ${addend}=                      Execute Command    cpu GetMmuWindowAddend ${window3}
148    ${priv}=                        Execute Command    cpu GetMmuWindowPrivileges ${window3}
149    Should Be Equal As Integers     0x3000  ${range_start}
150    Should Be Equal As Integers     0x4000  ${range_end}
151    Should Be Equal As Integers     0x100  ${addend}
152    Should Be Equal As Integers     ${PRIV_ALL}  ${priv}
153
154Read/Write Uses The Proper Addend
155    Create Platform
156    Define Window Using CPU API     0x0000  0x1000  0x0  ${PRIV_ALL}
157    Define Window Using CPU API     0x10000  0x11000  0x1000  ${PRIV_ALL}
158    Execute Command                 sysbus WriteWord 0x10000 0xFFFF
159    Execute Command                 sysbus WriteWord 0x11000 0x0124
160    Expect Value Read From Address  0x10000  0x0124
161
162Throws On Ranges Unaligned To The Page Size
163    Create Platform
164    Run Keyword And Expect Error    CpuAbortException: MMU ranges must be aligned to the page size (0x1000), the address 0x100 is not*
165    ...  Define Window Using CPU API     0x100  0x1100  0x1000  ${PRIV_ALL}
166
167Permissions Are Respected
168    Create Platform
169    Create Log Tester               0
170    Execute Command                 logLevel -1
171    Define Window Using CPU API     0x0000  0x1000  0x0  ${PRIV_ALL}
172    Define Window Using CPU API     0x1000  0x2000  0x100  ${PRIV_EXEC_ONLY}
173    Write Range With Doublewords    0x1000  0x1FF  0xFFFFFFFF
174    Expect Value Read From Address  0x1100  0x0
175    Wait For Log Entry              External MMU fault at 0x1100
176
177Fault Callback Works Only When Enabled
178    Create Platform
179    Create Log Tester               0.0001
180    Execute Command                 logLevel 0 cpu
181    Expect Value Read From Address  0x1100  0x0
182    Should Not Be In Log            External MMU fault at 0x1100
183
184Peripheral Can Be Attached To The Sysbus
185    Create Platform
186    Execute Command                 machine LoadPlatformDescriptionFromString "mmu1: Miscellaneous.ExternalWindowMMU @ sysbus 0x47000000 {cpu: cpu; startAddress: 0x0; windowSize: 0x1000; numberOfWindows: 4}"
187    Define Window In Peripheral     mmu1  0  0x0  0x1000  0x0  ${PRIV_EXEC_ONLY}
188    Provides                        SingleMMU
189
190Peripheral Can Be Configured Using The Registers Inteface
191    Requires                        SingleMMU
192    Define Window In Peripheral     mmu1  1  0x10000  0x11000  0x1000  ${PRIV_ALL}
193    Execute Command                 sysbus WriteWord 0x10000 0xFFFF
194    Execute Command                 sysbus WriteWord 0x11000 0x0124
195    Expect Value Read From Address  0x10000  0x0124
196
197CPU Can Have Two MMUs
198    Create Platform
199    Execute Command                 machine LoadPlatformDescriptionFromString "mmu1: Miscellaneous.ExternalWindowMMU @ sysbus 0x47000000 {cpu: cpu; startAddress: 0x0; windowSize: 0x1000; numberOfWindows: 2}"
200    Execute Command                 machine LoadPlatformDescriptionFromString "mmu2: Miscellaneous.ExternalWindowMMU @ sysbus 0x47001000 {cpu: cpu; startAddress: 0x1000; windowSize: 0x1000; numberOfWindows: 2}"
201    Define Window In Peripheral     mmu1  0  0x0  0x1000  0x0  ${PRIV_EXEC_ONLY}
202    Define Window In Peripheral     mmu2  0  0x0  0x1000  0x0  ${PRIV_EXEC_ONLY}
203    Provides                        TwoMmus
204
205Peripheral Throws Fault On Illegal Data Access
206    Requires                        SingleMMU
207    Create Log Tester               0
208    Define Window In Peripheral     mmu1  1  0x0000  0x1000  0x0000  ${PRIV_ALL}
209    Define Window In Peripheral     mmu1  2  0x1000  0x2000  0x0000  ${PRIV_NONE}
210    Execute Command                 logLevel 0 mmu1
211
212    Expect Value Read From Address  0x1100  0x0
213    Wait For Log Entry              mmu1: MMU fault occured
214
215Peripheral Does Not Throw When no_page_fault Is Set
216    Requires                        SingleMMU
217    Create Log Tester               0
218    Define Window In Peripheral     mmu1  1  0x0000  0x1000  0x0000  ${PRIV_ALL}
219    Define Window In Peripheral     mmu1  2  0x1000  0x2000  0x0000  ${PRIV_NONE}
220    Execute Command                 logLevel 0 mmu1
221
222    # cpu TranslateAddress uses the cpu_handle_mmu_fault with no_page_fault set to 1
223    ${returned value}=              Execute Command  cpu TranslateAddress 0x1100 Read
224    Should Be Equal As Integers     ${returned value}    ${-1u64}
225    Should Not Be In Log            mmu1: MMU fault occured
226
227Peripheal Throws On Illegal Instruction Fetch
228    Create Platform
229    Create Log Tester               0
230    Execute Command                 machine LoadPlatformDescriptionFromString "mmu1: Miscellaneous.ExternalWindowMMU @ sysbus 0x47000000 {cpu: cpu; startAddress: 0x0; windowSize: 0x1000; numberOfWindows: 4}"
231    Define Window In Peripheral     mmu1  1  0x0000  0x1000  0x0000  ${PRIV_WRITE_ONLY}
232    Execute Command                 logLevel 0 mmu1
233
234    Expect Value Read From Address  0x2000  0x0
235    Wait For Log Entry              mmu1: MMU fault occured
236
237First MMU Throws On Fault In Its Window
238    Requires                        TwoMmus
239    Create Log Tester               0
240
241    Execute Command                 logLevel 0 mmu1
242    Execute Command                 logLevel 0 mmu2
243
244    Define Window In Peripheral     mmu1  1  0x1000  0x2000  0x0000  ${PRIV_NONE}
245    Execute Command                 sysbus WriteWord 0x1000 0x0124
246    Expect Value Read From Address  0x1000  0x0
247
248    Wait For Log Entry              mmu1: MMU fault occured
249    Should Not Be In Log            mmu2: MMU fault occured
250
251Second MMU Throws On Fault In Its Window
252    Requires                        TwoMmus
253    Create Log Tester               0
254
255    Execute Command                 logLevel 0 mmu1
256    Execute Command                 logLevel 0 mmu2
257
258    Define Window In Peripheral     mmu2  1  0x2000  0x3000  0x1000  ${PRIV_WRITE_ONLY}
259    Execute Command                 sysbus WriteWord 0x2000 0x0124
260    Expect Value Read From Address  0x2000  0x0
261
262    Wait For Log Entry              mmu2: MMU fault occured
263    Should Not Be In Log            mmu1: MMU fault occured
264
265Execution Stops On Fault
266    Requires                        SingleMMU
267    Write Range With Doublewords    0x0  0x1000  0x13 # Nop sled on whole page
268    Execute Command                 cpu SetRegister ${a0} 0x1C
269    Execute Command                 sysbus WriteDoubleWord 0x1C 0x52583  # lw a1, 0(a0)
270    Execute Command                 sysbus WriteDoubleWord 0x20 0xd02503 # lw a2, 0(zero)
271    Start Emulation
272    Sleep 							0.1
273
274    # Assert we are halted on the faulting insn
275    ${pc}=                          Execute Command   cpu PC
276    Should Be Equal As integers     ${pc}  0x1C
277    # Assert that the second insn was not executed
278    ${val}=                         Execute Command   cpu GetRegister ${a2}
279    Should Be Equal As Integers     ${val}  0x0
280
281Throws When Window Is Out Of Range
282    Requires                        SingleMMU
283    Run Keyword And Expect Error    *Address is outside of the possible range.*
284    ...                             Define Typed Window Using CPU API  0x0  0x100001000  0x0  ${PRIV_ALL}  ${PRIV_ALL}
285
286Works With The Last Page Of Memory
287    Requires                        SingleMMU
288    Define Typed Window Using CPU API  0x0  0x100000000  0x0  ${PRIV_EXEC_ONLY}  ${PRIV_EXEC_ONLY}
289    Execute Command                 sysbus ReadWord 0xFFFFFFFF
290