From 8cafbfc49f611f6ca1f9147f90227fde3355e41d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sat, 19 Dec 2020 03:01:16 +0100 Subject: [PATCH] Add support for NBT-based custom HDD items. In particular, add ones with a named "base" block device, for now just one with the linux root fs. Using this to replace the hardcoded always present one. --- src/main/java/li/cil/oc2/Config.java | 4 +- src/main/java/li/cil/oc2/Constants.java | 7 + src/main/java/li/cil/oc2/OpenComputers.java | 2 +- .../block/entity/ComputerTileEntity.java | 9 +- .../device/AbstractHardDiskDriveDevice.java | 242 ++++++++++++++++++ .../bus/device/HardDiskDriveDevice.java | 39 +++ .../bus/device/SparseHardDiskDriveDevice.java | 113 ++++++++ .../oc2/common/bus/device/package-info.java | 7 + .../provider/HardDriveItemDeviceProvider.java | 232 +++-------------- .../li/cil/oc2/common/vm/VirtualMachine.java | 14 +- 10 files changed, 460 insertions(+), 209 deletions(-) create mode 100644 src/main/java/li/cil/oc2/common/bus/device/AbstractHardDiskDriveDevice.java create mode 100644 src/main/java/li/cil/oc2/common/bus/device/HardDiskDriveDevice.java create mode 100644 src/main/java/li/cil/oc2/common/bus/device/SparseHardDiskDriveDevice.java create mode 100644 src/main/java/li/cil/oc2/common/bus/device/package-info.java diff --git a/src/main/java/li/cil/oc2/Config.java b/src/main/java/li/cil/oc2/Config.java index cae11142..f379820a 100644 --- a/src/main/java/li/cil/oc2/Config.java +++ b/src/main/java/li/cil/oc2/Config.java @@ -1,5 +1,7 @@ package li.cil.oc2; public final class Config { - public static final long maxAllocatedData = 512 * Constants.MEGABYTE; + public static long maxAllocatedData = 512 * Constants.MEGABYTE; + + public static int maxHddSize = 8 * Constants.MEGABYTE; } diff --git a/src/main/java/li/cil/oc2/Constants.java b/src/main/java/li/cil/oc2/Constants.java index 984f61af..6954de27 100644 --- a/src/main/java/li/cil/oc2/Constants.java +++ b/src/main/java/li/cil/oc2/Constants.java @@ -1,5 +1,7 @@ package li.cil.oc2; +import li.cil.oc2.api.API; + public final class Constants { public static final int KILOBYTE = 1024; public static final int MEGABYTE = 1024 * KILOBYTE; @@ -11,4 +13,9 @@ public final class Constants { public static final String RAM_NAME = "ram"; public static final String HDD_NAME = "hdd"; + + public static final String HDD_INFO_NBT_TAG_NAME = API.MOD_ID + "hdd"; + public static final String HDD_SIZE_NBT_TAG_NAME = "size"; + public static final String HDD_BASE_NBT_TAG_NAME = "base"; + public static final String HDD_READONLY_NBT_TAG_NAME = "readonly"; } diff --git a/src/main/java/li/cil/oc2/OpenComputers.java b/src/main/java/li/cil/oc2/OpenComputers.java index 0aca1656..db07459d 100644 --- a/src/main/java/li/cil/oc2/OpenComputers.java +++ b/src/main/java/li/cil/oc2/OpenComputers.java @@ -49,7 +49,7 @@ public final class OpenComputers { public static final RegistryObject SCREEN_ITEM = ITEMS.register(Constants.SCREEN_BLOCK_NAME, () -> new BlockItem(SCREEN_BLOCK.get(), new Item.Properties().group(ITEM_GROUP))); public static final RegistryObject RAM_8M_ITEM = ITEMS.register(Constants.RAM_NAME, () -> new Item(new Item.Properties().group(ITEM_GROUP))); - public static final RegistryObject HDD_8M_ITEM = ITEMS.register(Constants.HDD_NAME, () -> new Item(new Item.Properties().maxStackSize(1).group(ITEM_GROUP))); + public static final RegistryObject HDD_ITEM = ITEMS.register(Constants.HDD_NAME, () -> new Item(new Item.Properties().maxStackSize(1).group(ITEM_GROUP))); public static final DeferredRegister> TILES = DeferredRegister.create(ForgeRegistries.TILE_ENTITIES, API.MOD_ID); public static final RegistryObject> COMPUTER_TILE_ENTITY = TILES.register(Constants.COMPUTER_BLOCK_NAME, () -> TileEntityType.Builder.create(ComputerTileEntity::new, COMPUTER_BLOCK.get()).build(null)); diff --git a/src/main/java/li/cil/oc2/common/block/entity/ComputerTileEntity.java b/src/main/java/li/cil/oc2/common/block/entity/ComputerTileEntity.java index aca94bd8..d021e5e9 100644 --- a/src/main/java/li/cil/oc2/common/block/entity/ComputerTileEntity.java +++ b/src/main/java/li/cil/oc2/common/block/entity/ComputerTileEntity.java @@ -2,6 +2,7 @@ package li.cil.oc2.common.block.entity; import it.unimi.dsi.fastutil.bytes.ByteArrayFIFOQueue; import li.cil.ceres.api.Serialized; +import li.cil.oc2.Constants; import li.cil.oc2.OpenComputers; import li.cil.oc2.api.bus.DeviceBusElement; import li.cil.oc2.api.bus.device.Device; @@ -70,7 +71,7 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic private static final String ITEMS_NBT_TAG_NAME = "items"; private static final int DEVICE_LOAD_RETRY_INTERVAL = 10 * 20; // In ticks. - private static final int VFS_INTERRUPT = 0x5; + private static final int VFS_INTERRUPT = 0x4; /////////////////////////////////////////////////////////////////// @@ -124,7 +125,11 @@ public final class ComputerTileEntity extends AbstractTileEntity implements ITic itemHandler.setStackInSlot(1, new ItemStack(OpenComputers.RAM_8M_ITEM.get())); itemHandler.setStackInSlot(2, new ItemStack(OpenComputers.RAM_8M_ITEM.get())); - itemHandler.setStackInSlot(4, new ItemStack(OpenComputers.HDD_8M_ITEM.get())); + final ItemStack hdd = new ItemStack(OpenComputers.HDD_ITEM.get()); + final CompoundNBT hddInfo = new CompoundNBT(); + hddInfo.putString(Constants.HDD_BASE_NBT_TAG_NAME, "linux"); + hdd.setTagInfo(Constants.HDD_INFO_NBT_TAG_NAME, hddInfo); + itemHandler.setStackInSlot(4, hdd); } public Terminal getTerminal() { diff --git a/src/main/java/li/cil/oc2/common/bus/device/AbstractHardDiskDriveDevice.java b/src/main/java/li/cil/oc2/common/bus/device/AbstractHardDiskDriveDevice.java new file mode 100644 index 00000000..6befa750 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/bus/device/AbstractHardDiskDriveDevice.java @@ -0,0 +1,242 @@ +package li.cil.oc2.common.bus.device; + +import li.cil.oc2.api.bus.device.vm.*; +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; +import net.minecraft.nbt.CompoundNBT; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.OptionalLong; +import java.util.UUID; + +public abstract class AbstractHardDiskDriveDevice extends AbstractObjectProxy implements VMDevice, VMDeviceLifecycleListener { + private static final UUID SERIALIZATION_KEY = UUID.fromString("8842cf60-a6e6-44d2-b442-380007625078"); + + private static final String DEVICE_NBT_TAG_NAME = "device"; + private static final String ADDRESS_NBT_TAG_NAME = "address"; + private static final String INTERRUPT_NBT_TAG_NAME = "interrupt"; + private static final String BLOB_HANDLE_NBT_TAG_NAME = "blob"; + + /////////////////////////////////////////////////////////////// + + private final UUID allocHandle = Allocator.createHandle(); + private BlobStorage.JobHandle jobHandle; + private T data; + private VirtIOBlockDevice device; + + /////////////////////////////////////////////////////////////// + + // Online persisted data. + private CompoundNBT deviceNbt; + private Long address; + private Integer interrupt; + + // Offline persisted data. + private UUID blobHandle; + + /////////////////////////////////////////////////////////////// + + protected AbstractHardDiskDriveDevice(final ItemStack stack) { + super(stack); + } + + @Override + public VMDeviceLoadResult load(final VMContext context) { + if (!allocateDevice(context)) { + return VMDeviceLoadResult.fail(); + } + + if (!claimAddress(context)) { + Allocator.freeMemory(allocHandle); + return VMDeviceLoadResult.fail(); + } + + if (!claimInterrupt(context)) { + Allocator.freeMemory(allocHandle); + return VMDeviceLoadResult.fail(); + } + + loadPersistedState(); + + return VMDeviceLoadResult.success(); + } + + @Override + public void handleLifecycleEvent(final VMDeviceLifecycleEventType event) { + switch (event) { + case RESUME_RUNNING: + awaitStorageOperation(); + break; + case UNLOAD: + unload(); + break; + } + } + + @Override + public Optional getSerializationKey() { + return Optional.of(SERIALIZATION_KEY); + } + + @Override + public CompoundNBT serializeNBT() { + final CompoundNBT nbt = new CompoundNBT(); + + if (data != null) { + final Optional optional = getSerializationStream(data); + optional.ifPresent(stream -> { + blobHandle = BlobStorage.validateHandle(blobHandle); + jobHandle = BlobStorage.submitSave(blobHandle, stream); + }); + if (!optional.isPresent()) { + BlobStorage.freeHandle(blobHandle); + blobHandle = null; + } + } + if (device != null) { + deviceNbt = NBTSerialization.serialize(device); + } + if (deviceNbt != null) { + nbt.put(DEVICE_NBT_TAG_NAME, deviceNbt); + } + if (address != null) { + nbt.putLong(ADDRESS_NBT_TAG_NAME, address); + } + if (interrupt != null) { + nbt.putInt(INTERRUPT_NBT_TAG_NAME, interrupt); + } + + if (blobHandle != null) { + nbt.putUniqueId(BLOB_HANDLE_NBT_TAG_NAME, blobHandle); + } + + return nbt; + } + + @Override + public void deserializeNBT(final CompoundNBT nbt) { + if (nbt.hasUniqueId(BLOB_HANDLE_NBT_TAG_NAME)) { + blobHandle = nbt.getUniqueId(BLOB_HANDLE_NBT_TAG_NAME); + } + + if (nbt.contains(DEVICE_NBT_TAG_NAME, NBTTagIds.TAG_COMPOUND)) { + deviceNbt = nbt.getCompound(DEVICE_NBT_TAG_NAME); + } + if (nbt.contains(ADDRESS_NBT_TAG_NAME, NBTTagIds.TAG_LONG)) { + address = nbt.getLong(ADDRESS_NBT_TAG_NAME); + } + if (nbt.contains(INTERRUPT_NBT_TAG_NAME, NBTTagIds.TAG_INT)) { + interrupt = nbt.getInt(INTERRUPT_NBT_TAG_NAME); + } + } + + /////////////////////////////////////////////////////////////// + + protected abstract int getSize(); + + protected abstract T createDevice(); + + protected abstract Optional getSerializationStream(T device); + + protected abstract OutputStream getDeserializationStream(T device); + + /////////////////////////////////////////////////////////////// + + private boolean allocateDevice(final VMContext context) { + if (!Allocator.claimMemory(allocHandle, getSize())) { + return false; + } + + data = createDevice(); + device = new VirtIOBlockDevice(context.getMemoryMap(), data); + + return true; + } + + private boolean claimAddress(final VMContext context) { + final OptionalLong claimedAddress; + if (this.address != null) { + claimedAddress = context.getMemoryRangeAllocator().claimMemoryRange(this.address, device); + } else { + claimedAddress = context.getMemoryRangeAllocator().claimMemoryRange(device); + } + + if (!claimedAddress.isPresent()) { + return false; + } + + this.address = claimedAddress.getAsLong(); + + return true; + } + + private boolean claimInterrupt(final VMContext context) { + final OptionalInt claimedInterrupt; + if (this.interrupt != null) { + claimedInterrupt = context.getInterruptAllocator().claimInterrupt(this.interrupt); + } else { + claimedInterrupt = context.getInterruptAllocator().claimInterrupt(); + } + + if (!claimedInterrupt.isPresent()) { + return false; + } + + this.interrupt = claimedInterrupt.getAsInt(); + + device.getInterrupt().set(this.interrupt, context.getInterruptController()); + + return true; + } + + private void loadPersistedState() { + if (blobHandle != null) { + jobHandle = BlobStorage.submitLoad(blobHandle, getDeserializationStream(data)); + } + if (deviceNbt != null) { + NBTSerialization.deserialize(deviceNbt, device); + } + } + + private void awaitStorageOperation() { + if (jobHandle != null) { + jobHandle.await(); + jobHandle = null; + } + } + + private void unload() { + // Since we cannot serialize the data in a regular serialize call due to the + // actual data being unloaded at that point, but want to permanently persist + // it (it's the contents of the block device) we need to serialize it in the + // unload, too. Don't need to wait for the job, though. + if (data != null) { + final Optional optional = getSerializationStream(data); + optional.ifPresent(stream -> { + blobHandle = BlobStorage.validateHandle(blobHandle); + BlobStorage.submitSave(blobHandle, stream); + }); + if (!optional.isPresent()) { + BlobStorage.freeHandle(blobHandle); + blobHandle = null; + } + } + + Allocator.freeMemory(allocHandle); + data = null; + + device = null; + deviceNbt = null; + address = null; + interrupt = null; + } +} diff --git a/src/main/java/li/cil/oc2/common/bus/device/HardDiskDriveDevice.java b/src/main/java/li/cil/oc2/common/bus/device/HardDiskDriveDevice.java new file mode 100644 index 00000000..4426acaa --- /dev/null +++ b/src/main/java/li/cil/oc2/common/bus/device/HardDiskDriveDevice.java @@ -0,0 +1,39 @@ +package li.cil.oc2.common.bus.device; + +import li.cil.sedna.device.block.ByteBufferBlockDevice; +import net.minecraft.item.ItemStack; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Optional; + +public final class HardDiskDriveDevice extends AbstractHardDiskDriveDevice { + private final int size; + private final boolean readonly; + + public HardDiskDriveDevice(final ItemStack stack, final int size, final boolean readonly) { + super(stack); + this.size = size; + this.readonly = readonly; + } + + @Override + protected int getSize() { + return size; + } + + @Override + protected ByteBufferBlockDevice createDevice() { + return ByteBufferBlockDevice.create(size, readonly); + } + + @Override + protected Optional getSerializationStream(final ByteBufferBlockDevice device) { + return Optional.of(device.getInputStream()); + } + + @Override + protected OutputStream getDeserializationStream(final ByteBufferBlockDevice device) { + return device.getOutputStream(); + } +} diff --git a/src/main/java/li/cil/oc2/common/bus/device/SparseHardDiskDriveDevice.java b/src/main/java/li/cil/oc2/common/bus/device/SparseHardDiskDriveDevice.java new file mode 100644 index 00000000..7ea760a4 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/bus/device/SparseHardDiskDriveDevice.java @@ -0,0 +1,113 @@ +package li.cil.oc2.common.bus.device; + +import li.cil.ceres.BinarySerialization; +import li.cil.sedna.api.device.BlockDevice; +import li.cil.sedna.device.block.SparseBlockDevice; +import li.cil.sedna.utils.ByteBufferInputStream; +import net.minecraft.item.ItemStack; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.util.Optional; + +public final class SparseHardDiskDriveDevice extends AbstractHardDiskDriveDevice { + private final BlockDevice base; + private final boolean readonly; + + public SparseHardDiskDriveDevice(final ItemStack stack, final BlockDevice base, final boolean readonly) { + super(stack); + this.base = base; + this.readonly = readonly; + } + + @Override + protected int getSize() { + return (int) base.getCapacity(); + } + + @Override + protected SparseBlockDevice createDevice() { + return new SparseBlockDevice(base, readonly); + } + + @Override + protected Optional getSerializationStream(final SparseBlockDevice device) { + if (device.getBlockCount() == 0) { + return Optional.empty(); + } + + return Optional.of(new SerializationStream(device)); + } + + @Override + protected OutputStream getDeserializationStream(final SparseBlockDevice device) { + return new DeserializationStream(device); + } + + private static final class SerializationStream extends InputStream { + private final SparseBlockDevice device; + private ByteBufferInputStream stream; + + private SerializationStream(final SparseBlockDevice device) { + this.device = device; + } + + @Override + public int read() { + ensureSerialized(); + return stream.read(); + } + + @Override + public int read(final byte[] b, final int off, final int len) { + ensureSerialized(); + return stream.read(b, off, len); + } + + @Override + public long skip(final long n) throws IOException { + return stream.skip(n); + } + + @Override + public int available() throws IOException { + return stream.available(); + } + + private void ensureSerialized() { + if (stream != null) { + return; + } + + stream = new ByteBufferInputStream(BinarySerialization.serialize(device)); + } + } + + private static final class DeserializationStream extends OutputStream { + private final SparseBlockDevice device; + private final ByteArrayOutputStream stream; + + public DeserializationStream(final SparseBlockDevice device) { + this.device = device; + stream = new ByteArrayOutputStream(); + } + + @Override + public void write(final int b) { + stream.write(b); + } + + @Override + public void write(final byte[] b, final int off, final int len) { + stream.write(b, off, len); + } + + @Override + public void close() { + BinarySerialization.deserialize(ByteBuffer.wrap(stream.toByteArray()), device); + } + } +} diff --git a/src/main/java/li/cil/oc2/common/bus/device/package-info.java b/src/main/java/li/cil/oc2/common/bus/device/package-info.java new file mode 100644 index 00000000..8e37ad75 --- /dev/null +++ b/src/main/java/li/cil/oc2/common/bus/device/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package li.cil.oc2.common.bus.device; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/li/cil/oc2/common/bus/device/provider/HardDriveItemDeviceProvider.java b/src/main/java/li/cil/oc2/common/bus/device/provider/HardDriveItemDeviceProvider.java index 9cf4c412..f4fe016b 100644 --- a/src/main/java/li/cil/oc2/common/bus/device/provider/HardDriveItemDeviceProvider.java +++ b/src/main/java/li/cil/oc2/common/bus/device/provider/HardDriveItemDeviceProvider.java @@ -1,226 +1,70 @@ package li.cil.oc2.common.bus.device.provider; +import li.cil.oc2.Config; import li.cil.oc2.Constants; import li.cil.oc2.OpenComputers; import li.cil.oc2.api.bus.device.Device; import li.cil.oc2.api.bus.device.provider.ItemDeviceQuery; -import li.cil.oc2.api.bus.device.vm.*; +import li.cil.oc2.common.bus.device.HardDiskDriveDevice; +import li.cil.oc2.common.bus.device.SparseHardDiskDriveDevice; 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.serialization.NBTSerialization; -import li.cil.oc2.common.util.ByteBufferInputStream; -import li.cil.oc2.common.util.ByteBufferOutputStream; 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.buildroot.Buildroot; import li.cil.sedna.device.block.ByteBufferBlockDevice; -import li.cil.sedna.device.virtio.VirtIOBlockDevice; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundNBT; import net.minecraftforge.common.util.LazyOptional; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -import java.util.Optional; -import java.util.OptionalInt; -import java.util.OptionalLong; -import java.util.UUID; +import java.io.IOException; +import java.util.Objects; public class HardDriveItemDeviceProvider extends AbstractItemDeviceProvider { + private static final Logger LOGGER = LogManager.getLogger(); + + /////////////////////////////////////////////////////////////////// + public HardDriveItemDeviceProvider() { - super(OpenComputers.HDD_8M_ITEM.get()); + super(OpenComputers.HDD_ITEM.get()); } /////////////////////////////////////////////////////////////////// @Override protected LazyOptional getItemDevice(final ItemDeviceQuery query) { - return LazyOptional.of(() -> new HardDiskDriveDevice(query.getItemStack())); - } + final ItemStack stack = query.getItemStack(); - /////////////////////////////////////////////////////////////////// - - private static final class HardDiskDriveDevice extends AbstractObjectProxy implements VMDevice, VMDeviceLifecycleListener { - private static final UUID SERIALIZATION_KEY = UUID.fromString("8842cf60-a6e6-44d2-b442-380007625078"); - - private static final String BLOB_HANDLE_NBT_TAG_NAME = "blob"; - private static final String DEVICE_NBT_TAG_NAME = "device"; - private static final String ADDRESS_NBT_TAG_NAME = "address"; - private static final String INTERRUPT_NBT_TAG_NAME = "interrupt"; - - final int HDD_SIZE = 2 * Constants.MEGABYTE; - - /////////////////////////////////////////////////////////////// - - private final UUID allocHandle = Allocator.createHandle(); - private BlobStorage.JobHandle jobHandle; - private ByteBufferBlockDevice data; - private VirtIOBlockDevice device; - - /////////////////////////////////////////////////////////////// - - private UUID blobHandle; - private CompoundNBT deviceNbt; - private Long address; - private Integer interrupt; - - /////////////////////////////////////////////////////////////// - - public HardDiskDriveDevice(final ItemStack stack) { - super(stack); + final CompoundNBT info = stack.getChildTag(Constants.HDD_INFO_NBT_TAG_NAME); + if (info == null) { + return LazyOptional.empty(); } - @Override - public VMDeviceLoadResult load(final VMContext context) { - if (!allocateDevice(context)) { - return VMDeviceLoadResult.fail(); + final boolean readonly = info.getBoolean(Constants.HDD_READONLY_NBT_TAG_NAME); + if (info.contains(Constants.HDD_BASE_NBT_TAG_NAME, NBTTagIds.TAG_STRING)) { + final String baseName = info.getString(Constants.HDD_BASE_NBT_TAG_NAME); + + BlockDevice base = null; + + // TODO Allow registering additional base file systems? + if (Objects.equals(baseName, "linux")) { + try { + base = ByteBufferBlockDevice.createFromStream(Buildroot.getRootFilesystem(), true); + } catch (final IOException e) { + LOGGER.error(e); + } } - if (!claimAddress(context)) { - return VMDeviceLoadResult.fail(); + if (base != null) { + final BlockDevice baseForClosure = base; + return LazyOptional.of(() -> new SparseHardDiskDriveDevice(stack, baseForClosure, readonly)); } - - if (!claimInterrupt(context)) { - return VMDeviceLoadResult.fail(); - } - - loadPersistedState(); - - return VMDeviceLoadResult.success(); + } else if (info.contains(Constants.HDD_SIZE_NBT_TAG_NAME, NBTTagIds.TAG_INT)) { + final int size = Math.max(0, Math.min(Config.maxHddSize, info.getInt(Constants.HDD_SIZE_NBT_TAG_NAME))); + return LazyOptional.of(() -> new HardDiskDriveDevice(stack, size, readonly)); } - @Override - public void handleLifecycleEvent(final VMDeviceLifecycleEventType event) { - switch (event) { - case RESUME_RUNNING: - awaitStorageOperation(); - break; - case UNLOAD: - unload(); - break; - } - } - - @Override - public Optional getSerializationKey() { - return Optional.of(SERIALIZATION_KEY); - } - - @Override - public CompoundNBT serializeNBT() { - final CompoundNBT nbt = new CompoundNBT(); - - if (data != null) { - blobHandle = BlobStorage.validateHandle(blobHandle); - nbt.putUniqueId(BLOB_HANDLE_NBT_TAG_NAME, blobHandle); - - jobHandle = BlobStorage.submitSave(blobHandle, new ByteBufferInputStream(data.getView())); - } - if (device != null) { - deviceNbt = NBTSerialization.serialize(device); - nbt.put(DEVICE_NBT_TAG_NAME, deviceNbt); - } - if (address != null) { - nbt.putLong(ADDRESS_NBT_TAG_NAME, address); - } - if (interrupt != null) { - nbt.putInt(INTERRUPT_NBT_TAG_NAME, interrupt); - } - - return nbt; - } - - @Override - public void deserializeNBT(final CompoundNBT nbt) { - if (nbt.hasUniqueId(BLOB_HANDLE_NBT_TAG_NAME)) { - blobHandle = nbt.getUniqueId(BLOB_HANDLE_NBT_TAG_NAME); - } - if (nbt.contains(DEVICE_NBT_TAG_NAME, NBTTagIds.TAG_COMPOUND)) { - deviceNbt = nbt.getCompound(DEVICE_NBT_TAG_NAME); - } - if (nbt.contains(ADDRESS_NBT_TAG_NAME, NBTTagIds.TAG_LONG)) { - address = nbt.getLong(ADDRESS_NBT_TAG_NAME); - } - if (nbt.contains(INTERRUPT_NBT_TAG_NAME, NBTTagIds.TAG_INT)) { - interrupt = nbt.getInt(INTERRUPT_NBT_TAG_NAME); - } - } - - /////////////////////////////////////////////////////////////// - - private boolean allocateDevice(final VMContext context) { - if (!Allocator.claimMemory(allocHandle, HDD_SIZE)) { - return false; - } - - data = ByteBufferBlockDevice.create(HDD_SIZE, false); - device = new VirtIOBlockDevice(context.getMemoryMap(), data); - - return true; - } - - private boolean claimAddress(final VMContext context) { - final OptionalLong claimedAddress; - if (this.address != null) { - claimedAddress = context.getMemoryRangeAllocator().claimMemoryRange(this.address, device); - } else { - claimedAddress = context.getMemoryRangeAllocator().claimMemoryRange(device); - } - - if (!claimedAddress.isPresent()) { - Allocator.freeMemory(allocHandle); - return false; - } - - this.address = claimedAddress.getAsLong(); - - return true; - } - - private boolean claimInterrupt(final VMContext context) { - final OptionalInt claimedInterrupt; - if (this.interrupt != null) { - claimedInterrupt = context.getInterruptAllocator().claimInterrupt(this.interrupt); - } else { - claimedInterrupt = context.getInterruptAllocator().claimInterrupt(); - } - - if (!claimedInterrupt.isPresent()) { - Allocator.freeMemory(allocHandle); - return false; - } - - this.interrupt = claimedInterrupt.getAsInt(); - - device.getInterrupt().set(this.interrupt, context.getInterruptController()); - - return true; - } - - private void loadPersistedState() { - if (blobHandle != null) { - jobHandle = BlobStorage.submitLoad(blobHandle, new ByteBufferOutputStream(data.getView())); - } - if (deviceNbt != null) { - NBTSerialization.deserialize(deviceNbt, device); - } - } - - private void awaitStorageOperation() { - if (jobHandle != null) { - jobHandle.await(); - jobHandle = null; - } - } - - private void unload() { - // Finish saves on unload to ensure future loads will read correct data. - awaitStorageOperation(); - - Allocator.freeMemory(allocHandle); - data = null; - - device = null; - address = null; - interrupt = null; - } + return LazyOptional.empty(); } } 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 bac8acd5..944c90aa 100644 --- a/src/main/java/li/cil/oc2/common/vm/VirtualMachine.java +++ b/src/main/java/li/cil/oc2/common/vm/VirtualMachine.java @@ -11,7 +11,6 @@ import li.cil.sedna.buildroot.Buildroot; import li.cil.sedna.device.block.ByteBufferBlockDevice; import li.cil.sedna.device.rtc.GoldfishRTC; import li.cil.sedna.device.serial.UART16550A; -import li.cil.sedna.device.virtio.VirtIOBlockDevice; import li.cil.sedna.device.virtio.VirtIOConsoleDevice; import li.cil.sedna.riscv.R5Board; import org.apache.logging.log4j.LogManager; @@ -30,9 +29,8 @@ public final class VirtualMachine { public static final int ACTUAL_CPU_FREQUENCY = REPORTED_CPU_FREQUENCY * 72; private static final int UART_INTERRUPT = 0x1; - private static final int HDD_INTERRUPT = 0x2; - private static final int RTC_INTERRUPT = 0x3; - private static final int RPC_INTERRUPT = 0x4; + private static final int RTC_INTERRUPT = 0x2; + private static final int RPC_INTERRUPT = 0x3; private static final ByteBufferBlockDevice ROOT_FS; @@ -56,7 +54,6 @@ public final class VirtualMachine { @Serialized public R5Board board; @Serialized public VirtualMachineDeviceBusAdapter vmAdapter; @Serialized public UART16550A uart; - @Serialized public VirtIOBlockDevice hdd; @Serialized public VirtIOConsoleDevice deviceBusSerialDevice; @Serialized public RPCAdapter rpcAdapter; @@ -79,11 +76,6 @@ public final class VirtualMachine { uart.getInterrupt().set(interrupt, interruptController)); memoryRangeAllocator.claimMemoryRange(uart); - hdd = new VirtIOBlockDevice(board.getMemoryMap(), ROOT_FS); - interruptAllocator.claimInterrupt(HDD_INTERRUPT).ifPresent(interrupt -> - hdd.getInterrupt().set(interrupt, interruptController)); - memoryRangeAllocator.claimMemoryRange(hdd); - final GoldfishRTC rtc = new GoldfishRTC(this.rtc); interruptAllocator.claimInterrupt(RTC_INTERRUPT).ifPresent(interrupt -> rtc.getInterrupt().set(interrupt, interruptController)); @@ -96,7 +88,7 @@ public final class VirtualMachine { rpcAdapter = new RPCAdapter(busController, deviceBusSerialDevice); - board.setBootArguments("root=/dev/vda ro"); + board.setBootArguments("root=/dev/vda rw"); board.setStandardOutputDevice(uart); }