[#53750] tests: sysbus: Test accessing locked mapped memory from CPU
diff --git a/tests/unit-tests/sysbus.robot b/tests/unit-tests/sysbus.robot
index f5bdc74..fcf8869 100644
--- a/tests/unit-tests/sysbus.robot
+++ b/tests/unit-tests/sysbus.robot
@@ -15,17 +15,62 @@
 ...                            ${SPACE*4}size: 0x500                                                                                   ${\n}
 ...                            """
 
+${max_32bit_addr}              0xFFFFFFFF
+
 *** Keywords ***
+Create Machine With CPU And Two MappedMemory Peripherals
+    Execute Command            using sysbus
+    Execute Command            mach create
+
+    # ARMv7A is used only because it can be created without any additional peripherals.
+    # Locking can be used with all CPUs.
+    Execute Command            machine LoadPlatformDescriptionFromString "cpu: CPU.ARMv7A @ sysbus { cpuType: \\"cortex-a9\\"}"
+    Execute Command            machine LoadPlatformDescriptionFromString "mem1: Memory.MappedMemory @ sysbus 0x10000 { size: 0x10000 }"
+    Execute Command            machine LoadPlatformDescriptionFromString "mem2: Memory.MappedMemory @ sysbus 0x80000 { size: 0x10000 }"
+
+Get ${peripheral} Size, Address And Range
+    ${size}=    Execute Command  ${peripheral} Size
+    ${size}=    Strip String     ${size}
+    ${ranges}=  Execute Command  sysbus GetRegistrationPoints ${peripheral}
+
+    # Let's make sure there's only one range.
+    ${count}=  Evaluate          """${ranges}""".count('<')
+    Should Be Equal As Integers  1  ${count}
+
+    ${range}  ${address}=  Evaluate
+    ...    [ re.search('<(0x[0-9A-F]*), .*>', """${ranges}""").group(i) for i in range(2) ]
+    ...    modules=re
+
+    [Return]  ${size}  ${address}  ${range}
+
+${lock_or_unlock:(Lock|Unlock)} Address Range From ${start} To ${end}
+    ${range}=     Evaluate   f"<{ hex(${start}) }, { hex(${end}) }>"
+    ${lock_or_unlock} Address Range ${range}
+
+${lock_or_unlock:(Lock|Unlock)} Address Range ${range}
+    ${set_lock}=  Evaluate   '${lock_or_unlock}' == 'Lock'
+    Execute Command          sysbus SetAddressRangeLocked ${range} ${set_lock}
+
+Range From ${start} To ${end} Should Be Accessible
+    ${range}=       Evaluate         f"<{ hex(${start}) }, { hex(${end}) }>"
+    ${locked_str}=  Execute Command  sysbus IsAddressRangeLocked ${range}
+    Should Start With                ${locked_str}  False
+
+No Blocked Access Should Be In Log
+    Should Not Be In Log     Tried to (read|write) .* which is inside a locked address range  treatAsRegex=True
+
+Blocked ${access_size}B Read From ${address} Should Be In Log
+    ${eval_addr}=  Evaluate  '0x' + hex(${address}).upper()[2:]
+    Wait For Log Entry       Tried to read ${access_size} bytes at ${eval_addr} which is inside a locked address range, returning 0
+
 Read From Sysbus And Check If Blocked
     [Arguments]  ${address}  ${expected_value}=0x0  ${should_be_blocked}=True  ${access_type}=Byte  ${access_size}=1  ${cpu_context}=
 
-    ${blocked_read_log}=           Set Variable    Tried to read ${access_size} bytes at ${address} which is inside a locked address range, returning 0
-
-    ${read_value}=                 Execute Command    sysbus Read${access_type} ${address} ${cpu_context}
+    ${read_value}=  Execute Command  sysbus Read${access_type} ${address} ${cpu_context}
     IF    ${should_be_blocked}
-        Wait For Log Entry         ${blocked_read_log}
+        Blocked ${access_size}B Read From ${address} Should Be In Log
     ELSE
-        Should Not Be In Log       ${blocked_read_log}
+        No Blocked Access Should Be In Log
     END
     Should Be Equal As Integers    ${read_value}  ${expected_value}  base=16
 
@@ -45,15 +90,18 @@
     [Arguments]  ${address}  ${expected_value}  ${cpu_context}=
     Read From Sysbus And Check If Blocked    ${address}  ${expected_value}  should_be_blocked=False  access_type=QuadWord  access_size=8  cpu_context=${cpu_context}
 
+Blocked ${access_size}B Write${access_type} Of ${value} To ${address} Should Be In Log
+    ${eval_addr}=  Evaluate  '0x' + hex(${address}).upper()[2:]
+    Wait For Log Entry       Tried to write ${access_size} bytes (${value}) at ${eval_addr} which is inside a locked address range, write ignored
+
 Write To Sysbus And Check If Blocked
     [Arguments]  ${address}  ${value}  ${should_be_blocked}=True  ${access_type}=Byte  ${access_size}=1  ${cpu_context}=
 
-    Execute Command             sysbus Write${access_type} ${address} ${value} ${cpu_context}
-    ${blocked_write_log}=       Set Variable  Tried to write ${access_size} bytes (${value}) at ${address} which is inside a locked address range, write ignored
+    Execute Command  sysbus Write${access_type} ${address} ${value} ${cpu_context}
     IF    ${should_be_blocked}
-        Wait For Log Entry      ${blocked_write_log}
+        Blocked ${access_size}B Write Of ${value} To ${address} Should Be In Log
     ELSE
-        Should Not Be In Log    ${blocked_write_log}
+        No Blocked Access Should Be In Log
     END
 
 Should Block Write Byte
@@ -264,6 +312,128 @@
     Wait For Log Entry         mockCpu0: CPU abort \[PC=0x4000\]: Trying to execute code from disabled or locked memory at 0x00004000  timeout=10
     Wait For Log Entry         mockCpu1: CPU abort \[PC=0x6000\]: Trying to execute code outside RAM or ROM at 0x00006000  timeout=10
 
+Locked MappedMemory Should Not Be Accessible From CPU
+    Create Machine With CPU And Two MappedMemory Peripherals
+    ${flash}=  Set Variable    mem1
+    ${ram}=    Set Variable    mem2
+
+    ${flash_size}  ${flash_addr}  ${flash_range}=  Get ${flash} Size, Address And Range
+    ${ram_size}    ${ram_addr}    ${ram_range}=    Get ${ram} Size, Address And Range
+
+    Execute Command            cpu ExecutionMode SingleStep
+    Execute Command            cpu PC ${ram_addr}
+    Create Log Tester          0
+
+    # With flash locked, the loads from [r3] and stores to [r3] should be blocked.
+    ${result_addr}=  Evaluate  hex(${flash_addr} + 0x1000)
+    Execute Command            cpu SetRegisterUnsafe 3 ${result_addr}
+
+    Execute Command            ${ram} WriteDoubleWord 0x00 0xe59f2028  # ldr   r2, [pc, #40] // =0x11111111
+    Execute Command            ${ram} WriteDoubleWord 0x04 0xe5832000  # str   r2, [r3]
+    Execute Command            ${ram} WriteDoubleWord 0x30 0x11111111  # this will be loaded by LDR instruction above
+
+    # Ranges have to fully contain all MappedMemory peripherals registered in the given range.
+    # Here we lock whole sysbus and then unlock ram.
+    Lock Address Range From 0x0 To ${max_32bit_addr}
+    Unlock Address Range ${ram_range}
+    Execute Command            cpu Step 2
+
+    Blocked 4B Write Of 0x11111111 To ${result_addr} Should Be In Log
+
+    Execute Command            ${ram} WriteDoubleWord 0x08 0xe59f2024  # ldr   r2, [pc, #34] // =0x22222222
+    Execute Command            ${ram} WriteDoubleWord 0x0c 0xe5832000  # str   r2, [r3]
+    Execute Command            ${ram} WriteDoubleWord 0x10 0xe3032333  # movw  r2, #0x3333
+    Execute Command            ${ram} WriteDoubleWord 0x14 0xe1c320b1  # strh  r2, [r3, #1]
+    Execute Command            ${ram} WriteDoubleWord 0x34 0x22222222  # this will be loaded by LDR instruction above
+
+    Unlock Address Range ${flash_range}
+    Execute Command            cpu Step 4
+
+    No Blocked Access Should Be In Log
+
+    Execute Command            ${ram} WriteDoubleWord 0x18 0xe3a02044  # mov   r2, #0x44
+    Execute Command            ${ram} WriteDoubleWord 0x1c 0xe5c32001  # strb  r2, [r3, #1]
+    Execute Command            ${ram} WriteDoubleWord 0x20 0xe5932000  # ldr   r2, [r3]
+
+    # Lock flash and some memory around it.
+    Lock Address Range From ${flash_addr}-${flash_size} To ${flash_addr}+${flash_size}*2
+    Execute Command            cpu Step 3
+
+    Blocked 1B Write Of 0x44 To ${result_addr}+1 Should Be In Log
+    Blocked 4B Read From ${result_addr} Should Be In Log
+
+    Execute Command            ${ram} WriteDoubleWord 0x24 0xe3a02055  # mov   r2, #0x55
+    Execute Command            ${ram} WriteDoubleWord 0x28 0xe5c32002  # strb  r2, [r3, #2]
+    Execute Command            ${ram} WriteDoubleWord 0x2c 0xe5932000  # ldr   r2, [r3]
+
+    Unlock Address Range From 0x0 To ${max_32bit_addr}
+    Execute Command            cpu Step 3
+
+    No Blocked Access Should Be In Log
+
+    ${res}=  Execute Command   sysbus ReadDoubleWord ${result_addr}
+    Should Be True             """${res}""".strip() == '0x22553322'
+
+Partial MappedMemory Locking Should Not Be Allowed With ICPUWithMappedMemory
+    # CPU is important; partial MappedMemory locking isn't allowed only with ICPUWithMappedMemory.
+    Create Machine With CPU And Two MappedMemory Peripherals
+
+    ${mem1_size}  ${mem1_addr}  ${mem1_range}=  Get mem1 Size, Address And Range
+    ${mem2_size}  ${mem2_addr}  ${mem2_range}=  Get mem2 Size, Address And Range
+    ${mem2_end}=  Evaluate        hex(${mem2_addr} + ${mem2_size} - 1)
+
+    ${error}=     Set Variable    Mapped peripherals registered at the given range * have to be fully included:
+    ${mem1_reg}=  Set Variable    \n\* machine-0.mem1 registered at ${mem1_range}
+    ${mem2_reg}=  Set Variable    \n\* machine-0.mem2 registered at ${mem2_range}
+
+    # Test partial locking of one or both MappedMemory peripherals.
+
+    Run Keyword And Expect Error  *${error}${mem1_reg}*
+    ...  Lock Address Range From 0x0 To ${mem1_addr}+0x10
+
+    Run Keyword And Expect Error  *${error}${mem1_reg}*
+    ...  Lock Address Range From ${mem1_addr}+0x10 To ${max_32bit_addr}
+
+    Run Keyword And Expect Error  *${error}${mem2_reg}*
+    ...  Lock Address Range From 0x0 To ${mem2_addr}+0x10
+
+    Run Keyword And Expect Error  *${error}${mem1_reg}${mem2_reg}*
+    ...  Lock Address Range From ${mem1_addr}+0x10 To ${mem2_addr}+0x10
+
+    # Make sure no range within 32-bit address space has been locked.
+    Range From 0x0 To ${max_32bit_addr} Should Be Accessible
+
+    # Lock mem1, mem2 and the address space in between.
+    Execute Command               sysbus SetAddressRangeLocked <${mem1_addr}, ${mem2_end}> true
+
+    # Test partial unlocking of one or both MappedMemory peripherals.
+
+    Run Keyword And Expect Error  *${error}${mem1_reg}*
+    ...  Unlock Address Range From 0x0 To ${mem1_addr}+0x10
+
+    Run Keyword And Expect Error  *${error}${mem1_reg}*
+    ...  Unlock Address Range From ${mem1_addr}+0x10 To ${max_32bit_addr}
+
+    Run Keyword And Expect Error  *${error}${mem2_reg}*
+    ...  Unlock Address Range From ${mem2_addr}+0x10 To ${max_32bit_addr}
+
+    Run Keyword And Expect Error  *${error}${mem1_reg}${mem2_reg}*
+    ...  Unlock Address Range From ${mem1_addr}+0x10 To ${mem2_addr}+0x10
+
+    # Make sure mem1, mem2 and the address space in between are still locked. Range
+    # is considered locked if the given range contains any locked range which is why
+    # `IsAddressRangeLocked` isn't used. Let's check accessing a byte every 0x8000.
+    @{locked_range_addresses}=  Evaluate
+    ...    [address for address in range(${mem1_addr}, ${mem2_end}, 0x8000)]
+    FOR  ${address}  IN  ${mem1_addr}  @{locked_range_addresses}
+        Log    ${address}
+        Should Block Read Byte    ${address}
+    END
+
+    # Unlock mem1, mem2 and the address space in between and verify there are no locks now.
+    Unlock Address Range <${mem1_addr}, ${mem2_end}>
+    Range From ${mem1_addr} To ${mem2_end} Should Be Accessible
+
 Symbols Should Be Dynamically Loaded and Unloaded On Request
     ${bin}=                        Set Variable  @https://dl.antmicro.com/projects/renode/stm32l07--zephyr-shell_module.elf-s_1195760-e9474da710aca88c89c7bddd362f7adb4b0c4b70
     ${cpu}=                        Set Variable  sysbus.cpu
@@ -299,4 +469,4 @@
     Should Be Equal As Numbers     ${main_symbol_address}  ${main_address_local}
     Run Keyword And Expect Error   *No symbol with name `main` found*
     ...                            Execute Command   sysbus GetSymbolAddress ${main_symbol_name}
-    
\ No newline at end of file
+