diff --git a/src/main/java/li/cil/oc2/api/bus/device/vm/MemoryRangeAllocator.java b/src/main/java/li/cil/oc2/api/bus/device/vm/MemoryRangeAllocator.java
new file mode 100644
index 00000000..6cfe8382
--- /dev/null
+++ b/src/main/java/li/cil/oc2/api/bus/device/vm/MemoryRangeAllocator.java
@@ -0,0 +1,11 @@
+package li.cil.oc2.api.bus.device.vm;
+
+import li.cil.sedna.api.device.MemoryMappedDevice;
+
+import java.util.OptionalLong;
+
+public interface MemoryRangeAllocator {
+ OptionalLong claimMemoryRange(long address, MemoryMappedDevice device);
+
+ OptionalLong claimMemoryRange(MemoryMappedDevice device);
+}
diff --git a/src/main/java/li/cil/oc2/api/bus/device/vm/VMContext.java b/src/main/java/li/cil/oc2/api/bus/device/vm/VMContext.java
index 339dc6fb..d990041a 100644
--- a/src/main/java/li/cil/oc2/api/bus/device/vm/VMContext.java
+++ b/src/main/java/li/cil/oc2/api/bus/device/vm/VMContext.java
@@ -14,32 +14,16 @@ public interface VMContext {
/**
* The memory of the virtual machine.
*
- * {@link MemoryMappedDevice}s can only be added inside {@link VMDevice#load(VMContext)}.
- * Trying to add devices after that method has returned will result in an exception.
- *
- * Removing {@link MemoryMappedDevice}s is not supported. Added devices will
- * automatically removed when the {@link VMDevice} that added it is unloaded,
- * e.g. because it has been removed from the {@link DeviceBus}.
+ * Adding or removing {@link MemoryMappedDevice}s directly is not supported.
+ * Use the {@link MemoryRangeAllocator} provided by {@link #getMemoryRangeAllocator()}
+ * to add devices.
*
* @return the memory map of the virtual machine.
*/
MemoryMap getMemoryMap();
/**
- * An object that allows claiming interrupts for use with the {@link InterruptController}.
- *
- * Interrupts can only be claimed inside {@link VMDevice#load(VMContext)}.
- * Trying to claim interrupts after that method has returned will result in an exception.
- *
- * Claimed interrupts will automatically be released when the {@link VMDevice} that
- * claimed them is unloaded, e.g. because it is removed from the {@link DeviceBus}.
- *
- * @return the interrupt allocator.
- */
- InterruptAllocator getInterruptAllocator();
-
- /**
- * The interrupt controller of the virtual machine devices should attach to.
+ * The interrupt controller of the virtual machine.
*
* Raising or lowering interrupts that have not been claimed using the {@link InterruptAllocator}
* made available through this instance will result in an exception.
@@ -50,4 +34,30 @@ public interface VMContext {
* @return the interrupt controller of the virtual machine.
*/
InterruptController getInterruptController();
+
+ /**
+ * Allows adding {@link MemoryMappedDevice}s to the VM's {@link MemoryMap}.
+ *
+ * {@link MemoryMappedDevice}s can only be added inside {@link VMDevice#load(VMContext)}.
+ * Trying to add devices after that method has returned will result in an exception.
+ *
+ * Added devices will be automatically removed when the {@link VMDevice} that added it
+ * is unloaded, e.g. because it has been removed from the {@link DeviceBus}.
+ *
+ * @return the memory range allocator.
+ */
+ MemoryRangeAllocator getMemoryRangeAllocator();
+
+ /**
+ * Allows claiming interrupts for use with the VM's {@link InterruptController}.
+ *
+ * Interrupts can only be claimed inside {@link VMDevice#load(VMContext)}.
+ * Trying to claim interrupts after that method has returned will result in an exception.
+ *
+ * Claimed interrupts will automatically be released when the {@link VMDevice} that
+ * claimed them is unloaded, e.g. because it is removed from the {@link DeviceBus}.
+ *
+ * @return the interrupt allocator.
+ */
+ InterruptAllocator getInterruptAllocator();
}
diff --git a/src/main/java/li/cil/oc2/common/vm/ManagedInterruptAllocator.java b/src/main/java/li/cil/oc2/common/vm/ManagedInterruptAllocator.java
index cccc3a8a..b2de8723 100644
--- a/src/main/java/li/cil/oc2/common/vm/ManagedInterruptAllocator.java
+++ b/src/main/java/li/cil/oc2/common/vm/ManagedInterruptAllocator.java
@@ -7,7 +7,7 @@ import java.util.BitSet;
import java.util.OptionalInt;
public final class ManagedInterruptAllocator implements InterruptAllocator {
- private final BitSet interrupts;
+ private final BitSet claimedInterrupts;
private final BitSet reservedInterrupts;
private final BitSet managedInterrupts;
private final int interruptCount;
@@ -16,8 +16,8 @@ public final class ManagedInterruptAllocator implements InterruptAllocator {
///////////////////////////////////////////////////////////////////
- public ManagedInterruptAllocator(final BitSet interrupts, final BitSet reservedInterrupts, final int interruptCount) {
- this.interrupts = interrupts;
+ public ManagedInterruptAllocator(final BitSet claimedInterrupts, final BitSet reservedInterrupts, final int interruptCount) {
+ this.claimedInterrupts = claimedInterrupts;
this.reservedInterrupts = reservedInterrupts;
this.managedInterrupts = new BitSet(interruptCount);
this.interruptCount = interruptCount;
@@ -30,7 +30,7 @@ public final class ManagedInterruptAllocator implements InterruptAllocator {
}
public void invalidate() {
- interrupts.andNot(managedInterrupts);
+ claimedInterrupts.andNot(managedInterrupts);
managedMask = 0;
}
@@ -48,10 +48,10 @@ public final class ManagedInterruptAllocator implements InterruptAllocator {
throw new IllegalArgumentException();
}
- if (interrupts.get(interrupt)) {
+ if (claimedInterrupts.get(interrupt)) {
return claimInterrupt();
} else {
- interrupts.set(interrupt);
+ claimedInterrupts.set(interrupt);
reservedInterrupts.set(interrupt);
managedInterrupts.set(interrupt);
return OptionalInt.of(interrupt);
@@ -64,16 +64,16 @@ public final class ManagedInterruptAllocator implements InterruptAllocator {
throw new IllegalStateException();
}
- final BitSet claimedInterrupts = new BitSet();
- claimedInterrupts.or(interrupts);
- claimedInterrupts.or(reservedInterrupts);
+ final BitSet allClaimedInterrupts = new BitSet();
+ allClaimedInterrupts.or(claimedInterrupts);
+ allClaimedInterrupts.or(reservedInterrupts);
- final int interrupt = claimedInterrupts.nextClearBit(0);
+ final int interrupt = allClaimedInterrupts.nextClearBit(0);
if (interrupt >= interruptCount) {
return OptionalInt.empty();
}
- interrupts.set(interrupt);
+ claimedInterrupts.set(interrupt);
reservedInterrupts.set(interrupt);
managedInterrupts.set(interrupt);
diff --git a/src/main/java/li/cil/oc2/common/vm/ManagedInterruptController.java b/src/main/java/li/cil/oc2/common/vm/ManagedInterruptController.java
index 2e0330b1..34eca29f 100644
--- a/src/main/java/li/cil/oc2/common/vm/ManagedInterruptController.java
+++ b/src/main/java/li/cil/oc2/common/vm/ManagedInterruptController.java
@@ -5,7 +5,7 @@ import li.cil.sedna.api.device.InterruptController;
public final class ManagedInterruptController implements InterruptController {
private final InterruptController interruptController;
private final ManagedInterruptAllocator allocator;
- private int managedInterrupts = 0;
+ private int raisedInterrupts = 0;
private boolean isValid = true;
///////////////////////////////////////////////////////////////////
@@ -17,19 +17,19 @@ public final class ManagedInterruptController implements InterruptController {
public void invalidate() {
isValid = false;
- interruptController.lowerInterrupts(managedInterrupts);
- managedInterrupts = 0;
+ interruptController.lowerInterrupts(raisedInterrupts);
+ raisedInterrupts = 0;
}
@Override
public void raiseInterrupts(final int mask) {
if (!isValid) {
- throw new IllegalArgumentException();
+ throw new IllegalStateException();
}
if (allocator.isMaskValid(mask)) {
interruptController.raiseInterrupts(mask);
- managedInterrupts |= mask;
+ raisedInterrupts |= mask;
} else {
throw new IllegalArgumentException("Trying to raise interrupt not allocated by this context.");
}
@@ -38,12 +38,12 @@ public final class ManagedInterruptController implements InterruptController {
@Override
public void lowerInterrupts(final int mask) {
if (!isValid) {
- throw new IllegalArgumentException();
+ throw new IllegalStateException();
}
if (allocator.isMaskValid(mask)) {
interruptController.lowerInterrupts(mask);
- managedInterrupts &= ~managedInterrupts;
+ raisedInterrupts &= ~mask;
} else {
throw new IllegalArgumentException("Trying to lower interrupt not allocated by this context.");
}
@@ -51,6 +51,6 @@ public final class ManagedInterruptController implements InterruptController {
@Override
public int getRaisedInterrupts() {
- return interruptController.getRaisedInterrupts();
+ return raisedInterrupts;
}
}
diff --git a/src/main/java/li/cil/oc2/common/vm/ManagedMemoryMap.java b/src/main/java/li/cil/oc2/common/vm/ManagedMemoryMap.java
index 87810d36..b9a5e453 100644
--- a/src/main/java/li/cil/oc2/common/vm/ManagedMemoryMap.java
+++ b/src/main/java/li/cil/oc2/common/vm/ManagedMemoryMap.java
@@ -6,14 +6,11 @@ import li.cil.sedna.api.memory.MemoryMap;
import li.cil.sedna.api.memory.MemoryRange;
import javax.annotation.Nullable;
-import java.util.ArrayList;
import java.util.Optional;
import java.util.OptionalLong;
final class ManagedMemoryMap implements MemoryMap {
private final MemoryMap memoryMap;
- private final ArrayList managedDevices = new ArrayList<>();
- private boolean isFrozen;
///////////////////////////////////////////////////////////////////
@@ -21,16 +18,6 @@ final class ManagedMemoryMap implements MemoryMap {
this.memoryMap = memoryMap;
}
- public void freeze() {
- isFrozen = true;
- }
-
- public void invalidate() {
- for (final MemoryMappedDevice device : managedDevices) {
- memoryMap.removeDevice(device);
- }
- }
-
@Override
public OptionalLong findFreeRange(final long start, final long end, final int size) {
return memoryMap.findFreeRange(start, end, size);
@@ -38,16 +25,7 @@ final class ManagedMemoryMap implements MemoryMap {
@Override
public boolean addDevice(final long address, final MemoryMappedDevice device) {
- if (isFrozen) {
- throw new IllegalStateException();
- }
-
- if (memoryMap.addDevice(address, device)) {
- managedDevices.add(device);
- return true;
- } else {
- return false;
- }
+ throw new UnsupportedOperationException();
}
@Override
diff --git a/src/main/java/li/cil/oc2/common/vm/ManagedMemoryRangeAllocator.java b/src/main/java/li/cil/oc2/common/vm/ManagedMemoryRangeAllocator.java
new file mode 100644
index 00000000..65df3d00
--- /dev/null
+++ b/src/main/java/li/cil/oc2/common/vm/ManagedMemoryRangeAllocator.java
@@ -0,0 +1,62 @@
+package li.cil.oc2.common.vm;
+
+import li.cil.oc2.api.bus.device.vm.MemoryRangeAllocator;
+import li.cil.sedna.api.Board;
+import li.cil.sedna.api.device.MemoryMappedDevice;
+import li.cil.sedna.api.memory.MemoryRange;
+
+import java.util.ArrayList;
+import java.util.Optional;
+import java.util.OptionalLong;
+
+public final class ManagedMemoryRangeAllocator implements MemoryRangeAllocator {
+ private final Board board;
+ private final ArrayList managedDevices = new ArrayList<>();
+ private boolean isFrozen;
+
+ public ManagedMemoryRangeAllocator(final Board board) {
+ this.board = board;
+ }
+
+ public void freeze() {
+ isFrozen = true;
+ }
+
+ public void invalidate() {
+ for (final MemoryMappedDevice device : managedDevices) {
+ board.removeDevice(device);
+ }
+ managedDevices.clear();
+ }
+
+ @Override
+ public OptionalLong claimMemoryRange(final long address, final MemoryMappedDevice device) {
+ if (isFrozen) {
+ throw new IllegalStateException();
+ }
+
+ if (board.addDevice(address, device)) {
+ managedDevices.add(device);
+ return OptionalLong.of(address);
+ }
+
+ return claimMemoryRange(device);
+ }
+
+ @Override
+ public OptionalLong claimMemoryRange(final MemoryMappedDevice device) {
+ if (isFrozen) {
+ throw new IllegalStateException();
+ }
+
+ if (!board.addDevice(device)) {
+ return OptionalLong.empty();
+ }
+
+ final Optional range = board.getMemoryMap().getMemoryRange(device);
+ assert range.isPresent();
+
+ managedDevices.add(device);
+ return OptionalLong.of(range.get().address());
+ }
+}
diff --git a/src/main/java/li/cil/oc2/common/vm/ManagedVMContext.java b/src/main/java/li/cil/oc2/common/vm/ManagedVMContext.java
index c833321f..07cbb145 100644
--- a/src/main/java/li/cil/oc2/common/vm/ManagedVMContext.java
+++ b/src/main/java/li/cil/oc2/common/vm/ManagedVMContext.java
@@ -1,33 +1,36 @@
package li.cil.oc2.common.vm;
import li.cil.oc2.api.bus.device.vm.InterruptAllocator;
+import li.cil.oc2.api.bus.device.vm.MemoryRangeAllocator;
import li.cil.oc2.api.bus.device.vm.VMContext;
+import li.cil.sedna.api.Board;
import li.cil.sedna.api.device.InterruptController;
import li.cil.sedna.api.memory.MemoryMap;
-import li.cil.sedna.riscv.device.R5PlatformLevelInterruptController;
import java.util.BitSet;
public final class ManagedVMContext implements VMContext {
private final ManagedMemoryMap memoryMap;
- private final ManagedInterruptAllocator interruptAllocator;
private final ManagedInterruptController interruptController;
+ private final ManagedMemoryRangeAllocator memoryRangeAllocator;
+ private final ManagedInterruptAllocator interruptAllocator;
///////////////////////////////////////////////////////////////////
- public ManagedVMContext(final MemoryMap memoryMap, final InterruptController interruptController, final BitSet allocatedInterrupts, final BitSet reservedInterrupts) {
- this.memoryMap = new ManagedMemoryMap(memoryMap);
- this.interruptAllocator = new ManagedInterruptAllocator(allocatedInterrupts, reservedInterrupts, R5PlatformLevelInterruptController.INTERRUPT_COUNT);
- this.interruptController = new ManagedInterruptController(interruptController, interruptAllocator);
+ public ManagedVMContext(final Board board, final BitSet claimedInterrupts, final BitSet reservedInterrupts) {
+ this.memoryRangeAllocator = new ManagedMemoryRangeAllocator(board);
+ this.interruptAllocator = new ManagedInterruptAllocator(claimedInterrupts, reservedInterrupts, board.getInterruptCount());
+ this.memoryMap = new ManagedMemoryMap(board.getMemoryMap());
+ this.interruptController = new ManagedInterruptController(board.getInterruptController(), interruptAllocator);
}
public void freeze() {
- memoryMap.freeze();
+ memoryRangeAllocator.freeze();
interruptAllocator.freeze();
}
public void invalidate() {
- memoryMap.invalidate();
+ memoryRangeAllocator.invalidate();
interruptAllocator.invalidate();
interruptController.invalidate();
}
@@ -37,13 +40,18 @@ public final class ManagedVMContext implements VMContext {
return memoryMap;
}
- @Override
- public InterruptAllocator getInterruptAllocator() {
- return interruptAllocator;
- }
-
@Override
public InterruptController getInterruptController() {
return interruptController;
}
+
+ @Override
+ public MemoryRangeAllocator getMemoryRangeAllocator() {
+ return memoryRangeAllocator;
+ }
+
+ @Override
+ public InterruptAllocator getInterruptAllocator() {
+ return interruptAllocator;
+ }
}
diff --git a/src/main/java/li/cil/oc2/common/vm/VirtualMachine.java b/src/main/java/li/cil/oc2/common/vm/VirtualMachine.java
index 97bd96a3..d2346186 100644
--- a/src/main/java/li/cil/oc2/common/vm/VirtualMachine.java
+++ b/src/main/java/li/cil/oc2/common/vm/VirtualMachine.java
@@ -45,7 +45,7 @@ public final class VirtualMachine {
board.getCpu().setFrequency(REPORTED_CPU_FREQUENCY);
- vmAdapter = new VirtualMachineDeviceBusAdapter(board.getMemoryMap(), board.getInterruptController());
+ vmAdapter = new VirtualMachineDeviceBusAdapter(board);
uart = new UART16550A();
uart.getInterrupt().set(vmAdapter.claimInterrupt(), board.getInterruptController());
diff --git a/src/main/java/li/cil/oc2/common/vm/VirtualMachineDeviceBusAdapter.java b/src/main/java/li/cil/oc2/common/vm/VirtualMachineDeviceBusAdapter.java
index 845e9247..8934312a 100644
--- a/src/main/java/li/cil/oc2/common/vm/VirtualMachineDeviceBusAdapter.java
+++ b/src/main/java/li/cil/oc2/common/vm/VirtualMachineDeviceBusAdapter.java
@@ -4,8 +4,7 @@ import li.cil.ceres.api.Serialized;
import li.cil.oc2.api.bus.Device;
import li.cil.oc2.api.bus.device.vm.VMDevice;
import li.cil.oc2.api.bus.device.vm.VMDeviceLoadResult;
-import li.cil.sedna.api.device.InterruptController;
-import li.cil.sedna.api.memory.MemoryMap;
+import li.cil.sedna.api.Board;
import li.cil.sedna.riscv.device.R5PlatformLevelInterruptController;
import java.util.ArrayList;
@@ -14,10 +13,9 @@ import java.util.HashMap;
import java.util.Set;
public final class VirtualMachineDeviceBusAdapter {
- private final MemoryMap memoryMap;
- private final InterruptController interruptController;
+ private final Board board;
- private final BitSet allocatedInterrupts = new BitSet();
+ private final BitSet claimedInterrupts = new BitSet();
private final HashMap deviceContexts = new HashMap<>();
private final ArrayList incompleteLoads = new ArrayList<>();
@@ -31,21 +29,20 @@ public final class VirtualMachineDeviceBusAdapter {
///////////////////////////////////////////////////////////////////
- public VirtualMachineDeviceBusAdapter(final MemoryMap memoryMap, final InterruptController interruptController) {
- this.memoryMap = memoryMap;
- this.interruptController = interruptController;
+ public VirtualMachineDeviceBusAdapter(final Board board) {
+ this.board = board;
}
public int claimInterrupt() {
- return claimInterrupt(allocatedInterrupts.nextClearBit(0) + 1);
+ return claimInterrupt(claimedInterrupts.nextClearBit(0));
}
public int claimInterrupt(final int interrupt) {
- if (interrupt < 1 || interrupt > R5PlatformLevelInterruptController.INTERRUPT_COUNT) {
+ if (interrupt < 0 || interrupt >= R5PlatformLevelInterruptController.INTERRUPT_COUNT) {
throw new IllegalArgumentException();
}
- allocatedInterrupts.set(interrupt - 1);
+ claimedInterrupts.set(interrupt);
return interrupt;
}
@@ -54,7 +51,7 @@ public final class VirtualMachineDeviceBusAdapter {
final VMDevice device = incompleteLoads.remove(i);
final ManagedVMContext context = new ManagedVMContext(
- memoryMap, interruptController, allocatedInterrupts, reservedInterrupts);
+ board, claimedInterrupts, reservedInterrupts);
deviceContexts.put(device, context);
final VMDeviceLoadResult result = device.load(context);
@@ -71,7 +68,7 @@ public final class VirtualMachineDeviceBusAdapter {
}
reservedInterrupts.clear();
- reservedInterrupts.or(allocatedInterrupts);
+ reservedInterrupts.or(claimedInterrupts);
return true;
}
diff --git a/src/test/java/li/cil/oc2/common/bus/VMDeviceTests.java b/src/test/java/li/cil/oc2/common/bus/VMDeviceTests.java
index 28ed1ec5..d26091ef 100644
--- a/src/test/java/li/cil/oc2/common/bus/VMDeviceTests.java
+++ b/src/test/java/li/cil/oc2/common/bus/VMDeviceTests.java
@@ -4,7 +4,9 @@ import li.cil.oc2.api.bus.device.vm.VMContext;
import li.cil.oc2.api.bus.device.vm.VMDevice;
import li.cil.oc2.api.bus.device.vm.VMDeviceLoadResult;
import li.cil.oc2.common.vm.VirtualMachineDeviceBusAdapter;
+import li.cil.sedna.api.Board;
import li.cil.sedna.api.device.InterruptController;
+import li.cil.sedna.api.device.MemoryMappedDevice;
import li.cil.sedna.api.memory.MemoryMap;
import li.cil.sedna.memory.SimpleMemoryMap;
import li.cil.sedna.riscv.device.R5PlatformLevelInterruptController;
@@ -13,6 +15,7 @@ import org.junit.jupiter.api.Test;
import java.util.Collections;
import java.util.OptionalInt;
+import java.util.OptionalLong;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
@@ -27,7 +30,27 @@ public final class VMDeviceTests {
public void setupEach() {
memoryMap = new SimpleMemoryMap();
interruptController = new R5PlatformLevelInterruptController();
- adapter = new VirtualMachineDeviceBusAdapter(memoryMap, interruptController);
+
+ final Board board = mock(Board.class);
+ when(board.getMemoryMap()).thenReturn(memoryMap);
+ when(board.getInterruptController()).thenReturn(interruptController);
+ when(board.getInterruptCount()).thenReturn(16);
+ when(board.addDevice(any())).then(invocation -> {
+ final MemoryMappedDevice device = invocation.getArgument(0);
+ final OptionalLong address = memoryMap.findFreeRange(0, 0xFFFFFFFF, device.getLength());
+ if (address.isPresent()) {
+ memoryMap.addDevice(address.getAsLong(), device);
+ return true;
+ } else {
+ return false;
+ }
+ });
+ doAnswer(invocation -> {
+ memoryMap.removeDevice(invocation.getArgument(0));
+ return null;
+ }).when(board).removeDevice(any());
+
+ adapter = new VirtualMachineDeviceBusAdapter(board);
}
@Test
@@ -141,8 +164,6 @@ public final class VMDeviceTests {
adapter.addDevices(Collections.singleton(device));
assertTrue(adapter.load());
- verify(device).load(any());
-
final int claimedInterruptMask = 1 << deviceData.interrupt;
deviceData.context.getInterruptController().raiseInterrupts(claimedInterruptMask);
@@ -161,8 +182,6 @@ public final class VMDeviceTests {
adapter.addDevices(Collections.singleton(device));
assertTrue(adapter.load());
- verify(device).load(any());
-
final int someInterruptMask = 0x1;
assertThrows(IllegalArgumentException.class, () ->
deviceData.context.getInterruptController().raiseInterrupts(someInterruptMask));
@@ -186,8 +205,6 @@ public final class VMDeviceTests {
adapter.addDevices(Collections.singleton(device));
assertTrue(adapter.load());
- verify(device).load(any());
-
final int claimedInterruptMask = 1 << deviceData.interrupt;
deviceData.context.getInterruptController().raiseInterrupts(claimedInterruptMask);
@@ -198,8 +215,73 @@ public final class VMDeviceTests {
assertFalse((interruptController.getRaisedInterrupts() & claimedInterruptMask) != 0);
}
+ @Test
+ public void devicesCannotAddToMemoryMapDirectly() {
+ final VMDevice device = mock(VMDevice.class);
+ when(device.load(any())).thenAnswer(invocation -> {
+ final VMContext context = invocation.getArgument(0);
+
+ assertThrows(UnsupportedOperationException.class, () ->
+ context.getMemoryMap().addDevice(0, mock(MemoryMappedDevice.class)));
+
+ return VMDeviceLoadResult.success();
+ });
+
+ adapter.addDevices(Collections.singleton(device));
+ adapter.load();
+ }
+
+ @Test
+ public void devicesCanAddMemoryMappedDevices() {
+ final DeviceData deviceData = new DeviceData();
+ final VMDevice device = mock(VMDevice.class);
+ when(device.load(any())).thenAnswer(invocation -> {
+ final VMContext context = invocation.getArgument(0);
+
+ deviceData.context = context;
+ deviceData.device = mock(MemoryMappedDevice.class);
+ when(deviceData.device.getLength()).thenReturn(0x1000);
+
+ assertTrue(context.getMemoryRangeAllocator().claimMemoryRange(deviceData.device).isPresent());
+
+ return VMDeviceLoadResult.success();
+ });
+
+ adapter.addDevices(Collections.singleton(device));
+ assertTrue(adapter.load());
+
+ assertTrue(deviceData.context.getMemoryMap().getMemoryRange(deviceData.device).isPresent());
+ }
+
+ @Test
+ public void addedDevicesGetRemovedOnUnload() {
+ final DeviceData deviceData = new DeviceData();
+ final VMDevice device = mock(VMDevice.class);
+ when(device.load(any())).thenAnswer(invocation -> {
+ final VMContext context = invocation.getArgument(0);
+
+ deviceData.context = context;
+ deviceData.device = mock(MemoryMappedDevice.class);
+ when(deviceData.device.getLength()).thenReturn(0x1000);
+
+ assertTrue(context.getMemoryRangeAllocator().claimMemoryRange(deviceData.device).isPresent());
+
+ return VMDeviceLoadResult.success();
+ });
+
+ adapter.addDevices(Collections.singleton(device));
+ assertTrue(adapter.load());
+
+ assertTrue(deviceData.context.getMemoryMap().getMemoryRange(deviceData.device).isPresent());
+
+ adapter.unload();
+
+ assertFalse(deviceData.context.getMemoryMap().getMemoryRange(deviceData.device).isPresent());
+ }
+
private static final class DeviceData {
public VMContext context;
public int interrupt;
+ public MemoryMappedDevice device;
}
}