[#59143] sysbus: Add range locking
diff --git a/src/Infrastructure b/src/Infrastructure
index c9720d6..f6f5069 160000
--- a/src/Infrastructure
+++ b/src/Infrastructure
@@ -1 +1 @@
-Subproject commit c9720d614dd1368812f8be26d2e2f2b5daee140b
+Subproject commit f6f5069bb95db5f49f9407d13d63acd23dccaba1
diff --git a/tests/unit-tests/sysbus.robot b/tests/unit-tests/sysbus.robot
index bea73ee..f5bdc74 100644
--- a/tests/unit-tests/sysbus.robot
+++ b/tests/unit-tests/sysbus.robot
@@ -6,13 +6,22 @@
 ${init_value_0x140}            0x44
 ${init_value_0x180}            0x55
 
+${per-core-memory}=            SEPARATOR=
+...                            """
+...                            memory2: Memory.ArrayMemory @ { sysbus new Bus.BusPointRegistration { address: 0x200; cpu: mockCpu0 } } ${\n}
+...                            ${SPACE*4}size: 0x100                                                                                   ${\n}
+...                                                                                                                                    ${\n}
+...                            memory3: Memory.ArrayMemory @ { sysbus new Bus.BusPointRegistration { address: 0x250; cpu: mockCpu1 } } ${\n}
+...                            ${SPACE*4}size: 0x500                                                                                   ${\n}
+...                            """
+
 *** Keywords ***
 Read From Sysbus And Check If Blocked
-    [Arguments]  ${address}  ${expected_value}=0x0  ${should_be_blocked}=True  ${access_type}=Byte  ${access_size}=1
+    [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}
+    ${read_value}=                 Execute Command    sysbus Read${access_type} ${address} ${cpu_context}
     IF    ${should_be_blocked}
         Wait For Log Entry         ${blocked_read_log}
     ELSE
@@ -21,25 +30,25 @@
     Should Be Equal As Integers    ${read_value}  ${expected_value}  base=16
 
 Should Block Read Byte
-    [Arguments]  ${address}
-    Read From Sysbus And Check If Blocked    ${address}
+    [Arguments]  ${address}  ${cpu_context}=
+    Read From Sysbus And Check If Blocked    ${address}     cpu_context=${cpu_context}
 
 Should Block Read Quad
-    [Arguments]  ${address}
-    Read From Sysbus And Check If Blocked    ${address}  access_type=QuadWord  access_size=8
+    [Arguments]  ${address}  ${cpu_context}=
+    Read From Sysbus And Check If Blocked    ${address}  access_type=QuadWord  access_size=8  cpu_context=${cpu_context}
 
 Should Read Byte
-    [Arguments]  ${address}  ${expected_value}
-    Read From Sysbus And Check If Blocked    ${address}  ${expected_value}  should_be_blocked=False
+    [Arguments]  ${address}  ${expected_value}  ${cpu_context}=
+    Read From Sysbus And Check If Blocked    ${address}  ${expected_value}  should_be_blocked=False  cpu_context=${cpu_context}
 
 Should Read Quad
-    [Arguments]  ${address}  ${expected_value}
-    Read From Sysbus And Check If Blocked    ${address}  ${expected_value}  should_be_blocked=False  access_type=QuadWord  access_size=8
+    [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}
 
 Write To Sysbus And Check If Blocked
-    [Arguments]  ${address}  ${value}  ${should_be_blocked}=True  ${access_type}=Byte  ${access_size}=1
+    [Arguments]  ${address}  ${value}  ${should_be_blocked}=True  ${access_type}=Byte  ${access_size}=1  ${cpu_context}=
 
-    Execute Command             sysbus Write${access_type} ${address} ${value}
+    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
     IF    ${should_be_blocked}
         Wait For Log Entry      ${blocked_write_log}
@@ -48,20 +57,25 @@
     END
 
 Should Block Write Byte
-    [Arguments]  ${address}  ${value}
-    Write To Sysbus And Check If Blocked    ${address}  ${value}
+    [Arguments]  ${address}  ${value}  ${cpu_context}=
+    Write To Sysbus And Check If Blocked    ${address}  ${value}  cpu_context=${cpu_context}
 
 Should Block Write Quad
-    [Arguments]  ${address}  ${value}
-    Write To Sysbus And Check If Blocked    ${address}  ${value}  access_type=QuadWord  access_size=8
+    [Arguments]  ${address}  ${value}  ${cpu_context}=
+    Write To Sysbus And Check If Blocked    ${address}  ${value}  access_type=QuadWord  access_size=8  cpu_context=${cpu_context}
 
 Should Write Byte
-    [Arguments]  ${address}  ${value}
-    Write To Sysbus And Check If Blocked    ${address}  ${value}  should_be_blocked=False
+    [Arguments]  ${address}  ${value}  ${cpu_context}=
+    Write To Sysbus And Check If Blocked    ${address}  ${value}  should_be_blocked=False  cpu_context=${cpu_context}
 
 Should Write Quad
-    [Arguments]  ${address}  ${value}
-    Write To Sysbus And Check If Blocked    ${address}  ${value}  should_be_blocked=False  access_type=QuadWord  access_size=8
+    [Arguments]  ${address}  ${value}  ${cpu_context}=
+    Write To Sysbus And Check If Blocked    ${address}  ${value}  should_be_blocked=False  access_type=QuadWord  access_size=8  cpu_context=${cpu_context}
+
+Should Write Byte To Non Existing Peripheral
+    [Arguments]  ${address}  ${value}  ${cpu_context}=
+    Execute Command            sysbus WriteByte ${address} ${value} ${cpu_context}
+    Wait For Log Entry         WriteByte to non existing peripheral at ${address}, value ${value}
 
 *** Test Cases ***
 Test Reading Bytes From Locked Sysbus Range
@@ -151,6 +165,105 @@
     Should Read Byte           0x140  ${new_value_0x140}
     Should Read Byte           0x180  ${init_value_0x180}
 
+Test Writing To A Locked Sysbus Range With CPU Context
+    Requires                   sysbus-with-test-values
+    # Architecture doesn't matter, these CPUs are just mocks
+    Execute Command            machine LoadPlatformDescriptionFromString "mockCpu0: CPU.ARMv7A @ sysbus { cpuType: \\"cortex-a9\\" }"
+    Execute Command            machine LoadPlatformDescriptionFromString "mockCpu1: CPU.ARMv7A @ sysbus { cpuType: \\"cortex-a9\\" }"
+
+    Provides                   sysbus-with-mock-cpus
+
+    ${new_value_0x100}=        Set Variable  0x66
+    ${new_value_0x140}=        Set Variable  0x77
+
+    Execute Command            sysbus SetAddressRangeLocked <0x100, 0x1FF> true sysbus.mockCpu0
+    Should Block Write Byte    0x100  ${new_value_0x100}  sysbus.mockCpu0
+    Should Write Byte          0x100  ${new_value_0x100}
+    Should Block Write Byte    0x140  ${new_value_0x100}  sysbus.mockCpu0
+    Should Block Write Byte    0x180  ${new_value_0x100}  sysbus.mockCpu0
+
+    Execute Command            sysbus SetAddressRangeLocked <0x100, 0x1FF> true sysbus.mockCpu1
+    Execute Command            sysbus SetAddressRangeLocked <0x140, 0x140> false sysbus.mockCpu0
+    Should Block Write Byte    0x100  ${new_value_0x140}  sysbus.mockCpu0
+    Should Write Byte          0x140  ${new_value_0x140}  sysbus.mockCpu0
+    Should Block Write Byte    0x180  ${new_value_0x140}  sysbus.mockCpu1
+
+    Execute Command            sysbus SetAddressRangeLocked <0x100, 0x1FF> false sysbus.mockCpu0
+    Should Read Byte           0x100  ${new_value_0x100}
+    Should Read Byte           0x140  ${new_value_0x140}
+    Should Read Byte           0x180  ${init_value_0x180}
+
+Test Writing To A Locked Sysbus Range Registered Per CPU
+    Requires                   sysbus-with-mock-cpus
+
+    Execute Command            machine LoadPlatformDescriptionFromString ${per-core-memory}
+
+    ${new_value_0x200}=        Set Variable  0x99
+
+    Execute Command            sysbus SetAddressRangeLocked <0x200, 0x2FF> true sysbus.mockCpu0
+    Should Block Write Byte    0x200  ${new_value_0x200}  sysbus.mockCpu0
+
+    # For these, the range doesn't exist, as it's local to CPU0 only
+    Should Write Byte To Non Existing Peripheral    0x200  ${new_value_0x200}  sysbus.mockCpu1
+    Should Write Byte To Non Existing Peripheral    0x200  ${new_value_0x200}
+
+    Execute Command            sysbus SetAddressRangeLocked <0x200, 0x2FF> false sysbus.mockCpu0
+
+    # Now the lock is global, but the peripheral is still locally-mapped.
+    # Since locking has precedence over non-existent access, CPUs won't trip on non-existing access
+    # and all writes will fail (lock is on "any" context)
+    Execute Command            sysbus SetAddressRangeLocked <0x200, 0x2FF> true
+
+    Should Block Write Byte    0x200  ${new_value_0x200}
+    Should Block Write Byte    0x200  ${new_value_0x200}  sysbus.mockCpu1
+    Should Block Write Byte    0x200  ${new_value_0x200}  sysbus.mockCpu0
+
+    # The other range should still not be writable by its core
+    Should Block Write Byte    0x250  ${new_value_0x200}  sysbus.mockCpu1
+
+    # Unlock the global range
+    Execute Command            sysbus SetAddressRangeLocked <0x200, 0x2FF> false
+    # Re-lock it but with CPU1 only and repeat the previous steps
+    Execute Command            sysbus SetAddressRangeLocked <0x200, 0x2FF> true sysbus.mockCpu1
+
+    # CPU0 is unaffected by the lock, as is the access without context
+    Should Write Byte          0x200  ${new_value_0x200}
+    Should Block Write Byte    0x200  ${new_value_0x200}  sysbus.mockCpu1
+    Should Write Byte          0x200  ${new_value_0x200}  sysbus.mockCpu0
+
+    # The other range should still not be writable by its core
+    Should Block Write Byte    0x250  ${new_value_0x200}  sysbus.mockCpu1
+
+    Execute Command            sysbus SetAddressRangeLocked <0x200, 0x2FF> false sysbus.mockCpu1
+    # Now, unlock the first range, and lock the second range
+    # Cpu1 should fail on accessing the range
+    Execute Command            sysbus SetAddressRangeLocked <0x250, 0x7FF> true sysbus.mockCpu1
+    Should Block Write Byte    0x250  ${new_value_0x200}  sysbus.mockCpu1
+
+Test Registering Mapped Memory In Locked Range
+    # We want to test IMapped memory here, so we need CPU's presence for a full test
+    # relocking is trivial for anything that isn't directly mapped to CPU (unmanaged memory)
+    Requires                   sysbus-with-mock-cpus
+    ${value}=                  Set Variable  0x66
+
+    Execute Command            sysbus SetAddressRangeLocked <0x4000, 0x4FFF> true
+
+    Execute Command            machine LoadPlatformDescriptionFromString "memory_locked: Memory.MappedMemory @ sysbus 0x4000 { size: 0x1000 }"
+    Execute Command            machine LoadPlatformDescriptionFromString "memory_unlocked: Memory.MappedMemory @ sysbus 0x5000 { size: 0x1000 }"
+
+    Should Write Byte          0x5000  ${value}
+    Should Block Write Byte    0x4000  ${value}
+
+    Execute Command            sysbus.mockCpu0 PC 0x4000
+    Execute Command            sysbus.mockCpu1 PC 0x5000
+
+    Start Emulation
+
+    # Now, to really test if newly registered memory has been locked correctly, try executing code (instructions don't matter here)
+    # Cpu0 should abort immediately, and Cpu1 should fall out of memory range
+    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
+
 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