Minimalistic - "Well-it-enumerates-I-guess" PCI impl

This commit is contained in:
gruetzkopf
2022-06-20 10:03:42 +02:00
parent 9517813395
commit 525c024cee
23 changed files with 561 additions and 1 deletions

View File

@@ -20,6 +20,8 @@ public final class Config {
@Path("energy.blocks") public static int chargerEnergyStorage = 10000;
@Path("energy.blocks") public static int projectorEnergyPerTick = 20;
@Path("energy.blocks") public static int projectorEnergyStorage = 2000;
@Path("energy.blocks") public static int cardCageEnergyPerTick = 20;
@Path("energy.blocks") public static int cardCageEnergyStorage = 2000;
@Path("energy.entities") public static int robotEnergyPerTick = 5;
@Path("energy.entities") public static int robotEnergyStorage = 750000;
@@ -60,6 +62,10 @@ public final class Config {
return projectorEnergyStorage > 0 && projectorEnergyPerTick > 0;
}
public static boolean cardCagesUseEnergy() {
return cardCageEnergyStorage > 0 && cardCageEnergyPerTick > 0;
}
public static boolean robotsUseEnergy() {
return robotEnergyPerTick > 0 && robotEnergyStorage > 0;
}

View File

@@ -2,8 +2,10 @@
package li.cil.oc2.common.block;
import li.cil.oc2.common.blockentity.VxlanBlockEntity;
import li.cil.oc2.common.util.RegistryUtils;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
@@ -25,6 +27,8 @@ public final class Blocks {
public static final RegistryObject<ProjectorBlock> PROJECTOR = BLOCKS.register("projector", ProjectorBlock::new);
public static final RegistryObject<RedstoneInterfaceBlock> REDSTONE_INTERFACE = BLOCKS.register("redstone_interface", RedstoneInterfaceBlock::new);
public static final RegistryObject<VxlanBlock> VXLAN_HUB = BLOCKS.register("vxlan_hub", VxlanBlock::new);
public static final RegistryObject<PciCardCageBlock> PCI_CARD_CAGE = BLOCKS.register("pci_card_cage", PciCardCageBlock::new);
///////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,100 @@
/* SPDX-License-Identifier: MIT */
package li.cil.oc2.common.block;
import li.cil.oc2.common.Config;
import li.cil.oc2.common.blockentity.BlockEntities;
import li.cil.oc2.common.blockentity.TickableBlockEntity;
import li.cil.oc2.common.util.VoxelShapeUtils;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.material.Material;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import javax.annotation.Nullable;
public final class PciCardCageBlock extends HorizontalDirectionalBlock implements EntityBlock, EnergyConsumingBlock {
public static final BooleanProperty LIT = BlockStateProperties.LIT;
// We bake the visual indents on the front and sides into the collision shape, to prevent stuff being
// placeable on those sides, such as network connectors, torches, etc.
private static final VoxelShape NEG_Z_SHAPE = Shapes.join(Shapes.block(), Shapes.or(
Shapes.box(0 / 16f, 2 / 16f, 2 / 16f, 1 / 16f, 6 / 16f, 14 / 16f),
Shapes.box(15 / 16f, 2 / 16f, 2 / 16f, 16 / 16f, 6 / 16f, 14 / 16f),
Shapes.box(4 / 16f, 4 / 16f, 0 / 16f, 12 / 16f, 12 / 16f, 2 / 16f)
), (a, b) -> a && !b);
private static final VoxelShape NEG_X_SHAPE = VoxelShapeUtils.rotateHorizontalClockwise(NEG_Z_SHAPE);
private static final VoxelShape POS_Z_SHAPE = VoxelShapeUtils.rotateHorizontalClockwise(NEG_X_SHAPE);
private static final VoxelShape POS_X_SHAPE = VoxelShapeUtils.rotateHorizontalClockwise(POS_Z_SHAPE);
public PciCardCageBlock() {
super(Properties
.of(Material.METAL)
.sound(SoundType.METAL)
.lightLevel(state -> state.getValue(LIT) ? 8 : 0)
.strength(1.5f, 6.0f));
registerDefaultState(getStateDefinition().any()
.setValue(FACING, Direction.NORTH)
.setValue(LIT, false));
}
///////////////////////////////////////////////////////////////////
@Override
public int getEnergyConsumption() {
if (Config.cardCagesUseEnergy()) {
return Config.cardCageEnergyPerTick;
} else {
return 0;
}
}
@Override
public BlockEntity newBlockEntity(final BlockPos pos, final BlockState state) {
return BlockEntities.PCI_CARD_CAGE.get().create(pos, state);
}
@Nullable
@Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(final Level level, final BlockState state, final BlockEntityType<T> type) {
return TickableBlockEntity.createServerTicker(level, type, BlockEntities.PROJECTOR.get());
}
@SuppressWarnings("deprecation")
@Override
public VoxelShape getShape(final BlockState state, final BlockGetter level, final BlockPos blockPos, final CollisionContext context) {
return switch (state.getValue(FACING)) {
case NORTH -> NEG_Z_SHAPE;
case SOUTH -> POS_Z_SHAPE;
case WEST -> NEG_X_SHAPE;
default -> POS_X_SHAPE;
};
}
@Override
public BlockState getStateForPlacement(final BlockPlaceContext context) {
return super.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite());
}
///////////////////////////////////////////////////////////////////
protected void createBlockStateDefinition(final StateDefinition.Builder<Block, BlockState> builder) {
builder.add(FACING, LIT);
}
}

View File

@@ -28,6 +28,8 @@ public final class BlockEntities {
public static final RegistryObject<BlockEntityType<ProjectorBlockEntity>> PROJECTOR = register(Blocks.PROJECTOR, ProjectorBlockEntity::new);
public static final RegistryObject<BlockEntityType<RedstoneInterfaceBlockEntity>> REDSTONE_INTERFACE = register(Blocks.REDSTONE_INTERFACE, RedstoneInterfaceBlockEntity::new);
public static final RegistryObject<BlockEntityType<VxlanBlockEntity>> VXLAN_HUB = register(Blocks.VXLAN_HUB, VxlanBlockEntity::new);
public static final RegistryObject<BlockEntityType<PciCardCageBlockEntity>> PCI_CARD_CAGE = register(Blocks.PCI_CARD_CAGE, PciCardCageBlockEntity::new);
///////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,122 @@
/* SPDX-License-Identifier: MIT */
package li.cil.oc2.common.blockentity;
import li.cil.oc2.common.Config;
import li.cil.oc2.common.block.PciCardCageBlock;
import li.cil.oc2.common.bus.device.vm.block.PciCardCageDevice;
import li.cil.oc2.common.capabilities.Capabilities;
import li.cil.oc2.common.energy.FixedEnergyStorage;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.state.BlockState;
import javax.annotation.Nullable;
public final class PciCardCageBlockEntity extends ModBlockEntity implements TickableBlockEntity {
private static final String ENERGY_TAG_NAME = "energy";
private static final String HAS_ENERGY_TAG_NAME = "has_energy";
///////////////////////////////////////////////////////////////
private final PciCardCageDevice cardCageDevice = new PciCardCageDevice(this, this::handleMountedChanged);
private boolean isMounted, hasEnergy;
private final FixedEnergyStorage energy = new FixedEnergyStorage(Config.cardCageEnergyStorage);
///////////////////////////////////////////////////////////////
public PciCardCageBlockEntity(final BlockPos pos, final BlockState state) {
super(BlockEntities.PCI_CARD_CAGE.get(), pos, state);
}
///////////////////////////////////////////////////////////////
private void handleMountedChanged(final boolean value) {
}
public boolean hasEnergy() {
return hasEnergy;
}
@Override
public void serverTick() {
if (!isMounted) {
return;
}
final boolean isPowered;
if (Config.cardCagesUseEnergy()) {
isPowered = energy.extractEnergy(Config.cardCageEnergyPerTick, true) >= Config.cardCageEnergyPerTick;
if (isPowered) {
energy.extractEnergy(Config.cardCageEnergyPerTick, false);
}
} else {
isPowered = true;
}
}
@Override
public CompoundTag getUpdateTag() {
final CompoundTag tag = super.getUpdateTag();
tag.putBoolean(HAS_ENERGY_TAG_NAME, hasEnergy);
return tag;
}
@Override
public void handleUpdateTag(final CompoundTag tag) {
super.handleUpdateTag(tag);
hasEnergy = tag.getBoolean(HAS_ENERGY_TAG_NAME);
}
@Override
protected void saveAdditional(final CompoundTag tag) {
super.saveAdditional(tag);
tag.put(ENERGY_TAG_NAME, energy.serializeNBT());
}
@Override
public void load(final CompoundTag tag) {
super.load(tag);
energy.deserializeNBT(tag.getCompound(ENERGY_TAG_NAME));
}
@SuppressWarnings("deprecation")
@Override
public void setBlockState(final BlockState state) {
super.setBlockState(state);
}
///////////////////////////////////////////////////////////////
@Override
protected void collectCapabilities(final CapabilityCollector collector, @Nullable final Direction direction) {
if (Config.cardCagesUseEnergy()) {
collector.offer(Capabilities.energyStorage(), energy);
}
if (direction == getBlockState().getValue(PciCardCageBlock.FACING).getOpposite()) {
collector.offer(Capabilities.device(), cardCageDevice);
}
}
///////////////////////////////////////////////////////////////
}

View File

@@ -0,0 +1,139 @@
/* SPDX-License-Identifier: MIT */
package li.cil.oc2.common.bus.device.vm.block;
import it.unimi.dsi.fastutil.booleans.BooleanConsumer;
import li.cil.oc2.api.bus.device.vm.VMDevice;
import li.cil.oc2.api.bus.device.vm.VMDeviceLoadResult;
import li.cil.oc2.api.bus.device.vm.context.VMContext;
import li.cil.oc2.common.Constants;
import li.cil.oc2.common.bus.device.util.IdentityProxy;
import li.cil.oc2.common.bus.device.util.OptionalAddress;
import li.cil.oc2.common.serialization.BlobStorage;
import li.cil.oc2.common.util.NBTTagIds;
import li.cil.oc2.common.vm.device.PciRootPortDevice;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.entity.BlockEntity;
import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.UUID;
public final class PciCardCageDevice extends IdentityProxy<BlockEntity> implements VMDevice {
private static final String ADDRESS_TAG_NAME = "address";
private static final String BLOB_HANDLE_TAG_NAME = "blob";
public static final int BUS_COUNT = 16;
public static final int WINDOW_SIZE = 16 * 1024 * 1024;
///////////////////////////////////////////////////////////////
private final BooleanConsumer onMountedChanged;
@Nullable private PciRootPortDevice device;
///////////////////////////////////////////////////////////////
private final OptionalAddress address = new OptionalAddress();
@Nullable private UUID blobHandle;
///////////////////////////////////////////////////////////////
public PciCardCageDevice(final BlockEntity identity, final BooleanConsumer onMountedChanged) {
super(identity);
this.onMountedChanged = onMountedChanged;
}
///////////////////////////////////////////////////////////////
@Override
public VMDeviceLoadResult mount(final VMContext context) {
if (!allocateDevice(context)) {
return VMDeviceLoadResult.fail();
}
assert device != null;
if (!address.claim(context, device)) {
return VMDeviceLoadResult.fail();
}
onMountedChanged.accept(true);
return VMDeviceLoadResult.success();
}
@Override
public void unmount() {
final PciRootPortDevice pciRootPortDevice = device;
device = null;
if (pciRootPortDevice != null) {
pciRootPortDevice.close();
}
if (blobHandle != null) {
BlobStorage.close(blobHandle);
}
onMountedChanged.accept(false);
}
@Override
public void dispose() {
if (blobHandle != null) {
BlobStorage.delete(blobHandle);
blobHandle = null;
}
address.clear();
}
@Override
public CompoundTag serializeNBT() {
final CompoundTag tag = new CompoundTag();
if (blobHandle != null) {
tag.putUUID(BLOB_HANDLE_TAG_NAME, blobHandle);
}
if (address.isPresent()) {
tag.putLong(ADDRESS_TAG_NAME, address.getAsLong());
}
return tag;
}
@Override
public void deserializeNBT(final CompoundTag tag) {
if (tag.hasUUID(BLOB_HANDLE_TAG_NAME)) {
blobHandle = tag.getUUID(BLOB_HANDLE_TAG_NAME);
}
if (tag.contains(ADDRESS_TAG_NAME, NBTTagIds.TAG_LONG)) {
address.set(tag.getLong(ADDRESS_TAG_NAME));
}
}
///////////////////////////////////////////////////////////////
private boolean allocateDevice(final VMContext context) {
if (!context.getMemoryAllocator().claimMemory(Constants.PAGE_SIZE)) {
return false;
}
try {
device = createPciRootPortDevice();
} catch (final IOException e) {
return false;
}
return true;
}
private PciRootPortDevice createPciRootPortDevice() throws IOException {
blobHandle = BlobStorage.validateHandle(blobHandle);
final FileChannel channel = BlobStorage.getOrOpen(blobHandle);
final MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, WINDOW_SIZE * 2);
return new PciRootPortDevice(BUS_COUNT, WINDOW_SIZE, buffer);
}
}

View File

@@ -35,6 +35,7 @@ public final class Items {
public static final RegistryObject<Item> PROJECTOR = register(Blocks.PROJECTOR);
public static final RegistryObject<Item> REDSTONE_INTERFACE = register(Blocks.REDSTONE_INTERFACE);
public static final RegistryObject<Item> VXLAN_HUB = register(Blocks.VXLAN_HUB);
public static final RegistryObject<Item> PCI_CARD_CAGE = register(Blocks.PCI_CARD_CAGE);
///////////////////////////////////////////////////////////////////

View File

@@ -79,6 +79,7 @@ public abstract class AbstractVirtualMachine implements VirtualMachine {
state.board.getCpu().setFrequency(Constants.CPU_FREQUENCY);
state.board.setBootArguments("root=/dev/vda rw");
state.board.setStandardOutputDevice(state.builtinDevices.uart);
}
///////////////////////////////////////////////////////////////////

View File

@@ -0,0 +1,94 @@
/* SPDX-License-Identifier: MIT */
package li.cil.oc2.common.vm.device;
import li.cil.sedna.api.device.MemoryMappedDevice;
import li.cil.sedna.api.memory.MemoryAccessException;
import li.cil.sedna.utils.DirectByteBufferUtils;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public final class PciRootPortDevice implements MemoryMappedDevice {
///////////////////////////////////////////////////////////////
private final ByteBuffer buffer;
private int length;
///////////////////////////////////////////////////////////////
public PciRootPortDevice(final int bus_count, final int window_size, final ByteBuffer buffer) {
length = window_size * 2;
if (buffer.capacity() < length) {
throw new IllegalArgumentException("Buffer too small.");
}
this.buffer = buffer.order(ByteOrder.LITTLE_ENDIAN);
this.buffer.putInt(0, 0x12345678);
this.buffer.putInt(4, 0);
this.buffer.putInt(8, 0xFF000000);
this.buffer.putInt(12, 0x00000101);
this.buffer.putInt(16, 0x40000000);
this.buffer.putInt(0x2C, 0x12345678);
}
///////////////////////////////////////////////////////////////
public void close() {
synchronized (buffer) {
length = 0;
DirectByteBufferUtils.release(buffer);
}
}
public boolean hasChanges() {
return false;
}
@Override
public int getLength() {
return length;
}
@Override
public long load(final int offset, final int sizeLog2) throws MemoryAccessException {
if (offset >= 0 && offset <= length - (1 << sizeLog2)) {
System.out.println(String.format("PCI config read: %x", offset));
return switch (sizeLog2) {
case 0 -> buffer.get(offset);
case 1 -> buffer.getShort(offset);
case 2 -> buffer.getInt(offset);
case 3 -> buffer.getLong(offset);
default -> throw new IllegalArgumentException();
};
} else {
return 0;
}
}
@Override
public void store(final int offset, final long value, final int sizeLog2) throws MemoryAccessException {
if (offset >= 0 && offset <= length - (1 << sizeLog2)) {
System.out.println(String.format("PCI config write: %x %x %x", offset, value, sizeLog2));
switch (sizeLog2) {
case 0 -> buffer.put(offset, (byte) value);
case 1 -> buffer.putShort(offset, (short) value);
case 2 -> buffer.putInt(offset, (int) value);
case 3 -> buffer.putLong(offset, value);
default -> throw new IllegalArgumentException();
}
}
}
///////////////////////////////////////////////////////////////
}

View File

@@ -2,11 +2,13 @@
package li.cil.oc2.common.vm.provider;
import li.cil.oc2.common.vm.device.PciRootPortDevice;
import li.cil.oc2.common.vm.device.SimpleFramebufferDevice;
import li.cil.sedna.devicetree.DeviceTreeRegistry;
public final class DeviceTreeProviders {
public static void initialize() {
DeviceTreeRegistry.putProvider(SimpleFramebufferDevice.class, new SimpleFramebufferDeviceProvider());
DeviceTreeRegistry.putProvider(PciRootPortDevice.class, new PciRootPortDeviceProvider());
}
}

View File

@@ -0,0 +1,48 @@
/* SPDX-License-Identifier: MIT */
package li.cil.oc2.common.vm.provider;
import li.cil.oc2.common.vm.device.PciRootPortDevice;
import li.cil.sedna.api.device.Device;
import li.cil.sedna.api.device.MemoryMappedDevice;
import li.cil.sedna.api.devicetree.DeviceNames;
import li.cil.sedna.api.devicetree.DevicePropertyNames;
import li.cil.sedna.api.devicetree.DeviceTree;
import li.cil.sedna.api.devicetree.DeviceTreeProvider;
import li.cil.sedna.api.memory.MappedMemoryRange;
import li.cil.sedna.api.memory.MemoryMap;
import java.util.Optional;
public final class PciRootPortDeviceProvider implements DeviceTreeProvider {
@Override
public Optional<String> getName(final Device device) {
return Optional.of("pci");
}
@Override
public Optional<DeviceTree> createNode(final DeviceTree root, final MemoryMap memoryMap, final Device device, final String deviceName) {
final Optional<MappedMemoryRange> range = memoryMap.getMemoryRange((MemoryMappedDevice) device);
return range.map(r -> {
final DeviceTree chosen = root.find("/soc");
//chosen.addProp(DevicePropertyNames.RANGES);
return chosen.getChild(deviceName, r.address());
});
}
@Override
public void visit(final DeviceTree node, final MemoryMap memoryMap, final Device device) {
final PciRootPortDevice pr = (PciRootPortDevice) device;
final Optional<MappedMemoryRange> range = memoryMap.getMemoryRange((MemoryMappedDevice) device);
node
.addProp(DevicePropertyNames.COMPATIBLE, "pci-host-cam-generic")
.addProp(DevicePropertyNames.DEVICE_TYPE, DeviceNames.PCI)
.addProp(DevicePropertyNames.NUM_ADDRESS_CELLS,3)
.addProp(DevicePropertyNames.NUM_SIZE_CELLS, 2)
.addProp("bus-range", 0, 1)
.addProp("linux,pci-probe-only", 1)
.addProp(DevicePropertyNames.RANGES, 0x02000000, 0, 0x40000000, 0x40000000, 0, 0x20000000);
}
}

View File

@@ -24,7 +24,7 @@ public final class SimpleFramebufferDeviceProvider implements DeviceTreeProvider
final Optional<MappedMemoryRange> range = memoryMap.getMemoryRange((MemoryMappedDevice) device);
return range.map(r -> {
final DeviceTree chosen = root.find("/chosen");
chosen.addProp(DevicePropertyNames.RANGES);
//chosen.addProp(DevicePropertyNames.RANGES);
return chosen.getChild(deviceName, r.address());
});

View File

@@ -28,6 +28,8 @@ public final class ModBlockStateProvider extends BlockStateProvider {
private static final ResourceLocation NETWORK_HUB_MODEL = new ResourceLocation(API.MOD_ID, "block/network_hub");
private static final ResourceLocation PROJECTOR_MODEL = new ResourceLocation(API.MOD_ID, "block/projector");
private static final ResourceLocation REDSTONE_INTERFACE_MODEL = new ResourceLocation(API.MOD_ID, "block/redstone_interface");
private static final ResourceLocation PCI_CARD_CAGE_MODEL = new ResourceLocation(API.MOD_ID, "block/pci_card_cage");
public ModBlockStateProvider(final DataGenerator generator, final ExistingFileHelper existingFileHelper) {
super(generator, API.MOD_ID, existingFileHelper);
@@ -57,6 +59,7 @@ public final class ModBlockStateProvider extends BlockStateProvider {
horizontalBlock(Blocks.NETWORK_SWITCH, Items.NETWORK_SWITCH, NETWORK_HUB_MODEL);
horizontalBlock(Blocks.PROJECTOR, Items.PROJECTOR, PROJECTOR_MODEL);
horizontalBlock(Blocks.REDSTONE_INTERFACE, Items.REDSTONE_INTERFACE, REDSTONE_INTERFACE_MODEL);
horizontalBlock(Blocks.PCI_CARD_CAGE, Items.PCI_CARD_CAGE, PCI_CARD_CAGE_MODEL);
registerCableStates();
}

View File

@@ -0,0 +1,34 @@
{
"variants": {
"facing=north,lit=false": {
"model": "oc2:block/pci_card_cage"
},
"facing=south,lit=false": {
"model": "oc2:block/pci_card_cage",
"y": 180
},
"facing=west,lit=false": {
"model": "oc2:block/pci_card_cage",
"y": 270
},
"facing=east,lit=false": {
"model": "oc2:block/pci_card_cage",
"y": 90
},
"facing=north,lit=true": {
"model": "oc2:block/pci_card_cage"
},
"facing=south,lit=true": {
"model": "oc2:block/pci_card_cage",
"y": 180
},
"facing=west,lit=true": {
"model": "oc2:block/pci_card_cage",
"y": 270
},
"facing=east,lit=true": {
"model": "oc2:block/pci_card_cage",
"y": 90
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
{
"parent": "oc2:block/pci_card_cage"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B