diff --git a/hw/xbox/mcpx/nvnet/nvnet.c b/hw/xbox/mcpx/nvnet/nvnet.c index 1a4d50ad83..0590601dc8 100644 --- a/hw/xbox/mcpx/nvnet/nvnet.c +++ b/hw/xbox/mcpx/nvnet/nvnet.c @@ -65,11 +65,13 @@ typedef struct NvNetState { uint8_t regs[MMIO_SIZE]; uint32_t phy_regs[6]; - uint8_t tx_ring_index; - uint8_t rx_ring_index; uint8_t tx_dma_buf[TX_ALLOC_BUFSIZE]; uint32_t tx_dma_buf_offset; uint8_t rx_dma_buf[RX_ALLOC_BUFSIZE]; + + /* Deprecated */ + uint8_t tx_ring_index; + uint8_t rx_ring_index; } NvNetState; struct RingDesc { @@ -110,8 +112,13 @@ static const char *nvnet_get_reg_name(hwaddr addr) R(NVNET_RING_SIZE) R(NVNET_UNKNOWN_TRANSMITTER_REG) R(NVNET_LINKSPEED) + R(NVNET_TX_RING_CURRENT_DESC_PHYS_ADDR) + R(NVNET_RX_RING_CURRENT_DESC_PHYS_ADDR) + R(NVNET_TX_CURRENT_BUFFER_PHYS_ADDR) + R(NVNET_RX_CURRENT_BUFFER_PHYS_ADDR) R(NVNET_UNKNOWN_SETUP_REG5) - R(NVNET_UNKNOWN_SETUP_REG3) + R(NVNET_TX_RING_NEXT_DESC_PHYS_ADDR) + R(NVNET_RX_RING_NEXT_DESC_PHYS_ADDR) R(NVNET_UNKNOWN_SETUP_REG8) R(NVNET_UNKNOWN_SETUP_REG7) R(NVNET_TX_RX_CONTROL) @@ -229,6 +236,19 @@ static uint16_t get_rx_ring_size(NvNetState *s) return GET_MASK(ring_size, NVNET_RING_SIZE_RX) + 1; } +static void reset_descriptor_ring_pointers(NvNetState *s) +{ + uint32_t base_desc_addr; + + base_desc_addr = nvnet_get_reg(s, NVNET_TX_RING_PHYS_ADDR, 4); + nvnet_set_reg(s, NVNET_TX_RING_CURRENT_DESC_PHYS_ADDR, base_desc_addr, 4); + nvnet_set_reg(s, NVNET_TX_RING_NEXT_DESC_PHYS_ADDR, base_desc_addr, 4); + + base_desc_addr = nvnet_get_reg(s, NVNET_RX_RING_PHYS_ADDR, 4); + nvnet_set_reg(s, NVNET_RX_RING_CURRENT_DESC_PHYS_ADDR, base_desc_addr, 4); + nvnet_set_reg(s, NVNET_RX_RING_NEXT_DESC_PHYS_ADDR, base_desc_addr, 4); +} + static ssize_t nvnet_dma_packet_to_guest(NvNetState *s, const uint8_t *buf, size_t size) { @@ -238,19 +258,28 @@ static ssize_t nvnet_dma_packet_to_guest(NvNetState *s, const uint8_t *buf, uint32_t ctrl = nvnet_get_reg(s, NVNET_TX_RX_CONTROL, 4); nvnet_set_reg(s, NVNET_TX_RX_CONTROL, ctrl & ~NVNET_TX_RX_CONTROL_IDLE, 4); + uint32_t base_desc_addr = nvnet_get_reg(s, NVNET_RX_RING_PHYS_ADDR, 4); + uint32_t max_desc_addr = + base_desc_addr + get_rx_ring_size(s) * sizeof(struct RingDesc); + uint32_t cur_desc_addr = + nvnet_get_reg(s, NVNET_RX_RING_NEXT_DESC_PHYS_ADDR, 4); + if ((cur_desc_addr < base_desc_addr) || + ((cur_desc_addr + sizeof(struct RingDesc)) > max_desc_addr)) { + cur_desc_addr = base_desc_addr; + } + nvnet_set_reg(s, NVNET_RX_RING_CURRENT_DESC_PHYS_ADDR, cur_desc_addr, 4); + struct RingDesc desc; - s->rx_ring_index %= get_rx_ring_size(s); - dma_addr_t rx_ring_addr = nvnet_get_reg(s, NVNET_RX_RING_PHYS_ADDR, 4); - rx_ring_addr += s->rx_ring_index * sizeof(desc); - pci_dma_read(d, rx_ring_addr, &desc, sizeof(desc)); + pci_dma_read(d, cur_desc_addr, &desc, sizeof(desc)); uint32_t buffer_addr = le32_to_cpu(desc.buffer_addr); uint16_t length = le16_to_cpu(desc.length); uint16_t flags = le16_to_cpu(desc.flags); - NVNET_DPRINTF("RX: Looking at ring descriptor %d (0x%" HWADDR_PRIx "): " + NVNET_DPRINTF("RX: Looking at ring descriptor %zd (0x%x): " "Buffer: 0x%x, Length: 0x%x, Flags: 0x%x\n", - s->rx_ring_index, rx_ring_addr, buffer_addr, length, flags); + (cur_desc_addr - base_desc_addr) / sizeof(struct RingDesc), + cur_desc_addr, buffer_addr, length, flags); if (flags & NV_RX_AVAIL) { assert((length + 1) >= size); // FIXME @@ -264,7 +293,7 @@ static ssize_t nvnet_dma_packet_to_guest(NvNetState *s, const uint8_t *buf, desc.length = cpu_to_le16(length); desc.flags = cpu_to_le16(flags); - pci_dma_write(d, rx_ring_addr, &desc, sizeof(desc)); + pci_dma_write(d, cur_desc_addr, &desc, sizeof(desc)); NVNET_DPRINTF("Updated ring descriptor: Length: 0x%x, Flags: 0x%x\n", length, flags); @@ -274,7 +303,12 @@ static ssize_t nvnet_dma_packet_to_guest(NvNetState *s, const uint8_t *buf, nvnet_set_reg(s, NVNET_IRQ_STATUS, irq_status | NVNET_IRQ_STATUS_RX, 4); nvnet_update_irq(s); - s->rx_ring_index += 1; + uint32_t next_desc_addr = cur_desc_addr + sizeof(struct RingDesc); + if (next_desc_addr >= max_desc_addr) { + next_desc_addr = base_desc_addr; + } + nvnet_set_reg(s, NVNET_RX_RING_NEXT_DESC_PHYS_ADDR, next_desc_addr, 4); + rval = size; } else { NVNET_DPRINTF("Could not find free buffer!\n"); @@ -295,28 +329,37 @@ static ssize_t nvnet_dma_packet_from_guest(NvNetState *s) uint32_t ctrl = nvnet_get_reg(s, NVNET_TX_RX_CONTROL, 4); nvnet_set_reg(s, NVNET_TX_RX_CONTROL, ctrl & ~NVNET_TX_RX_CONTROL_IDLE, 4); + uint32_t base_desc_addr = nvnet_get_reg(s, NVNET_TX_RING_PHYS_ADDR, 4); + uint32_t max_desc_addr = + base_desc_addr + get_tx_ring_size(s) * sizeof(struct RingDesc); + for (int i = 0; i < get_tx_ring_size(s); i++) { + uint32_t cur_desc_addr = + nvnet_get_reg(s, NVNET_TX_RING_NEXT_DESC_PHYS_ADDR, 4); + if ((cur_desc_addr < base_desc_addr) || + ((cur_desc_addr + sizeof(struct RingDesc)) > max_desc_addr)) { + cur_desc_addr = base_desc_addr; + } + nvnet_set_reg(s, NVNET_TX_RING_CURRENT_DESC_PHYS_ADDR, cur_desc_addr, + 4); + struct RingDesc desc; - s->tx_ring_index %= get_tx_ring_size(s); - dma_addr_t tx_ring_addr = nvnet_get_reg(s, NVNET_TX_RING_PHYS_ADDR, 4); - tx_ring_addr += s->tx_ring_index * sizeof(desc); - pci_dma_read(d, tx_ring_addr, &desc, sizeof(desc)); + pci_dma_read(d, cur_desc_addr, &desc, sizeof(desc)); uint32_t buffer_addr = le32_to_cpu(desc.buffer_addr); uint16_t length = le16_to_cpu(desc.length) + 1; uint16_t flags = le16_to_cpu(desc.flags); - NVNET_DPRINTF("TX: Looking at ring desc %d (%" HWADDR_PRIx "): " + NVNET_DPRINTF("TX: Looking at ring desc %zd (%x): " "Buffer: 0x%x, Length: 0x%x, Flags: 0x%x\n", - s->tx_ring_index, tx_ring_addr, buffer_addr, length, - flags); + (cur_desc_addr - base_desc_addr) / + sizeof(struct RingDesc), + cur_desc_addr, buffer_addr, length, flags); if (!(flags & NV_TX_VALID)) { break; } - s->tx_ring_index += 1; - assert((s->tx_dma_buf_offset + length) <= sizeof(s->tx_dma_buf)); pci_dma_read(d, buffer_addr, &s->tx_dma_buf[s->tx_dma_buf_offset], @@ -335,7 +378,13 @@ static ssize_t nvnet_dma_packet_from_guest(NvNetState *s) NV_TX_ERROR); desc.flags = cpu_to_le16(flags); - pci_dma_write(d, tx_ring_addr, &desc, sizeof(desc)); + pci_dma_write(d, cur_desc_addr, &desc, sizeof(desc)); + + uint32_t next_desc_addr = cur_desc_addr + sizeof(struct RingDesc); + if (next_desc_addr >= max_desc_addr) { + next_desc_addr = base_desc_addr; + } + nvnet_set_reg(s, NVNET_TX_RING_NEXT_DESC_PHYS_ADDR, next_desc_addr, 4); if (is_last_packet) { // FIXME @@ -628,8 +677,7 @@ static void nvnet_mmio_write(void *opaque, hwaddr addr, uint64_t val, } if (val & NVNET_TX_RX_CONTROL_RESET) { - s->tx_ring_index = 0; - s->rx_ring_index = 0; + reset_descriptor_ring_pointers(s); s->tx_dma_buf_offset = 0; } @@ -638,13 +686,9 @@ static void nvnet_mmio_write(void *opaque, hwaddr addr, uint64_t val, nvnet_set_reg(s, NVNET_IRQ_STATUS, 0, 4); break; } else if (val == 0) { - uint32_t temp = nvnet_get_reg(s, NVNET_UNKNOWN_SETUP_REG3, 4); - if (temp == NVNET_UNKNOWN_SETUP_REG3_VAL1) { - /* forcedeth waits for this bit to be set... */ - nvnet_set_reg(s, NVNET_UNKNOWN_SETUP_REG5, - NVNET_UNKNOWN_SETUP_REG5_BIT31, 4); - break; - } + /* forcedeth waits for this bit to be set... */ + nvnet_set_reg(s, NVNET_UNKNOWN_SETUP_REG5, + NVNET_UNKNOWN_SETUP_REG5_BIT31, 4); } break; @@ -695,9 +739,6 @@ static void nvnet_realize(PCIDevice *pci_dev, Error **errp) memset(s->regs, 0, sizeof(s->regs)); - s->rx_ring_index = 0; - s->tx_ring_index = 0; - memory_region_init_io(&s->mmio, OBJECT(dev), &nvnet_mmio_ops, s, "nvnet-mmio", MMIO_SIZE); pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio); @@ -729,11 +770,15 @@ static void nvnet_reset(void *opaque) memset(&s->regs, 0, sizeof(s->regs)); memset(&s->phy_regs, 0, sizeof(s->phy_regs)); + memset(&s->tx_dma_buf, 0, sizeof(s->tx_dma_buf)); + memset(&s->rx_dma_buf, 0, sizeof(s->rx_dma_buf)); + s->tx_dma_buf_offset = 0; + + reset_descriptor_ring_pointers(s); + + /* Deprecated */ s->tx_ring_index = 0; s->rx_ring_index = 0; - memset(&s->tx_dma_buf, 0, sizeof(s->tx_dma_buf)); - s->tx_dma_buf_offset = 0; - memset(&s->rx_dma_buf, 0, sizeof(s->rx_dma_buf)); } static void nvnet_reset_hold(Object *obj, ResetType type) @@ -742,10 +787,34 @@ static void nvnet_reset_hold(Object *obj, ResetType type) nvnet_reset(s); } +static int nvnet_post_load(void *opaque, int version_id) +{ + NvNetState *s = NVNET(opaque); + + if (version_id < 2) { + /* Migrate old snapshot tx descriptor index */ + uint32_t next_desc_addr = + nvnet_get_reg(s, NVNET_TX_RING_PHYS_ADDR, 4) + + (s->tx_ring_index % get_tx_ring_size(s)) * sizeof(struct RingDesc); + nvnet_set_reg(s, NVNET_TX_RING_NEXT_DESC_PHYS_ADDR, next_desc_addr, 4); + s->tx_ring_index = 0; + + /* Migrate old snapshot rx descriptor index */ + next_desc_addr = + nvnet_get_reg(s, NVNET_RX_RING_PHYS_ADDR, 4) + + (s->rx_ring_index % get_rx_ring_size(s)) * sizeof(struct RingDesc); + nvnet_set_reg(s, NVNET_RX_RING_NEXT_DESC_PHYS_ADDR, next_desc_addr, 4); + s->rx_ring_index = 0; + } + + return 0; +} + static const VMStateDescription vmstate_nvnet = { .name = "nvnet", - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, + .post_load = nvnet_post_load, .fields = (VMStateField[]){ VMSTATE_PCI_DEVICE(parent_obj, NvNetState), VMSTATE_UINT8_ARRAY(regs, NvNetState, MMIO_SIZE), diff --git a/hw/xbox/mcpx/nvnet/nvnet_regs.h b/hw/xbox/mcpx/nvnet/nvnet_regs.h index d521c97cca..8f6d41eb6a 100644 --- a/hw/xbox/mcpx/nvnet/nvnet_regs.h +++ b/hw/xbox/mcpx/nvnet/nvnet_regs.h @@ -129,10 +129,14 @@ # define NVNET_LINKSPEED_10 10 # define NVNET_LINKSPEED_100 100 # define NVNET_LINKSPEED_1000 1000 +#define NVNET_TX_RING_CURRENT_DESC_PHYS_ADDR 0x11C +#define NVNET_RX_RING_CURRENT_DESC_PHYS_ADDR 0x120 +#define NVNET_TX_CURRENT_BUFFER_PHYS_ADDR 0x124 +#define NVNET_RX_CURRENT_BUFFER_PHYS_ADDR 0x12C #define NVNET_UNKNOWN_SETUP_REG5 0x130 # define NVNET_UNKNOWN_SETUP_REG5_BIT31 (1 << 31) -#define NVNET_UNKNOWN_SETUP_REG3 0x134 -# define NVNET_UNKNOWN_SETUP_REG3_VAL1 0x00200010 +#define NVNET_TX_RING_NEXT_DESC_PHYS_ADDR 0x134 +#define NVNET_RX_RING_NEXT_DESC_PHYS_ADDR 0x138 #define NVNET_UNKNOWN_SETUP_REG8 0x13C # define NVNET_UNKNOWN_SETUP_REG8_VAL1 0x00300010 #define NVNET_UNKNOWN_SETUP_REG7 0x140