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.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public final class OpenComputers {
|
||||
public static final RegistryObject<Item> SCREEN_ITEM = ITEMS.register(Constants.SCREEN_BLOCK_NAME, () -> new BlockItem(SCREEN_BLOCK.get(), new Item.Properties().group(ITEM_GROUP)));
|
||||
|
||||
public static final RegistryObject<Item> RAM_8M_ITEM = ITEMS.register(Constants.RAM_NAME, () -> new Item(new Item.Properties().group(ITEM_GROUP)));
|
||||
public static final RegistryObject<Item> HDD_8M_ITEM = ITEMS.register(Constants.HDD_NAME, () -> new Item(new Item.Properties().maxStackSize(1).group(ITEM_GROUP)));
|
||||
public static final RegistryObject<Item> HDD_ITEM = ITEMS.register(Constants.HDD_NAME, () -> new Item(new Item.Properties().maxStackSize(1).group(ITEM_GROUP)));
|
||||
|
||||
public static final DeferredRegister<TileEntityType<?>> TILES = DeferredRegister.create(ForgeRegistries.TILE_ENTITIES, API.MOD_ID);
|
||||
public static final RegistryObject<TileEntityType<ComputerTileEntity>> COMPUTER_TILE_ENTITY = TILES.register(Constants.COMPUTER_BLOCK_NAME, () -> TileEntityType.Builder.create(ComputerTileEntity::new, COMPUTER_BLOCK.get()).build(null));
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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<T extends BlockDevice> extends AbstractObjectProxy<ItemStack> 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<UUID> getSerializationKey() {
|
||||
return Optional.of(SERIALIZATION_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT serializeNBT() {
|
||||
final CompoundNBT nbt = new CompoundNBT();
|
||||
|
||||
if (data != null) {
|
||||
final Optional<InputStream> 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<InputStream> 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<InputStream> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<ByteBufferBlockDevice> {
|
||||
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<InputStream> getSerializationStream(final ByteBufferBlockDevice device) {
|
||||
return Optional.of(device.getInputStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected OutputStream getDeserializationStream(final ByteBufferBlockDevice device) {
|
||||
return device.getOutputStream();
|
||||
}
|
||||
}
|
||||
@@ -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<SparseBlockDevice> {
|
||||
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<InputStream> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
package li.cil.oc2.common.bus.device;
|
||||
|
||||
import mcp.MethodsReturnNonnullByDefault;
|
||||
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
@@ -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<Device> getItemDevice(final ItemDeviceQuery query) {
|
||||
return LazyOptional.of(() -> new HardDiskDriveDevice(query.getItemStack()));
|
||||
}
|
||||
final ItemStack stack = query.getItemStack();
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
|
||||
private static final class HardDiskDriveDevice extends AbstractObjectProxy<ItemStack> 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<UUID> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user