Expose memory allocation sandboxing through VMContext.
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
package li.cil.oc2.api.bus.device.vm;
|
||||
|
||||
/**
|
||||
* A memory allocator used to ensure sandbox limits when loading devices.
|
||||
*/
|
||||
public interface MemoryAllocator {
|
||||
/**
|
||||
* Tries to reserve the specified amount of memory, in bytes.
|
||||
*
|
||||
* @param size the amount of memory to reserve, in bytes.
|
||||
* @return {@code true} when the memory was claimed successfully; {@code false} otherwise.
|
||||
*/
|
||||
boolean claimMemory(int size);
|
||||
}
|
||||
@@ -29,7 +29,8 @@ public interface VMContext {
|
||||
* made available through this instance will result in an exception.
|
||||
* <p>
|
||||
* Interrupts raised will automatically be lowered when the {@link VMDevice} that
|
||||
* raised them is unloaded, e.g. because it is removed from the {@link DeviceBus}.
|
||||
* raised them is unloaded, e.g. because it is removed from the {@link DeviceBus}
|
||||
* or the VM stopped.
|
||||
*
|
||||
* @return the interrupt controller of the virtual machine.
|
||||
*/
|
||||
@@ -42,7 +43,8 @@ public interface 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}.
|
||||
* is unloaded, e.g. because it has been removed from the {@link DeviceBus} or the VM
|
||||
* stopped.
|
||||
*
|
||||
* @return the memory range allocator.
|
||||
*/
|
||||
@@ -60,4 +62,23 @@ public interface VMContext {
|
||||
* @return the interrupt allocator.
|
||||
*/
|
||||
InterruptAllocator getInterruptAllocator();
|
||||
|
||||
/**
|
||||
* Allows reserving fixed amounts of memory respecting sandbox constraints.
|
||||
* <p>
|
||||
* It is strongly advised to use this allocator to make known large memory
|
||||
* uses, e.g. when allocating large blobs for RAM or block devices. This
|
||||
* allows respecting the built-in limits for overall memory usage of
|
||||
* running VMs.
|
||||
* <p>
|
||||
* Devices failing to reserve the memory they would use should fail their
|
||||
* {@link VMDevice#load(VMContext)}.
|
||||
* <p>
|
||||
* Memory will automatically be released when the {@link VMDevice} that claimed
|
||||
* it is unloaded, e.g. because it is removed from the {@link DeviceBus} or the
|
||||
* VM stopped.
|
||||
*
|
||||
* @return the memory allocator.
|
||||
*/
|
||||
MemoryAllocator getMemoryAllocator();
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import li.cil.oc2.common.bus.device.provider.util.AbstractObjectProxy;
|
||||
import li.cil.oc2.common.serialization.BlobStorage;
|
||||
import li.cil.oc2.common.serialization.NBTSerialization;
|
||||
import li.cil.oc2.common.util.NBTTagIds;
|
||||
import li.cil.oc2.common.vm.Allocator;
|
||||
import li.cil.sedna.api.device.BlockDevice;
|
||||
import li.cil.sedna.device.virtio.VirtIOBlockDevice;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@@ -28,7 +27,6 @@ public abstract class AbstractHardDiskDriveDevice<T extends BlockDevice> extends
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
|
||||
private final UUID allocHandle = Allocator.createHandle();
|
||||
private BlobStorage.JobHandle jobHandle;
|
||||
private T data;
|
||||
private VirtIOBlockDevice device;
|
||||
@@ -56,12 +54,10 @@ public abstract class AbstractHardDiskDriveDevice<T extends BlockDevice> extends
|
||||
}
|
||||
|
||||
if (!claimAddress(context)) {
|
||||
Allocator.freeMemory(allocHandle);
|
||||
return VMDeviceLoadResult.fail();
|
||||
}
|
||||
|
||||
if (!claimInterrupt(context)) {
|
||||
Allocator.freeMemory(allocHandle);
|
||||
return VMDeviceLoadResult.fail();
|
||||
}
|
||||
|
||||
@@ -152,7 +148,7 @@ public abstract class AbstractHardDiskDriveDevice<T extends BlockDevice> extends
|
||||
///////////////////////////////////////////////////////////////
|
||||
|
||||
private boolean allocateDevice(final VMContext context) {
|
||||
if (!Allocator.claimMemory(allocHandle, getSize())) {
|
||||
if (!context.getMemoryAllocator().claimMemory(getSize())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -231,12 +227,12 @@ public abstract class AbstractHardDiskDriveDevice<T extends BlockDevice> extends
|
||||
}
|
||||
}
|
||||
|
||||
Allocator.freeMemory(allocHandle);
|
||||
data = null;
|
||||
|
||||
device = null;
|
||||
deviceNbt = null;
|
||||
address = null;
|
||||
interrupt = null;
|
||||
jobHandle = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import li.cil.oc2.common.bus.device.provider.util.AbstractItemDeviceProvider;
|
||||
import li.cil.oc2.common.bus.device.provider.util.AbstractObjectProxy;
|
||||
import li.cil.oc2.common.serialization.BlobStorage;
|
||||
import li.cil.oc2.common.util.NBTTagIds;
|
||||
import li.cil.oc2.common.vm.Allocator;
|
||||
import li.cil.sedna.api.device.PhysicalMemory;
|
||||
import li.cil.sedna.device.memory.Memory;
|
||||
import li.cil.sedna.memory.PhysicalMemoryInputStream;
|
||||
@@ -46,7 +45,6 @@ public final class MemoryItemDeviceProvider extends AbstractItemDeviceProvider {
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
|
||||
private final UUID allocHandle = Allocator.createHandle();
|
||||
private BlobStorage.JobHandle jobHandle;
|
||||
private PhysicalMemory device;
|
||||
|
||||
@@ -63,7 +61,7 @@ public final class MemoryItemDeviceProvider extends AbstractItemDeviceProvider {
|
||||
|
||||
@Override
|
||||
public VMDeviceLoadResult load(final VMContext context) {
|
||||
if (!allocateDevice()) {
|
||||
if (!allocateDevice(context)) {
|
||||
return VMDeviceLoadResult.fail();
|
||||
}
|
||||
|
||||
@@ -122,8 +120,8 @@ public final class MemoryItemDeviceProvider extends AbstractItemDeviceProvider {
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
|
||||
private boolean allocateDevice() {
|
||||
if (!Allocator.claimMemory(allocHandle, RAM_SIZE)) {
|
||||
private boolean allocateDevice(final VMContext context) {
|
||||
if (!context.getMemoryAllocator().claimMemory(RAM_SIZE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -141,7 +139,6 @@ public final class MemoryItemDeviceProvider extends AbstractItemDeviceProvider {
|
||||
}
|
||||
|
||||
if (!claimedAddress.isPresent()) {
|
||||
Allocator.freeMemory(allocHandle);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -164,17 +161,13 @@ public final class MemoryItemDeviceProvider extends AbstractItemDeviceProvider {
|
||||
}
|
||||
|
||||
private void unload() {
|
||||
// Finish saves on unload to ensure future loads will read correct data.
|
||||
awaitStorageOperation();
|
||||
|
||||
Allocator.freeMemory(allocHandle);
|
||||
device = null;
|
||||
|
||||
// RAM is volatile, so free up our persisted blob when device is unloaded.
|
||||
BlobStorage.freeHandle(blobHandle);
|
||||
blobHandle = null;
|
||||
|
||||
device = null;
|
||||
address = null;
|
||||
jobHandle = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
package li.cil.oc2.common.vm;
|
||||
|
||||
import li.cil.oc2.api.bus.device.vm.MemoryAllocator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class ManagedMemoryAllocator implements MemoryAllocator {
|
||||
private final ArrayList<UUID> claimedMemory = new ArrayList<>();
|
||||
private boolean isFrozen;
|
||||
|
||||
public void freeze() {
|
||||
isFrozen = true;
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
for (final UUID handle : claimedMemory) {
|
||||
Allocator.freeMemory(handle);
|
||||
}
|
||||
|
||||
claimedMemory.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean claimMemory(final int size) {
|
||||
if (isFrozen) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
final UUID handle = Allocator.createHandle();
|
||||
if (!Allocator.claimMemory(handle, size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
claimedMemory.add(handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package li.cil.oc2.common.vm;
|
||||
|
||||
import li.cil.oc2.api.bus.device.vm.InterruptAllocator;
|
||||
import li.cil.oc2.api.bus.device.vm.MemoryAllocator;
|
||||
import li.cil.oc2.api.bus.device.vm.MemoryRangeAllocator;
|
||||
import li.cil.oc2.api.bus.device.vm.VMContext;
|
||||
import li.cil.sedna.api.Board;
|
||||
@@ -14,6 +15,7 @@ public final class ManagedVMContext implements VMContext {
|
||||
private final ManagedInterruptController interruptController;
|
||||
private final ManagedMemoryRangeAllocator memoryRangeAllocator;
|
||||
private final ManagedInterruptAllocator interruptAllocator;
|
||||
private final ManagedMemoryAllocator memoryAllocator;
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -22,17 +24,20 @@ public final class ManagedVMContext implements VMContext {
|
||||
this.interruptAllocator = new ManagedInterruptAllocator(claimedInterrupts, reservedInterrupts, board.getInterruptCount());
|
||||
this.memoryMap = new ManagedMemoryMap(board.getMemoryMap());
|
||||
this.interruptController = new ManagedInterruptController(board.getInterruptController(), interruptAllocator);
|
||||
this.memoryAllocator = new ManagedMemoryAllocator();
|
||||
}
|
||||
|
||||
public void freeze() {
|
||||
memoryRangeAllocator.freeze();
|
||||
interruptAllocator.freeze();
|
||||
memoryAllocator.freeze();
|
||||
}
|
||||
|
||||
public void invalidate() {
|
||||
memoryRangeAllocator.invalidate();
|
||||
interruptAllocator.invalidate();
|
||||
interruptController.invalidate();
|
||||
memoryAllocator.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,4 +59,9 @@ public final class ManagedVMContext implements VMContext {
|
||||
public InterruptAllocator getInterruptAllocator() {
|
||||
return interruptAllocator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemoryAllocator getMemoryAllocator() {
|
||||
return memoryAllocator;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user