Analogous to interrupt allocator added memory range allocator for adding devices.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
@@ -14,32 +14,16 @@ public interface VMContext {
|
||||
/**
|
||||
* The memory of the virtual machine.
|
||||
* <p>
|
||||
* {@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.
|
||||
* <p>
|
||||
* 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}.
|
||||
* <p>
|
||||
* Interrupts can only be claimed inside {@link VMDevice#load(VMContext)}.
|
||||
* Trying to claim interrupts after that method has returned will result in an exception.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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}.
|
||||
* <p>
|
||||
* {@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.
|
||||
* <p>
|
||||
* 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}.
|
||||
* <p>
|
||||
* Interrupts can only be claimed inside {@link VMDevice#load(VMContext)}.
|
||||
* Trying to claim interrupts after that method has returned will result in an exception.
|
||||
* <p>
|
||||
* 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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<MemoryMappedDevice> 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
|
||||
|
||||
@@ -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<MemoryMappedDevice> 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<MemoryRange> range = board.getMemoryMap().getMemoryRange(device);
|
||||
assert range.isPresent();
|
||||
|
||||
managedDevices.add(device);
|
||||
return OptionalLong.of(range.get().address());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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<VMDevice, ManagedVMContext> deviceContexts = new HashMap<>();
|
||||
private final ArrayList<VMDevice> 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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user