VM device interrupts zero based since we use them for masking anyway.

This commit is contained in:
Florian Nücke
2020-12-12 12:36:10 +01:00
parent 4ebc722126
commit d4fffc40b9
2 changed files with 93 additions and 15 deletions

View File

@@ -44,17 +44,16 @@ public final class ManagedInterruptAllocator implements InterruptAllocator {
throw new IllegalStateException();
}
if (interrupt < 1 || interrupt > R5PlatformLevelInterruptController.INTERRUPT_COUNT) {
if (interrupt < 0 || interrupt >= R5PlatformLevelInterruptController.INTERRUPT_COUNT) {
throw new IllegalArgumentException();
}
final int interruptBit = interrupt - 1;
if (interrupts.get(interruptBit)) {
if (interrupts.get(interrupt)) {
return claimInterrupt();
} else {
interrupts.set(interruptBit);
reservedInterrupts.set(interruptBit);
managedInterrupts.set(interruptBit);
interrupts.set(interrupt);
reservedInterrupts.set(interrupt);
managedInterrupts.set(interrupt);
return OptionalInt.of(interrupt);
}
}
@@ -69,16 +68,15 @@ public final class ManagedInterruptAllocator implements InterruptAllocator {
claimedInterrupts.or(interrupts);
claimedInterrupts.or(reservedInterrupts);
final int interruptBit = claimedInterrupts.nextClearBit(0);
if (interruptBit >= interruptCount) {
final int interrupt = claimedInterrupts.nextClearBit(0);
if (interrupt >= interruptCount) {
return OptionalInt.empty();
}
interrupts.set(interruptBit);
reservedInterrupts.set(interruptBit);
managedInterrupts.set(interruptBit);
interrupts.set(interrupt);
reservedInterrupts.set(interrupt);
managedInterrupts.set(interrupt);
final int interrupt = interruptBit + 1;
return OptionalInt.of(interrupt);
}
}

View File

@@ -7,14 +7,14 @@ import li.cil.oc2.common.vm.VirtualMachineDeviceBusAdapter;
import li.cil.sedna.api.device.InterruptController;
import li.cil.sedna.api.memory.MemoryMap;
import li.cil.sedna.memory.SimpleMemoryMap;
import li.cil.sedna.riscv.device.R5PlatformLevelInterruptController;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.Collections;
import java.util.OptionalInt;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@@ -26,7 +26,7 @@ public final class VMDeviceTests {
@BeforeEach
public void setupEach() {
memoryMap = new SimpleMemoryMap();
interruptController = mock(InterruptController.class);
interruptController = new R5PlatformLevelInterruptController();
adapter = new VirtualMachineDeviceBusAdapter(memoryMap, interruptController);
}
@@ -122,4 +122,84 @@ public final class VMDeviceTests {
adapter.addDevices(Collections.singleton(device));
assertTrue(adapter.load());
}
@Test
public void deviceCanRaiseClaimedInterrupts() {
final DeviceData deviceData = new DeviceData();
final VMDevice device = mock(VMDevice.class);
when(device.load(any())).thenAnswer(invocation -> {
final VMContext context = invocation.getArgument(0);
final OptionalInt interrupt = context.getInterruptAllocator().claimInterrupt();
assertTrue(interrupt.isPresent());
deviceData.context = context;
deviceData.interrupt = interrupt.getAsInt();
return VMDeviceLoadResult.success();
});
adapter.addDevices(Collections.singleton(device));
assertTrue(adapter.load());
verify(device).load(any());
final int claimedInterruptMask = 1 << deviceData.interrupt;
deviceData.context.getInterruptController().raiseInterrupts(claimedInterruptMask);
assertTrue((interruptController.getRaisedInterrupts() & claimedInterruptMask) != 0);
}
@Test
public void devicesCannotRaiseUnclaimedInterrupts() {
final DeviceData deviceData = new DeviceData();
final VMDevice device = mock(VMDevice.class);
when(device.load(any())).thenAnswer(invocation -> {
deviceData.context = invocation.getArgument(0);
return VMDeviceLoadResult.success();
});
adapter.addDevices(Collections.singleton(device));
assertTrue(adapter.load());
verify(device).load(any());
final int someInterruptMask = 0x1;
assertThrows(IllegalArgumentException.class, () ->
deviceData.context.getInterruptController().raiseInterrupts(someInterruptMask));
}
@Test
public void unloadLowersClaimedInterrupts() {
final DeviceData deviceData = new DeviceData();
final VMDevice device = mock(VMDevice.class);
when(device.load(any())).thenAnswer(invocation -> {
final VMContext context = invocation.getArgument(0);
final OptionalInt interrupt = context.getInterruptAllocator().claimInterrupt();
assertTrue(interrupt.isPresent());
deviceData.context = context;
deviceData.interrupt = interrupt.getAsInt();
return VMDeviceLoadResult.success();
});
adapter.addDevices(Collections.singleton(device));
assertTrue(adapter.load());
verify(device).load(any());
final int claimedInterruptMask = 1 << deviceData.interrupt;
deviceData.context.getInterruptController().raiseInterrupts(claimedInterruptMask);
assertTrue((interruptController.getRaisedInterrupts() & claimedInterruptMask) != 0);
adapter.unload();
assertFalse((interruptController.getRaisedInterrupts() & claimedInterruptMask) != 0);
}
private static final class DeviceData {
public VMContext context;
public int interrupt;
}
}