From: Itamar Tal <ita...@guardicore.com> --- default-configs/i386-softmmu.mak | 1 + default-configs/x86_64-softmmu.mak | 1 + hw/1394/Makefile.objs | 1 + hw/1394/hcd-ohci.c | 1754 ++++++++++++++++++++++++++++++++++++ hw/1394/hcd-ohci.h | 147 +++ hw/Makefile.objs | 1 + 6 files changed, 1905 insertions(+) create mode 100644 hw/1394/Makefile.objs create mode 100644 hw/1394/hcd-ohci.c create mode 100644 hw/1394/hcd-ohci.h
diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index 6a74e00..8742af9 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -45,3 +45,4 @@ CONFIG_MEM_HOTPLUG=y CONFIG_XIO3130=y CONFIG_IOH3420=y CONFIG_I82801B11=y +CONFIG_1394_OHCI=y diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index 46b87dd..a47863d 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -45,3 +45,4 @@ CONFIG_MEM_HOTPLUG=y CONFIG_XIO3130=y CONFIG_IOH3420=y CONFIG_I82801B11=y +CONFIG_1394_OHCI=y diff --git a/hw/1394/Makefile.objs b/hw/1394/Makefile.objs new file mode 100644 index 0000000..bddc0e1 --- /dev/null +++ b/hw/1394/Makefile.objs @@ -0,0 +1 @@ +common-obj-$(CONFIG_1394_OHCI) += hcd-ohci.o diff --git a/hw/1394/hcd-ohci.c b/hw/1394/hcd-ohci.c new file mode 100644 index 0000000..3f7ad75 --- /dev/null +++ b/hw/1394/hcd-ohci.c @@ -0,0 +1,1754 @@ +/* + * FireWire (1394) support + * + * Copyright (c) 2015 Guardicore + * - address@hidden + * Originally Written by James Harper + * + * This is a `bare-bones' implementation of the Firewire 1394 OHCI + * for virtual->virtual firewire connections emulation + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "hcd-ohci.h" +#include <sys/time.h> + +#define OHCI_1394_MMIO_SIZE 0x800 + +#define HCCONTROL_RESET 16 +#define HCCONTROL_LINK_ENABLE 17 +#define HCCONTROL_LPS 19 + +#define HCCONTROL_RESET_MASK (1 << (HCCONTROL_RESET)) +#define HCCONTROL_LINK_ENABLE_MASK (1 << (HCCONTROL_LINK_ENABLE)) +#define HCCONTROL_LPS_MASK (1 << (HCCONTROL_LPS)) + +#define HCD_STATE_UNPLUGGED 0 /* no connection */ +#define HCD_STATE_MAGIC 1 /* waiting for magic */ +#define HCD_STATE_DISCONNECTED 2 /* waiting for link packet */ +#define HCD_STATE_ARBITRATION1 3 /* send bid */ +#define HCD_STATE_ARBITRATION2 4 /* receive bid and compare */ +#define HCD_STATE_CONNECTED 5 /* connected and ready to go */ + +#define IS_Ax_ACTIVE(n) (s->mmio.regs[((0x180 + ((n) * 0x20)) + \ + 0x000) >> 2] & (1 << 10)) +#define SET_Ax_ACTIVE(n) (s->mmio.regs[((0x180 + ((n) * 0x20)) + \ + 0x000) >> 2] |= (1 << 10)) +#define CLR_Ax_ACTIVE(n) (s->mmio.regs[((0x180 + ((n) * 0x20)) + \ + 0x000) >> 2] &= ~(1 << 10)) + +#define IS_Ax_DEAD(n) (s->mmio.regs[((0x180 + ((n) * 0x20)) + \ + 0x000) >> 2] & (1 << 11)) +#define SET_Ax_DEAD(n) (s->mmio.regs[((0x180 + ((n) * 0x20)) + \ + 0x000) >> 2] |= (1 << 11)) + +#define IS_Ax_WAKE(n) (s->mmio.regs[((0x180 + ((n) * 0x20)) + \ + 0x000) >> 2] & (1 << 12)) +#define CLR_Ax_WAKE(n) (s->mmio.regs[((0x180 + ((n) * 0x20)) + \ + 0x000) >> 2] &= ~(1 << 12)) + +#define IS_Ax_RUN(n) (s->mmio.regs[((0x180 + ((n) * 0x20)) + \ + 0x000) >> 2] & (1 << 15)) + +#define SET_Ax_EVENT_CODE(n, e) s->mmio.regs[((0x180 + ((n) * 0x20)) + \ + 0x000) >> 2] = ((s->mmio.regs[((0x180 + \ + ((n) * 0x20)) + 0x00) >> 2] & \ + 0xFFFFFFE0) | (e)) + +#define Ax_COMMAND_PTR(n) (s->mmio.regs[((0x180 + ((n) * 0x20)) + \ + 0x00C) >> 2]) +#define SET_Ax_COMMAND_PTR(n, c) (s->mmio.regs[((0x180 + ((n) * 0x20)) + \ + 0x00C) >> 2] = (c)) + +#define Ax_CONTEXT_CONTROL(n) (s->mmio.regs[((0x180 + ((n) * 0x20)) + \ + 0x00) >> 2]) + +#define DECLARE_PHY \ +union { \ + uint32_t PhyControl; \ + struct { \ + uint32_t PhyControl_wrData:8; \ + uint32_t PhyControl_regAddr:4; \ + uint32_t:2; \ + uint32_t PhyControl_wrReg:1; \ + uint32_t PhyControl_rdReg:1; \ + uint32_t PhyControl_rdData:8; \ + uint32_t PhyControl_rdAddr:4; \ + uint32_t:3; \ + uint32_t PhyControl_rdDone:1; \ + }; \ +} + +#define DECLARE_INT(prefix) \ +union { \ + uint32_t prefix; \ + struct { \ + uint32_t prefix##_reqTxComplete:1; \ + uint32_t prefix##_respTxComplete:1; \ + uint32_t prefix##_ARRQ:1; \ + uint32_t prefix##_ARRS:1; \ + uint32_t prefix##_RQPkt:1; \ + uint32_t prefix##_RSPkt:1; \ + uint32_t prefix##_isochTx:1; \ + uint32_t prefix##_isockRx:1; \ + uint32_t prefix##_postedWriteErr:1; \ + uint32_t prefix##_lockRespErr:1; \ + uint32_t:5; \ + uint32_t prefix##_selfIDComplete2:1; \ + uint32_t prefix##_selfIDComplete:1; \ + uint32_t prefix##_busReset:1; \ + uint32_t prefix##_regAccessFail:1; \ + uint32_t prefix##_phy:1; \ + uint32_t prefix##_cycleSynch:1; \ + uint32_t prefix##_cycle64Seconds:1; \ + uint32_t prefix##_cycleLost:1; \ + uint32_t prefix##_cycleInconsistent:1; \ + uint32_t prefix##_unrecoverableError:1; \ + uint32_t prefix##_cycleTooLong:1; \ + uint32_t prefix##_phyRegRcvd:1; \ + uint32_t prefix##_ack_tardy:1; \ + uint32_t:1; \ + uint32_t prefix##_softInterrupt:1; \ + uint32_t prefix##_vendorSpecific:1; \ + uint32_t prefix##_masterIntEnable:1; \ + }; \ +} + +#define DECLARE_CONFIG_ROM_HDR \ +union { \ + uint32_t ConfigROMhdr; \ + struct { \ + uint32_t ConfigRomhdr_rom_crc_value:16; \ + uint32_t ConfigRomhdr_crc_length:8; \ + uint32_t ConfigRomhdr_info_length:8; \ + }; \ +} + +#define DECLARE_NODE_ID \ +union { \ + uint32_t NodeID; \ + struct { \ + uint32_t NodeID_nodeNumber:6; \ + uint32_t NodeID_busNumber:10; \ + uint32_t:11; \ + uint32_t NodeID_CPS:1; \ + uint32_t:2; \ + uint32_t NodeID_root:1; \ + uint32_t NodeID_iDValid:1; \ + }; \ +} + +#define DECLARE_SELF_ID_COUNT \ +union { \ + uint32_t SelfIDCount; \ + struct { \ + uint32_t:2; \ + uint32_t SelfIDCount_Size:9; \ + uint32_t:5; \ + uint32_t SelfIDCount_Generation:8; \ + uint32_t:7; \ + uint32_t SelfIDCount_Error:1; \ + }; \ +} + +#define DECLARE_ASYNC(prefix) \ +struct { \ + union { \ + struct { \ + uint32_t prefix##ContextControl; \ + uint32_t prefix##ContextControl_Alt; \ + }; \ + struct { \ + uint32_t prefix##ContextControlSet; \ + uint32_t prefix##ContextControlClear; \ + }; \ + }; \ + uint32_t prefix##Reserved_08; \ + uint32_t prefix##CommandPtr; \ + uint32_t prefix##Reserved_10; \ + uint32_t prefix##Reserved_14; \ + uint32_t prefix##Reserved_18; \ + uint32_t prefix##Reserved_1c; \ +} + +typedef union { + struct { + uint32_t Version; + uint32_t GUID_ROM; + uint32_t ATRetries; + union { + uint32_t CSRReadData; /* 00c */ + uint32_t CSRWriteData; /* 00c */ + }; + uint32_t CSRCompareData; + uint32_t CSRControl; + DECLARE_CONFIG_ROM_HDR; + uint32_t BusID; + uint32_t BusOptions; + uint32_t GUIDHi; + uint32_t GUIDLo; + uint32_t Reserved_002c; + uint32_t Reserved_0030; + uint32_t ConfigROMMap; + uint32_t PostedWriteAddressLo; + uint32_t PostedWriteAddressHi;; + uint32_t VendorID; + uint32_t Reserved_0044; + uint32_t Reserved_0048; + uint32_t Reserved_004c; + union { + struct { + /* read */ + uint32_t HCControl; + uint32_t HCControl_Alt; + }; + struct { + /* write */ + uint32_t HCControlSet; + uint32_t HCControlClear; + }; + }; + uint32_t Reserved_0058; + uint32_t Reserved_005c; + uint32_t Reserved_0060; + uint32_t SelfIDBuffer; + DECLARE_SELF_ID_COUNT; + uint32_t Reserved_006c; + union { + struct { + /* read */ + uint32_t IRMultiChanMaskHi; + uint32_t IRMultiChanMaskHi_Alt; + }; + struct { + /* write */ + uint32_t IRMultiChanMaskHiSet; + uint32_t IRMultiChanMaskHiClear; + }; + }; + union { + struct { + /* read */ + uint32_t IRMultiChanMaskLo; + uint32_t IRMultiChanMaskLo_Alt; + }; + struct { + /* write */ + uint32_t IRMultiChanMaskLoSet; + uint32_t IRMultiChanMaskLoClear; + }; + }; + union { + struct { + /* read */ + DECLARE_INT(IntEvent); /* 0080 */ + DECLARE_INT(IntEventMasked); /* 0084 */ + }; + struct { + /* write */ + DECLARE_INT(IntEventSet); /* 0080 */ + DECLARE_INT(IntEventClear); /* 0084 */ + }; + }; + union { + struct { + /* read */ + DECLARE_INT(IntMask); + DECLARE_INT(IntMask_Alt); + }; + struct { + /* write */ + DECLARE_INT(IntMaskSet); + DECLARE_INT(IntMaskClear); + }; + }; + union { + struct { + /* read */ + uint32_t IsoXmitIntEvent; + uint32_t IsoXmitIntEventMasked; + }; + struct { + /* write */ + uint32_t IsoXmitIntEventSet; + uint32_t IsoXmitIntEventClear; + }; + }; + union { + struct { + /* read */ + uint32_t IsoXmitIntMask; + uint32_t IsoXmitIntMask_Alt; + }; + struct { + /* write */ + uint32_t IsoXmitIntMaskSet; + uint32_t IsoXmitIntMaskClear; + }; + }; + union { + struct { + /* read */ + uint32_t IsoRecvIntEvent; + uint32_t IsoRecvIntEventMasked; + }; + struct { + /* write */ + uint32_t IsoRecvIntEventSet; + uint32_t IsoRecvIntEventClear; + }; + }; + union { + struct { + /* read */ + uint32_t IsoRecvIntMask; + uint32_t IsoRecvIntMask_Alt; + }; + struct { + /* write */ + uint32_t IsoRecvIntMaskSet; + uint32_t IsoRecvIntMaskClear; + }; + }; + uint32_t InitialBandwidthAvailable; /* 00B0 */ + uint32_t InitialChannelsAvailableHi; /* 00B4 */ + uint32_t InitialChannelsAvailableLo; /* 00B8 */ + uint32_t Reserved_00bc; + uint32_t Reserved_00c0; + uint32_t Reserved_00c4; + uint32_t Reserved_00c8; + uint32_t Reserved_00dc; + uint32_t Reserved_00d0; + uint32_t Reserved_00d4; + uint32_t Reserved_00d8; + uint32_t FairnessControl; /* 00dc */ + union { + struct { + /* read */ + uint32_t LinkControl; /* 0xe0 */ + uint32_t LinkControl_Alt; /* 0xe4 */ + }; + struct { + /* write */ + uint32_t LinkControlSet; /* 0xe0 */ + uint32_t LinkControlClear; /* 0xe4 */ + }; + }; + DECLARE_NODE_ID; /* 00e8 */ + DECLARE_PHY; /* 00ec */ + uint32_t IsochronousCycleTimer; /* 00f0 */ + uint32_t Reserved_00f4; + uint32_t Reserved_00f8; + uint32_t Reserved_00fc; + union { + struct { + /* read */ + uint32_t AsynchronousRequestFilterHi; /* 0100 */ + uint32_t AsynchronousRequestFilterHi_Alt; /* 0104 */ + }; + struct { + /* write */ + uint32_t AsynchronousRequestFilterHiSet; /* 0100 */ + uint32_t AsynchronousRequestFilterHiClear; /* 0104 */ + }; + }; + union { + struct { + /* read */ + uint32_t AsynchronousRequestFilterLo; /* 0108 */ + uint32_t AsynchronousRequestFilterLo_Alt; /* 010c */ + }; + struct { + /* write */ + uint32_t AsynchronousRequestFilterLoSet; /* 0108 */ + uint32_t AsynchronousRequestFilterLoClear; /* 010c */ + }; + }; + union { + struct { + /* read */ + uint32_t PhysicalRequestFilterHi; /* 0110 */ + uint32_t PhysicalRequestFilterHi_Alt; /* 0114 */ + }; + struct { + /* write */ + uint32_t PhysicalRequestFilterHiSet; /* 0110 */ + uint32_t PhysicalRequestFilterHiClear; /* 0114 */ + }; + }; + union { + struct { + /* read */ + uint32_t PhysicalRequestFilterLo; /* 0118 */ + uint32_t PhysicalRequestFilterLo_Alt; /* 011c */ + }; + struct { + /* write */ + uint32_t PhysicalRequestFilterLoSet; /* 0118 */ + uint32_t PhysicalRequestFilterLoClear; /* 011c */ + }; + }; + uint32_t PhyiscalUpperBound; /* 0120 */ + uint32_t Reserved_0124; + uint32_t Reserved_0128; + uint32_t Reserved_012c; + uint32_t Reserved_0130; + uint32_t Reserved_0134; + uint32_t Reserved_0138; + uint32_t Reserved_013c; + uint32_t Reserved_0140; + uint32_t Reserved_0144; + uint32_t Reserved_0148; + uint32_t Reserved_014c; + uint32_t Reserved_0150; + uint32_t Reserved_0154; + uint32_t Reserved_0158; + uint32_t Reserved_015c; + uint32_t Reserved_0160; + uint32_t Reserved_0164; + uint32_t Reserved_0168; + uint32_t Reserved_016c; + uint32_t Reserved_0170; + uint32_t Reserved_0174; + uint32_t Reserved_0178; + uint32_t Reserved_017c; + DECLARE_ASYNC(AsyncRequestTransmit); + DECLARE_ASYNC(AsyncResponseTransmit); + DECLARE_ASYNC(AsyncRequestReceive); + DECLARE_ASYNC(AsyncResponseReceive); + /* Isoch stuff */ + }; + uint32_t regs[OHCI_1394_MMIO_SIZE >> 2]; +} mmio_regs_t; + +typedef union { + uint8_t bytes[1024]; + uint32_t quads[1024 / 4]; + struct { + DECLARE_CONFIG_ROM_HDR; + uint32_t BusID; + uint32_t BusOptions; + uint32_t GUIDHi; + uint32_t GUIDLo; + }; +} config_rom_t; + +typedef union { + uint8_t bytes[16]; + struct { + /* 00 */ + uint8_t CPS:1; + uint8_t Root:1; + uint8_t PhysicalID:6; + /* 01 */ + uint8_t GapCount:6; + uint8_t IBR:1; + uint8_t RHB:1; + /* 02 */ + uint8_t NumPorts:4; + uint8_t:1; + uint8_t Extended:3; + /* 03 */ + uint8_t Delay:4; + uint8_t:1; + uint8_t PHYSpeed:3; + /* 04 */ + uint8_t PwrClass:3; + uint8_t Jitter:3; + uint8_t C:1; + uint8_t L:1; + /* 05 */ + uint8_t EMC:1; + uint8_t EAA:1; + uint8_t PEI:1; + uint8_t STOI:1; + uint8_t CPSI:1; + uint8_t CTOI:1; + uint8_t ISBR:1; + uint8_t RPIE:1; + /* 06 */ + uint8_t reg6; + /* 07 */ + uint8_t PortSelect:4; + uint8_t:1; + uint8_t PageSelect:3; + }; +} phy_t; + +#define REG_OFFSET(field) ((uint32_t)(uintptr_t)&(((mmio_regs_t *)0)->field)) + +typedef union { + uint32_t val; + struct { + uint32_t m:1; /* = 0 */ + uint32_t initiated:1; /* = 1 for root node */ + uint32_t p2:2; /* = 0 */ + uint32_t p1:2; /* = 0 */ + uint32_t p0:2; /* = 3, maybe 2 when "child" compared to root */ + uint32_t pwr:3; /* = 0 */ + uint32_t c:1; /* = 1 when root */ + uint32_t del:2; /* = 0? */ + uint32_t sp:2; /* = 0? */ + uint32_t gap_cnt:6; /* = 0? */ + uint32_t L:1; /* = 1? maybe just when connected */ + uint32_t:1; /* = 0 */ + uint32_t node_id:6; + uint32_t type:2; /* = 2 */ + }; +} self_id_t; + +struct _hcd_state_t; + +typedef struct _hcd_state_t hcd_state_t; + +typedef struct { + uint16_t req_count; + uint16_t:2; + uint16_t branch:2; + uint16_t interrupt:2; + uint16_t:1; + uint16_t ping:1; + uint16_t key:3; + uint16_t:1; + uint16_t cmd:4; + uint32_t data_address; + uint32_t branch_address; + uint16_t timestamp; + uint16_t transfer_status; +} hcd_at_db_t; + +typedef struct { + hcd_state_t *s; + QEMUTimer *timer; + uint32_t num; /* base register is 0x180 + num * 0x20 */ + uint32_t address; /* current address */ + uint32_t response; +} hcd_timer_state_t; + +struct _hcd_state_t { + PCIDevice pci_dev; + MemoryRegion mmio_bar; + mmio_regs_t mmio; + hcd_timer_state_t at_req_timer; + hcd_timer_state_t at_rsp_timer; + phy_t phy; + uint8_t phy_pages[8][8]; + qemu_irq irq; + uint32_t irq_asserted; + /* properties from init */ + CharDriverState *chr; + int state; + int other_link; + uint16_t bid; + int root; + int bufpos; + uint8_t buf[16 + 65536]; /* maximum request size + maximum data size */ +}; + +static void hcd_bus_reset(hcd_state_t *s); +static void hcd_chr_event(void *opaque, int event); + +static const VMStateDescription vmstate_pci_hcd = { + .name = "ohci-1394", + .version_id = 3, + .minimum_version_id = 3, + .minimum_version_id_old = 0, + .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(pci_dev, hcd_state_t), + VMSTATE_UINT32_ARRAY(mmio.regs, hcd_state_t, OHCI_1394_MMIO_SIZE >> 2), + VMSTATE_UINT8_ARRAY(phy.bytes, hcd_state_t, 16), + VMSTATE_UINT8_2DARRAY(phy_pages, hcd_state_t, 8, 8), + VMSTATE_END_OF_LIST(), + } +}; + +static void +hcd_check_irq(hcd_state_t *s) { + if ((s->mmio.IntMask & 0x80000000) && + (s->mmio.IntEvent & s->mmio.IntMask)) { + if (!s->irq_asserted) { + qemu_set_irq(s->irq, 1); + s->irq_asserted = 1; + } + } else { + if (s->irq_asserted) { + qemu_set_irq(s->irq, 0); + s->irq_asserted = 0; + } + } +} + +static void +hcd_soft_reset(hcd_state_t *s) { + s->mmio.BusOptions = 0x00008002; /* 5.11 */ + s->mmio.HCControl &= 0x00C00000; /* 5.7.2 */ +} + +static void +hcd_hard_reset(hcd_state_t *s) { + memset(&s->mmio, 0, sizeof(s->mmio)); + s->mmio.Version = 0x00010010; /* Release 1.1 of OHCI spec */ + s->mmio.BusID = 0x31333934; /* 1394 */ + s->mmio.BusOptions = 0x00008002; /* 5.11 */ + s->mmio.GUIDHi = 0x89abcdef; + s->mmio.GUIDLo = 0x01234567; + memset(&s->phy, 0, sizeof(s->phy)); + s->phy.NumPorts = 1; + s->phy.L = 1; + s->phy.C = 1; + s->phy_pages[0][0] = 0x08; /* 0xFE; */ + hcd_soft_reset(s); +} + +static void +hcd_complete_self_id(hcd_state_t *s) { + s->mmio.NodeID_nodeNumber = (s->root) ? 0 : 1; /* 5.11 */ + s->mmio.NodeID_busNumber = 0x3ff; + s->mmio.NodeID_CPS = (s->state != HCD_STATE_CONNECTED) ? 0 : 1; + s->mmio.NodeID_root = s->root; + s->mmio.NodeID_iDValid = 1; + s->mmio.SelfIDCount_Size = 0; + s->mmio.SelfIDCount_Error = 0; + if (s->mmio.LinkControl & 0x00000200) { /* if RcvSelfID */ + uint32_t tmp = 0; + self_id_t sid; + + sid.val = 0; + sid.initiated = 1; + sid.p0 = 2; + sid.c = 1; + sid.L = 1; + sid.node_id = 0; + sid.type = 2; + dma_memory_write(&address_space_memory, + s->mmio.SelfIDBuffer + 4, + &sid.val, 4); + sid.val = ~sid.val; + dma_memory_write(&address_space_memory, + s->mmio.SelfIDBuffer + 8, + &sid.val, 4); + s->mmio.SelfIDCount_Size += 2; + + if (s->state == HCD_STATE_CONNECTED) { + sid.val = 0; + sid.initiated = 0; + sid.p0 = 3; + sid.c = 0; + sid.L = 1; + sid.node_id = 1; + sid.type = 2; + dma_memory_write(&address_space_memory, + s->mmio.SelfIDBuffer + 12, + &sid.val, 4); + sid.val = ~sid.val; + dma_memory_write(&address_space_memory, + s->mmio.SelfIDBuffer + 16, + &sid.val, 4); + s->mmio.SelfIDCount_Size += 2; + } + + tmp = (s->mmio.SelfIDCount_Generation << 16) | 1; + dma_memory_write(&address_space_memory, + s->mmio.SelfIDBuffer, + &tmp, 4); + s->mmio.SelfIDCount_Size++; + } + s->mmio.IntEvent |= 0x00018000; /* selfIDcomplete | selfIDcomplete2 */ + hcd_check_irq(s); +} + +typedef struct { + uint32_t req_count:16; + uint32_t:2; + uint32_t branch:2; + uint32_t interrupt:2; + uint32_t:2; + uint32_t key:3; + uint32_t status:1; + uint32_t cmd:4; + uint32_t data_address; + uint32_t branch_address; + uint32_t res_count:16; + uint32_t transfer_status:16; +} hcd_ar_db_t; + +static void +hcd_async_rx_rsp_packet(hcd_state_t *s, uint8_t *buf, uint32_t size, + uint8_t response) { + int num = 3; + hcd_ar_db_t db; + uint32_t data_address = 0; + uint32_t status = 0; + int state = 0; + + if (size == 0) { + return; + } + SET_Ax_EVENT_CODE(num, response); + dma_memory_read(&address_space_memory, + Ax_COMMAND_PTR(num) & 0xFFFFFFF0, + &db, sizeof(db)); + data_address = db.data_address + db.req_count - db.res_count; + while (state != 3) { + int write_size; + + db.transfer_status = + s->mmio.AsyncResponseReceiveContextControl & 0xFFFF; + if (db.res_count == 0) { + dma_memory_write(&address_space_memory, + Ax_COMMAND_PTR(num) & 0xFFFFFFF0, + &db, sizeof(db)); + if (db.branch_address == 0) { + CLR_Ax_ACTIVE(num); + /* TODO: need to roll back if this happens */ + } + SET_Ax_COMMAND_PTR(num, db.branch_address); + dma_memory_read(&address_space_memory, + Ax_COMMAND_PTR(num) & 0xFFFFFFF0, + &db, sizeof(db)); + data_address = db.data_address + db.req_count - db.res_count; + } + switch (state) { + case 0: + if (db.res_count > size) { + write_size = size; + } else { + write_size = db.res_count; + } + dma_memory_write(&address_space_memory, + data_address, + buf, write_size); + db.res_count -= write_size; + data_address += write_size; + size -= write_size; + buf += write_size; + if (size == 0) { + state = 1; + } + break; + case 1: + status = s->mmio.AsyncResponseReceiveContextControl << 16; + db.transfer_status = + s->mmio.AsyncResponseReceiveContextControl & 0xFFFF; + dma_memory_write(&address_space_memory, data_address, &status, 4); + db.res_count -= 4; + data_address += 4; + dma_memory_write(&address_space_memory, + Ax_COMMAND_PTR(num) & 0xFFFFFFF0, + &db, sizeof(db)); + state = 2; + break; + case 2: + /* this state exists to go around the loop again and update the db + if required */ + state = 3; + break; + } + } + s->mmio.IntEvent |= (1 << 5); + hcd_check_irq(s); +} + +static void +hcd_async_rx_run(hcd_state_t *s, uint32_t addr) { + int num; + + num = (addr & 0x0180) >> 7; + SET_Ax_ACTIVE(num); +} + +static void +hcd_async_rx_stop(hcd_state_t *s, uint32_t addr) { + int num; + + num = (addr & 0x0180) >> 7; + CLR_Ax_ACTIVE(num); +} + +static void +hcd_async_rx_wake(hcd_state_t *s, uint32_t addr) { + uint32_t address; + hcd_ar_db_t db; + int num; + + num = (addr & 0x0180) >> 7; + if (IS_Ax_ACTIVE(num)) { + return; + } + address = s->mmio.regs[(addr >> 2) + 0x00c]; + dma_memory_read(&address_space_memory, + address & 0xFFFFFFF0, + &db, sizeof(db)); + if ((db.branch_address & 0x0000000f) != 0) { + SET_Ax_ACTIVE(num); + SET_Ax_COMMAND_PTR(num, db.branch_address); + } +} + +static void +hcd_at_run(hcd_timer_state_t *t) { + hcd_state_t *s = t->s; + t->address = Ax_COMMAND_PTR(t->num) & 0xfffffff0; + t->response = EVT_TCODE_ERR; + SET_Ax_ACTIVE(t->num); +} + +static void +hcd_at_timer(void *o) { + hcd_timer_state_t *t = (hcd_timer_state_t *)o; + hcd_state_t *s = t->s; + ohci_packet_header_t packet_header; + hcd_at_db_t db; + + if (IS_Ax_DEAD(t->num) || !IS_Ax_RUN(t->num)) { + CLR_Ax_WAKE(t->num); + CLR_Ax_ACTIVE(t->num); + return; + } + if (!IS_Ax_ACTIVE(t->num)) { + if (!IS_Ax_WAKE(t->num)) { + return; + } + CLR_Ax_WAKE(t->num); + dma_memory_read(&address_space_memory, + t->address, &db, + sizeof(hcd_at_db_t)); + if (!(db.branch_address & 0x0000000f)) { + return; + } + SET_Ax_COMMAND_PTR(t->num, db.branch_address); + hcd_at_run(t); /* also sets active */ + } + CLR_Ax_WAKE(t->num); + dma_memory_read(&address_space_memory, + t->address, &db, + sizeof(hcd_at_db_t)); + if (db.cmd == 0 && db.key == 0) { + /* Do nothing */ + } else if (db.cmd == 0 && db.key == 2) { + /* OUTPUT_MORE_Immediate */ + } else if (db.cmd == 1 && db.key == 0) { + /* OUTPUT_LAST */ + } else if (db.cmd == 1 && db.key == 2) { + /* OUTPUT_LAST_Immediate */ + } else { + /* UNKNOWN COMMAND */ + return ; + } + + switch (db.key) { + case 0: { /* non-Immediate */ + uint8_t buf[65536]; + dma_memory_read(&address_space_memory, + db.data_address, buf, + db.req_count); + qemu_chr_fe_write(s->chr, buf, db.req_count); + break; + } + case 2: { /* Immediate */ + uint32_t data[4]; + dma_memory_read(&address_space_memory, + t->address + sizeof(hcd_at_db_t), + data, db.req_count); + + packet_header = *(ohci_packet_header_t *)data; + switch (packet_header.t_code) { + case 0x00: { /* quadlet write - quadlet format */ + ohci_req_quadlet_packet_t at_packet = + *(ohci_req_quadlet_packet_t *)data; + qemu_chr_fe_write(s->chr, (uint8_t *)data, sizeof(at_packet)); + t->response = ACK_PENDING; + break; + } + case 0x01: { /* block write - block write format */ + qemu_chr_fe_write(s->chr, (uint8_t *)data, db.req_count); + t->response = ACK_PENDING; + break; + } + case 0x04: { /* quadlet read - nodata format */ + qemu_chr_fe_write(s->chr, (uint8_t *)data, db.req_count); + t->response = ACK_PENDING; + break; + } + case 0x05: { /* read bytes from target */ + qemu_chr_fe_write(s->chr, (uint8_t *)data, db.req_count); + t->response = ACK_PENDING; + break; + } + case 0x0e: { /* PHY packet */ + /* probably just configuring the gap count... */ + t->response = ACK_COMPLETE; + /* reset because PHY packet */ + hcd_bus_reset(s); /* not all PHY packets require reset... */ + break; + } + default: + break; + } + break; + } + default: + break; + } + if (db.cmd == 0) { /* more */ + if (db.key == 2) { + t->address += sizeof(hcd_at_db_t) + sizeof(int32_t) * 4; + } else { + t->address += sizeof(hcd_at_db_t); + } + } else { /* last */ + if (db.interrupt == 3) { + s->mmio.IntEvent |= (1 << t->num); + } + SET_Ax_EVENT_CODE(t->num, t->response); + db.transfer_status = (uint16_t)Ax_CONTEXT_CONTROL(t->num); + dma_memory_write(&address_space_memory, t->address, &db, sizeof(db)); + if ((db.branch_address & 0x0000000f) == 0) { + CLR_Ax_ACTIVE(t->num); + return; + } + SET_Ax_COMMAND_PTR(t->num, db.branch_address); + hcd_at_run(t); + } + timer_mod(t->timer, 0); + /* maybe + t_now + get_ticks_per_sec() / 100000); + 100/sec isn't going to be right */ +} + +static void +hcd_bus_reset(hcd_state_t *s) { + uint32_t bus_reset_packet[3] = {0x000000e0, 0x00000000, 0x00000000}; + s->mmio.NodeID_busNumber = 0x3ff; + s->mmio.NodeID_CPS = 0; + s->mmio.NodeID_root = 0; + s->mmio.NodeID_iDValid = 0; + s->mmio.SelfIDCount_Generation++; + s->mmio.IntEvent |= 0x00020000; /* bus reset complete */ + if (s->state != HCD_STATE_CONNECTED) { + s->root = 1; + } + s->mmio.AsyncRequestTransmitContextControl &= 0xFFFFFBFF; + s->mmio.AsyncResponseTransmitContextControl &= 0xFFFFFBFF; + if (s->mmio.AsyncResponseReceiveContextControl & 0x00008000) { + bus_reset_packet[2] |= s->mmio.SelfIDCount_Generation << 16; + hcd_async_rx_rsp_packet(s, (uint8_t *)bus_reset_packet, + sizeof(bus_reset_packet), EVT_BUS_RESET); + } + hcd_complete_self_id(s); +} + +static uint8_t +hcd_phy_read(hcd_state_t *s, uint8_t reg) { + if (reg < 8) { + return s->phy.bytes[reg]; + } else { + return s->phy_pages[s->phy.PageSelect][reg & 7]; + } +} + +static void +hcd_phy_write(hcd_state_t *s, uint8_t reg, uint8_t data) { + if (reg < 8) { + switch (reg) { + case 0: /* not allowed? */ + break; + case 1: + s->phy.bytes[reg] = data & 0xBF; + if (data & 0x40) { + hcd_bus_reset(s); + } + break; + case 5: + s->phy.bytes[reg] = data & 0xBF; + if (data & 0x40) { + hcd_bus_reset(s); + } + break; + default: + s->phy.bytes[reg] = data; + break; + } + } else { + s->phy_pages[s->phy.PageSelect][reg & 7] = data; + } +} + +static uint64_t hcd_mmio_read(void *opaque, hwaddr addr, unsigned size) +{ + uint64_t ret; + hcd_state_t *s = (hcd_state_t *)opaque; + switch (addr) { + case REG_OFFSET(IntEventMasked): /* 0084 */ + ret = s->mmio.IntEvent & s->mmio.IntMask; + break; + default: + ret = s->mmio.regs[addr >> 2]; + break; + } + if (addr != 0x0080) { + /* what to do? */ + } + return ret; +} + +static void hcd_mmio_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size) +{ + hcd_state_t *s = (hcd_state_t *)opaque; + DECLARE_PHY phy_control; + DECLARE_NODE_ID NodeID; + + if ((addr & 0xFFE0) != 0x0100) { + /* what to do? */ + } + switch (addr) { + case REG_OFFSET(ATRetries): /* 0008 */ + s->mmio.regs[addr >> 2] = data; + break; + case REG_OFFSET(CSRWriteData): /* 00c */ + s->mmio.regs[addr >> 2] = data; + break; + case REG_OFFSET(CSRCompareData): /* 010 */ + s->mmio.regs[addr >> 2] = data; + break; + case REG_OFFSET(CSRControl): /* 014 */ + switch (data & 0x00000003) { + case 0: /* BUS_MANAGER_ID */ + /* TODO: set bus manager somewhere... */ + s->mmio.CSRReadData = s->mmio.CSRCompareData; + s->mmio.CSRControl = 0x80000000 | (data & 0x00000003); + break; + case 1: /* BANDWIDTH_AVAILABLE */ + /* not actioned */ + s->mmio.CSRControl = 0x80000000 | (data & 0x00000003); + break; + case 2: /* CHANNELS_AVAILABLE_HI */ + /* not actioned */ + s->mmio.CSRControl = 0x80000000 | (data & 0x00000003); + break; + case 3: /* CHANNELS_AVAILABLE_LO */ + /* not actioned */ + s->mmio.CSRControl = 0x80000000 | (data & 0x00000003); + break; + } + break; + case REG_OFFSET(ConfigROMhdr): /* 0018 */ + s->mmio.regs[addr >> 2] = data; + break; + case REG_OFFSET(BusID): /* 001c */ + s->mmio.regs[addr >> 2] = data; + break; + case REG_OFFSET(BusOptions): /* 0020 */ + s->mmio.regs[addr >> 2] = data; + break; + case REG_OFFSET(GUIDHi): /* 0024 */ + s->mmio.regs[addr >> 2] = data; + break; + case REG_OFFSET(GUIDLo): /* 0028 */ + s->mmio.regs[addr >> 2] = data; + break; + case REG_OFFSET(ConfigROMMap): /* 0034 */ + s->mmio.regs[addr >> 2] = data; + break; + case REG_OFFSET(HCControlSet): /* 0050 */ + data &= 0xE0CF0000; + s->mmio.HCControl |= data; + if (data & HCCONTROL_RESET_MASK) { + /* do a reset */ + hcd_soft_reset(s); + } + if (data & HCCONTROL_LINK_ENABLE_MASK) { + if ((s->state == HCD_STATE_DISCONNECTED) || + (s->state == HCD_STATE_ARBITRATION1)) { + uint32_t buf = 0xFFFFFFFF; + qemu_chr_fe_write(s->chr, (uint8_t *)&buf, 4); + } + } + break; + case REG_OFFSET(HCControlClear): /* 0054 */ + data &= 0xE0CE0000; + s->mmio.HCControl &= ~data; + break; + case REG_OFFSET(SelfIDBuffer): /* 0064 */ + s->mmio.regs[addr >> 2] = data; + break; + case REG_OFFSET(SelfIDCount): /* 0068 */ + s->mmio.regs[addr >> 2] = data; + break; + case REG_OFFSET(IRMultiChanMaskHiSet): /* 0070 */ + s->mmio.regs[(addr >> 2) & 0xFFFE] |= data; + s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] = + s->mmio.regs[((addr >> 2) & 0xFFFE)]; + break; + case REG_OFFSET(IRMultiChanMaskHiClear): /* 0074 */ + s->mmio.regs[(addr >> 2) & 0xFFFE] &= ~data; + s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] = + s->mmio.regs[((addr >> 2) & 0xFFFE)]; + break; + case REG_OFFSET(IRMultiChanMaskLoSet): /* 0078 */ + s->mmio.regs[(addr >> 2) & 0xFFFE] |= data; + s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] = + s->mmio.regs[((addr >> 2) & 0xFFFE)]; + break; + case REG_OFFSET(IRMultiChanMaskLoClear): /* 007c */ + s->mmio.regs[(addr >> 2) & 0xFFFE] &= ~data; + s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] = + s->mmio.regs[((addr >> 2) & 0xFFFE)]; + break; + case REG_OFFSET(IntEventSet): /* 0080 */ + s->mmio.regs[(addr >> 2) & 0xFFFE] |= data; + break; + case REG_OFFSET(IntEventClear): /* 0084 */ + s->mmio.regs[(addr >> 2) & 0xFFFE] &= ~data; + break; + case REG_OFFSET(IntMaskSet): /* 0088 */ + s->mmio.regs[(addr >> 2) & 0xFFFE] |= data; + break; + case REG_OFFSET(IntMaskClear): /* 008c */ + s->mmio.regs[(addr >> 2) & 0xFFFE] &= ~data; + break; + case REG_OFFSET(IsoXmitIntMaskSet): + s->mmio.regs[(addr >> 2) & 0xFFFE] |= data; + break; + case REG_OFFSET(IsoXmitIntMaskClear): + s->mmio.regs[(addr >> 2) & 0xFFFE] &= ~data; + break; + case REG_OFFSET(IsoRecvIntMaskSet): + s->mmio.regs[(addr >> 2) & 0xFFFE] |= data; + break; + case REG_OFFSET(IsoRecvIntMaskClear): + s->mmio.regs[(addr >> 2) & 0xFFFE] &= ~data; + break; + case REG_OFFSET(LinkControlSet): + s->mmio.regs[(addr >> 2) & 0xFFFE] |= data; + break; + case REG_OFFSET(LinkControlClear): + s->mmio.regs[(addr >> 2) & 0xFFFE] &= ~data; + break; + case REG_OFFSET(NodeID): /* 00E8 */ + NodeID.NodeID = data; + s->mmio.NodeID_busNumber = NodeID.NodeID_busNumber; + break; + case REG_OFFSET(PhyControl): /* 00ec */ + *(uint32_t *)&phy_control = data; + s->mmio.PhyControl_regAddr = phy_control.PhyControl_regAddr; + if (phy_control.PhyControl_rdReg) { + s->mmio.PhyControl_rdAddr = phy_control.PhyControl_regAddr; + s->mmio.PhyControl_rdData = + hcd_phy_read(s, phy_control.PhyControl_regAddr); + s->mmio.PhyControl_rdDone = 1; + s->mmio.IntEvent_phyRegRcvd = 1; + } + if (phy_control.PhyControl_wrReg) { + hcd_phy_write(s, phy_control.PhyControl_regAddr, + phy_control.PhyControl_wrData); + s->mmio.PhyControl_wrData = phy_control.PhyControl_wrData; + s->mmio.PhyControl_rdDone = 0; + } + break; + case REG_OFFSET(AsynchronousRequestFilterHiSet): /* 0x100 */ + s->mmio.regs[(addr >> 2) & 0xFFFE] |= data; + s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] = + s->mmio.regs[((addr >> 2) & 0xFFFE)]; + break; + case REG_OFFSET(AsynchronousRequestFilterHiClear): /* 0x104 */ + s->mmio.regs[(addr >> 2) & 0xFFFE] &= ~data; + s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] = + s->mmio.regs[((addr >> 2) & 0xFFFE)]; + break; + case REG_OFFSET(AsynchronousRequestFilterLoSet): /* 0x108 */ + s->mmio.regs[(addr >> 2) & 0xFFFE] |= data; + s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] = + s->mmio.regs[((addr >> 2) & 0xFFFE)]; + break; + case REG_OFFSET(AsynchronousRequestFilterLoClear): /* 0x10c */ + s->mmio.regs[(addr >> 2) & 0xFFFE] &= ~data; + s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] = + s->mmio.regs[((addr >> 2) & 0xFFFE)]; + break; + case REG_OFFSET(PhysicalRequestFilterHiSet): /* 0x110 */ + s->mmio.regs[(addr >> 2) & 0xFFFE] |= data; + s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] = + s->mmio.regs[((addr >> 2) & 0xFFFE)]; + break; + case REG_OFFSET(PhysicalRequestFilterHiClear): /* 0x114 */ + s->mmio.regs[(addr >> 2) & 0xFFFE] &= ~data; + s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] = + s->mmio.regs[((addr >> 2) & 0xFFFE)]; + break; + case REG_OFFSET(PhysicalRequestFilterLoSet): /* 0x118 */ + s->mmio.regs[(addr >> 2) & 0xFFFE] |= data; + s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] = + s->mmio.regs[((addr >> 2) & 0xFFFE)]; + break; + case REG_OFFSET(PhysicalRequestFilterLoClear): /* 0x11c */ + s->mmio.regs[(addr >> 2) & 0xFFFE] &= ~data; + s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] = + s->mmio.regs[((addr >> 2) & 0xFFFE)]; + break; + case REG_OFFSET(AsyncRequestTransmitContextControlSet): + case REG_OFFSET(AsyncResponseTransmitContextControlSet): + data &= 0x00009000; + s->mmio.regs[(addr >> 2) & 0xFFFE] |= data; + s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] = + s->mmio.regs[((addr >> 2) & 0xFFFE)]; + if (data & 0x00009000) { + hcd_timer_state_t *t = NULL; + if (addr == REG_OFFSET(AsyncRequestTransmitContextControlSet)) { + t = &s->at_req_timer; + } else { + t = &s->at_rsp_timer; + } + if (data & 0x00008000) { + hcd_at_run(t); + } + timer_mod(t->timer, 0); + } + break; + case REG_OFFSET(AsyncRequestReceiveContextControlSet): + case REG_OFFSET(AsyncResponseReceiveContextControlSet): + data &= 0x00009000; + s->mmio.regs[(addr >> 2) & 0xFFFE] |= data; + s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] = + s->mmio.regs[((addr >> 2) & 0xFFFE)]; + if (data & 0x00008000) { + hcd_async_rx_run(s, addr & 0xFFE0); + } + if (data & 0x00001000) { + hcd_async_rx_wake(s, addr & 0xFFE0); + } + break; + case REG_OFFSET(AsyncRequestTransmitContextControlClear): + case REG_OFFSET(AsyncResponseTransmitContextControlClear): + s->mmio.regs[(addr >> 2) & 0xFFFE] &= ~data; + s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] = + s->mmio.regs[((addr >> 2) & 0xFFFE)]; + break; + case REG_OFFSET(AsyncRequestReceiveContextControlClear): + case REG_OFFSET(AsyncResponseReceiveContextControlClear): + data &= 0x00008000; + s->mmio.regs[(addr >> 2) & 0xFFFE] &= ~data; + s->mmio.regs[((addr >> 2) & 0xFFFE) + 1] = + s->mmio.regs[((addr >> 2) & 0xFFFE)]; + if (data & 0x00008000) { + hcd_async_rx_stop(s, addr & 0xFFE0); + } + break; + default: + s->mmio.regs[addr >> 2] = data; + break; + } + hcd_check_irq(s); +} + +static const MemoryRegionOps hcd_mmio_ops = { + .read = hcd_mmio_read, + .write = hcd_mmio_write, + .endianness = DEVICE_LITTLE_ENDIAN, /* TODO: might change with arch */ +}; + +static int hcd_chr_can_receive(void *opaque) +{ + if (runstate_check(RUN_STATE_INMIGRATE)) { + /* this seems to race with the restore + RUN_STATE_INMIGRATE */ + return 0; + } else { + return 8192; + } +} + +static void hcd_fill_buffer(hcd_state_t *s, const uint8_t **buf, int *len, + int required) +{ + int to_copy; + + if (s->bufpos >= required) { + return; + } + if (required - s->bufpos > *len) { + to_copy = *len; + } else { + to_copy = required - s->bufpos; + } + + memcpy(s->buf + s->bufpos, *buf, to_copy); + *buf += to_copy; + *len -= to_copy; + s->bufpos += to_copy; +} + +static void hcd_chr_receive(void *opaque, const uint8_t *buf, int len) +{ + hcd_state_t *s = (hcd_state_t *)opaque; + uint16_t received_bid; + struct timeval tv; + ohci_packet_header_t *packet_header; + + while (len) { + switch (s->state) { + case HCD_STATE_UNPLUGGED: + /* restore races with chr event, just fake it here */ + hcd_chr_event(s, CHR_EVENT_OPENED); + break; + case HCD_STATE_MAGIC: /* waiting for magic */ + hcd_fill_buffer(s, &buf, &len, 4); + if (s->bufpos < 4) { + /* not enough data yet HCD_MAGIC */ + break; + } + if (s->bufpos > 4) { + /* overflow HCD_MAGIC */ + break; + } + if (memcmp(s->buf, "1394", 4) != 0) { + /* TODO: what do we do here? drop the connection I suppose */ + break; + } else { + s->state = HCD_STATE_DISCONNECTED; + } + s->bufpos = 0; + break; + case HCD_STATE_DISCONNECTED: + hcd_fill_buffer(s, &buf, &len, 4); + if (s->bufpos < 4) { + /* not enough data yet HCD_STATE_DISCONNECTED */ + return; + } + s->bufpos = 0; + if (*(uint32_t *)s->buf != 0xFFFFFFFF) { + /* unknown data */ + break; + } + s->other_link = 1; + /* link change - connected */ + s->state = HCD_STATE_ARBITRATION1; + if (!(s->mmio.HCControl & HCCONTROL_LINK_ENABLE_MASK)) { + /* we will progress when our link comes up and the other end + sends a bid */ + break; + } + /* fall through as we won't go around again because len == 0 */ + case HCD_STATE_ARBITRATION1: + gettimeofday(&tv, NULL); + s->bid = 0; + s->bid ^= (tv.tv_sec >> 0) & 0xFFFF; + s->bid ^= (tv.tv_sec >> 16) & 0xFFFF; + s->bid ^= (tv.tv_sec >> 32) & 0xFFFF; + s->bid ^= (tv.tv_sec >> 48) & 0xFFFF; + s->bid ^= (tv.tv_usec >> 0) & 0xFFFF; + s->bid ^= (tv.tv_usec >> 16) & 0xFFFF; + s->bid ^= (tv.tv_usec >> 32) & 0xFFFF; + s->bid ^= (tv.tv_usec >> 48) & 0xFFFF; + s->bid &= 0x7FFF; + /* TODO: set high bit based on preference to become root */ + qemu_chr_fe_write(s->chr, (uint8_t *)&s->bid, 2); + s->state = HCD_STATE_ARBITRATION2; + break; + case HCD_STATE_ARBITRATION2: + hcd_fill_buffer(s, &buf, &len, 2); + if (s->bufpos < 2) { + /* not enough data yet HCD_STATE_ARBITRATION2 */ + break; + } + received_bid = *(uint16_t *)s->buf; + s->bufpos = 0; + if (received_bid == s->bid) { + s->state = HCD_STATE_ARBITRATION1; + break; + } else if (received_bid < s->bid) { + s->root = 1; + s->state = HCD_STATE_CONNECTED; + } else { + s->root = 0; + s->state = HCD_STATE_CONNECTED; + } + hcd_bus_reset(s); + break; + case HCD_STATE_CONNECTED: + if (!(s->mmio.HCControl & HCCONTROL_LINK_ENABLE_MASK)) { + return; + } + hcd_fill_buffer(s, &buf, &len, 4); + if (s->bufpos < 4) { + /* not enough data yet HCD_STATE_CONNECTED */ + return; + } + if (*(uint32_t *)s->buf == 0xFFFFFFFE) { + /* Reset because link change */ + s->bufpos = 0; + s->state = HCD_STATE_DISCONNECTED; + hcd_bus_reset(s); + break; + } + packet_header = (ohci_packet_header_t *)s->buf; + + switch (packet_header->t_code) { + case 0x00: { /* request - quadlet write */ + ohci_req_quadlet_packet_t *req_packet; + ohci_rsp_nodata_packet_t rsp_packet; + hcd_fill_buffer(s, &buf, &len, sizeof(*req_packet)); + if (s->bufpos < sizeof(*req_packet)) { + /* not enough data yet HCD_STATE_CONNECTED 00 */ + return; + } + if (s->bufpos > sizeof(*req_packet)) { + /* overflow HCD_STATE_CONNECTED 00 */ + return; + } + req_packet = (ohci_req_quadlet_packet_t *)s->buf; + dma_memory_write(&address_space_memory, + req_packet->destination_offset_low, + &req_packet->data, sizeof(uint32_t)); + /* forward to ar or handle here?? */ + rsp_packet.t_code = 0x02; + rsp_packet.rt = req_packet->rt; + rsp_packet.t_label = req_packet->t_label; + rsp_packet.r_code = RESP_COMPLETE; + rsp_packet.destination_id = req_packet->destination_id ^ 1; + rsp_packet.source_id = req_packet->destination_id; + qemu_chr_fe_write(s->chr, (uint8_t *)&rsp_packet, + sizeof(rsp_packet)); + s->bufpos = 0; + break; + } + case 0x01: { /* request - block write */ + ohci_req_block_packet_t *req_packet; + ohci_rsp_nodata_packet_t rsp_packet; + hcd_fill_buffer(s, &buf, &len, sizeof(*req_packet)); + if (s->bufpos < sizeof(*req_packet)) { + /* not enough data yet HCD_STATE_CONNECTED 01 */ + return; + } + req_packet = (ohci_req_block_packet_t *)s->buf; + hcd_fill_buffer(s, &buf, &len, + sizeof(*req_packet) + req_packet->data_length); + if (s->bufpos < (sizeof(*req_packet) + + req_packet->data_length)) { + /* not enough data yet HCD_STATE_CONNECTED 01 */ + return; + } + if (s->bufpos > (sizeof(*req_packet) + + req_packet->data_length)) { + /* overflow HCD_STATE_CONNECTED 01 */ + return; + } + dma_memory_write(&address_space_memory, + req_packet->destination_offset_low, + s->buf + sizeof(*req_packet), + req_packet->data_length); + /* forward to ar or handle here?? */ + rsp_packet.t_code = 0x02; + rsp_packet.rt = req_packet->rt; + rsp_packet.t_label = req_packet->t_label; + rsp_packet.r_code = RESP_COMPLETE; + rsp_packet.destination_id = req_packet->destination_id ^ 1; + rsp_packet.source_id = req_packet->destination_id; + qemu_chr_fe_write(s->chr, (uint8_t *)&rsp_packet, + sizeof(rsp_packet)); + s->bufpos = 0; + break; + } + case 0x02: { /* response - quadlet write */ + ohci_rsp_nodata_packet_t *rsp_packet; + hcd_fill_buffer(s, &buf, &len, sizeof(*rsp_packet)); + if (s->bufpos < sizeof(*rsp_packet)) { + /* not enough data yet HCD_STATE_CONNECTED 02 */ + return; + } + if (s->bufpos > sizeof(*rsp_packet)) { + /* overflow HCD_STATE_CONNECTED 02 */ + return; + } + rsp_packet = (ohci_rsp_nodata_packet_t *)s->buf; + hcd_async_rx_rsp_packet(s, (uint8_t *)rsp_packet, + sizeof(*rsp_packet), ACK_COMPLETE); + s->bufpos = 0; + break; + } + case 0x04: { /* request - quadlet read */ + ohci_req_nodata_packet_t *req_nodata_packet; + ohci_rsp_quadlet_packet_t rsp_quadlet_packet; + hcd_fill_buffer(s, &buf, &len, 12); + if (s->bufpos < 12) { + /* not enough data yet HCD_STATE_CONNECTED 04 */ + return; + } + if (s->bufpos > 12) { + /* overflow HCD_STATE_CONNECTED 04 */ + return; + } + req_nodata_packet = (ohci_req_nodata_packet_t *)s->buf; + /* forward to ar or handle here?? */ + rsp_quadlet_packet.t_code = 0x06; + rsp_quadlet_packet.rt = req_nodata_packet->rt; + rsp_quadlet_packet.t_label = req_nodata_packet->t_label; + rsp_quadlet_packet.destination_id = + req_nodata_packet->destination_id ^ 1; + rsp_quadlet_packet.source_id = + req_nodata_packet->destination_id; + if (req_nodata_packet->destination_offset_high == 0xFFFF) { + if (0xF0000400 == + (req_nodata_packet->destination_offset_low & + 0xFFFFFC00)) { + uint32_t tmp_addr = s->mmio.ConfigROMMap + + (req_nodata_packet->destination_offset_low & 0x3ff); + + dma_memory_read(&address_space_memory, + tmp_addr, + &rsp_quadlet_packet.data, + sizeof(uint32_t)); + rsp_quadlet_packet.r_code = RESP_COMPLETE; + } else { + /* Unknown address */ + rsp_quadlet_packet.r_code = RESP_ADDRESS_ERROR; + } + } else if ((req_nodata_packet->destination_offset_high) == + 0x0000) { + if (dma_memory_read( + &address_space_memory, + req_nodata_packet->destination_offset_low, + &rsp_quadlet_packet.data, sizeof(uint32_t))) { + rsp_quadlet_packet.r_code = RESP_ADDRESS_ERROR; + } else { + rsp_quadlet_packet.r_code = RESP_COMPLETE; + } + } else { + /* Unknown address */ + rsp_quadlet_packet.r_code = RESP_ADDRESS_ERROR; + } + + qemu_chr_fe_write(s->chr, (uint8_t *)&rsp_quadlet_packet, + sizeof(rsp_quadlet_packet)); + s->bufpos = 0; + break; + } + case 0x05: { /* request - block read */ + ohci_req_block_packet_t *req_packet; + ohci_rsp_block_packet_t rsp_packet; + uint8_t bounce_buffer[65536]; + hcd_fill_buffer(s, &buf, &len, sizeof(*req_packet)); + if (s->bufpos < sizeof(*req_packet)) { + /* not enough data yet HCD_STATE_CONNECTED 05 */ + return; + } + if (s->bufpos > sizeof(*req_packet)) { + /* overflow HCD_STATE_CONNECTED 05 */ + return; + } + req_packet = (ohci_req_block_packet_t *)s->buf; + /* forward to ar or handle here?? */ + rsp_packet.t_code = 0x07; + rsp_packet.rt = req_packet->rt; + rsp_packet.t_label = req_packet->t_label; + rsp_packet.destination_id = req_packet->destination_id ^ 1; + rsp_packet.source_id = req_packet->destination_id; + rsp_packet.data_length = req_packet->data_length; + if (req_packet->destination_offset_high == 0xFFFF) { + if ((req_packet->destination_offset_low & 0xFFFFFC00) == + 0xF0000400) { + uint32_t tmp_addr = s->mmio.ConfigROMMap + + (req_packet->destination_offset_low & 0x3ff); + dma_memory_read(&address_space_memory, + tmp_addr, + bounce_buffer, rsp_packet.data_length); + rsp_packet.r_code = RESP_COMPLETE; + } else { + /* Unknown address */ + rsp_packet.r_code = RESP_ADDRESS_ERROR; + } + } else if ((req_packet->destination_offset_high) == 0x0000) { + if (dma_memory_read(&address_space_memory, + req_packet->destination_offset_low, + bounce_buffer, + rsp_packet.data_length)) { + /* address error */ + rsp_packet.r_code = RESP_ADDRESS_ERROR; + } else { + rsp_packet.r_code = RESP_COMPLETE; + } + } else { + /* Unknown address */ + rsp_packet.r_code = RESP_ADDRESS_ERROR; + } + qemu_chr_fe_write(s->chr, (uint8_t *)&rsp_packet, + sizeof(rsp_packet)); + if (rsp_packet.r_code == RESP_COMPLETE) { + qemu_chr_fe_write(s->chr, bounce_buffer, + rsp_packet.data_length); + } + s->bufpos = 0; + break; + } + case 0x06: { /* response - quadlet read */ + ohci_rsp_quadlet_packet_t *rsp_packet; + hcd_fill_buffer(s, &buf, &len, sizeof(*rsp_packet)); + if (s->bufpos < sizeof(*rsp_packet)) { + /* not enough data yet HCD_STATE_CONNECTED 4 */ + return; + } + if (s->bufpos > sizeof(*rsp_packet)) { + /* overflow HCD_STATE_CONNECTED 4 */ + return; + } + rsp_packet = (ohci_rsp_quadlet_packet_t *)s->buf; + + hcd_async_rx_rsp_packet(s, (uint8_t *)rsp_packet, + sizeof(*rsp_packet), ACK_COMPLETE); + s->bufpos = 0; + break; + } + case 0x07: { /* response - block read */ + ohci_rsp_block_packet_t *rsp_packet; + hcd_fill_buffer(s, &buf, &len, sizeof(*rsp_packet)); + if (s->bufpos < sizeof(*rsp_packet)) { + /* not enough data yet HCD_STATE_CONNECTED 07 */ + return; + } + rsp_packet = (ohci_rsp_block_packet_t *)s->buf; + hcd_fill_buffer(s, &buf, &len, + sizeof(*rsp_packet) + rsp_packet->data_length); + if (s->bufpos < + (sizeof(*rsp_packet) + rsp_packet->data_length)) { + /* not enough data yet HCD_STATE_CONNECTED 07 */ + return; + } + if (s->bufpos > + (sizeof(*rsp_packet) + rsp_packet->data_length)) { + /* overflow HCD_STATE_CONNECTED 07 */ + return; + } + hcd_async_rx_rsp_packet(s, (uint8_t *)rsp_packet, + sizeof(*rsp_packet) + + rsp_packet->data_length, + ACK_COMPLETE); + s->bufpos = 0; + break; + } + default: + /* unknown t_code */ + break; + } + return; + } + } +} + +static void hcd_chr_event(void *opaque, int event) +{ + hcd_state_t *s = (hcd_state_t *)opaque; + + if (runstate_check(RUN_STATE_INMIGRATE)) { + /* RUN_STATE_INMIGRATE */ + return; + } + + switch (event) { + case CHR_EVENT_OPENED: + s->state = HCD_STATE_MAGIC; + qemu_chr_fe_write(s->chr, (uint8_t *)"1394", 4); + if (s->mmio.HCControl & HCCONTROL_LINK_ENABLE_MASK) { + uint32_t buf = 0xFFFFFFFF; + qemu_chr_fe_write(s->chr, (uint8_t *)&buf, 4); + if (s->other_link) { + hcd_bus_reset(s); + } + } + break; + case CHR_EVENT_CLOSED: + s->state = HCD_STATE_UNPLUGGED; + s->phy_pages[0][0] = 0x08; /* 0xFE ? */ + /* TODO: interrupt? */ + s->phy.PEI = 1; + s->mmio.IntEvent |= (1 << 19); + hcd_bus_reset(s); + break; + default: + break; + } +} + +static int +hcd_pci_init(PCIDevice *pci_dev) { + hcd_state_t *s = DO_UPCAST(hcd_state_t, pci_dev, pci_dev); + uint8_t *pci_conf = pci_dev->config; + + pci_set_byte(pci_conf + PCI_CLASS_PROG, 0x10); + pci_set_word(pci_conf + PCI_STATUS, + PCI_STATUS_DEVSEL_MEDIUM | PCI_STATUS_FAST_BACK); + pci_set_byte(pci_conf + PCI_INTERRUPT_PIN, 1); + pci_set_byte(pci_conf + PCI_MIN_GNT, 0x08); + + memory_region_init_io(&s->mmio_bar, OBJECT(s), &hcd_mmio_ops, s, + "ohci-1394-mmio", OHCI_1394_MMIO_SIZE); + pci_register_bar(&s->pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, + &s->mmio_bar); + s->irq = pci_allocate_irq(&s->pci_dev); + s->at_req_timer.s = s; + s->at_req_timer.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hcd_at_timer, + &s->at_req_timer); + s->at_req_timer.num = 0; + s->at_rsp_timer.s = s; + s->at_rsp_timer.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hcd_at_timer, + &s->at_rsp_timer); + s->at_rsp_timer.num = 1; + qemu_chr_add_handlers(s->chr, hcd_chr_can_receive, hcd_chr_receive, + hcd_chr_event, s); + hcd_hard_reset(s); + return 0; +} + +static void +hcd_pci_exit(PCIDevice *pci_dev) +{ + /* + hcd_state_t *s = DO_UPCAST(hcd_state_t, pci_dev, pci_dev); + memory_region_destroy(&s->mmio_bar); */ +} + +static Property hcd_properties[] = { + DEFINE_PROP_CHR("chardev", hcd_state_t, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void +hcd_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->init = hcd_pci_init; + k->exit = hcd_pci_exit; + k->vendor_id = PCI_VENDOR_ID_QEMU; + k->device_id = 0x1394; + k->class_id = 0x0C00; + dc->vmsd = &vmstate_pci_hcd; + dc->props = hcd_properties; +} + +static const TypeInfo hcd_info = { + .name = "ohci-1394", + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(hcd_state_t), + .class_init = hcd_class_init, +}; + diff --git a/hw/1394/hcd-ohci.h b/hw/1394/hcd-ohci.h new file mode 100644 index 0000000..b150175 --- /dev/null +++ b/hw/1394/hcd-ohci.h @@ -0,0 +1,147 @@ +#ifndef HCD_OHCI_H +#define HCD_OHCI_H + +#include "hw/hw.h" +#include "hw/pci/pci.h" +#include "hcd-ohci.h" +#include "hw/loader.h" +#include "sysemu/sysemu.h" +#include "sysemu/char.h" +#include "qemu/timer.h" + +#define EVT_NO_STATUS 0x00 +#define EVT_UNDERRUN 0x04 +#define EVT_OVERRUN 0x05 +#define EVT_DATA_READ 0x07 +#define EVT_DATA_WRITE 0x08 +#define EVT_BUS_RESET 0x09 +#define EVT_TCODE_ERR 0x0B +#define EVT_UNKNOWN 0x0E +#define EVT_FLUSHED 0x0F +#define ACK_COMPLETE 0x11 +#define ACK_PENDING 0x12 + +#define RESP_COMPLETE 0x00 +#define RESP_CONFLICT_ERROR 0x04 +#define RESP_DATA_ERROR 0x05 +#define RESP_TYPE_ERROR 0x06 +#define RESP_ADDRESS_ERROR 0x07 + +typedef union { + uint32_t qdata[3]; + struct { + uint32_t:4; + uint32_t t_code:4; + uint32_t rt:2; + uint32_t t_label:6; + uint32_t spd:3; + uint32_t:4; + uint32_t src_bus_id:1; + uint32_t:8; + }; +} ohci_packet_header_t; + +typedef union { + uint32_t qdata[3]; + struct { + uint32_t:4; + uint32_t t_code:4; + uint32_t rt:2; + uint32_t t_label:6; + uint32_t spd:3; + uint32_t:4; + uint32_t src_bus_id:1; + uint32_t:8; + uint32_t destination_offset_high:16; + uint32_t destination_id:16; + uint32_t destination_offset_low:32; + }; +} ohci_req_nodata_packet_t; + +typedef union { + uint32_t qdata[3]; + struct { + uint32_t:4; + uint32_t t_code:4; + uint32_t rt:2; + uint32_t t_label:6; + uint32_t spd:3; + uint32_t:4; + uint32_t src_bus_id:1; + uint32_t:8; + uint32_t destination_offset_high:16; + uint32_t destination_id:16; + uint32_t destination_offset_low:32; + uint32_t data; + }; +} ohci_req_quadlet_packet_t; + +typedef union { + uint32_t qdata[4]; + struct { + uint32_t:4; + uint32_t t_code:4; + uint32_t rt:2; + uint32_t t_label:6; + uint32_t spd:3; + uint32_t:4; + uint32_t src_bus_id:1; + uint32_t:8; + uint32_t destination_offset_high:16; + uint32_t destination_id:16; + uint32_t destination_offset_low:32; + uint32_t:16; + uint32_t data_length:16; + }; +} ohci_req_block_packet_t; + +typedef union { + uint32_t qdata[3]; + struct { + uint32_t:4; + uint32_t t_code:4; + uint32_t rt:2; + uint32_t t_label:6; + uint32_t destination_id:16; + uint32_t:12; + uint32_t r_code:4; + uint32_t source_id:16; + uint32_t:32; + }; +} ohci_rsp_nodata_packet_t; + +typedef union { + uint32_t qdata[4]; + struct { + uint32_t:4; + uint32_t t_code:4; + uint32_t rt:2; + uint32_t t_label:6; + uint32_t destination_id:16; + uint32_t:12; + uint32_t r_code:4; + uint32_t source_id:16; + uint32_t:32; + uint32_t data; + }; +} ohci_rsp_quadlet_packet_t; + +typedef union { + uint32_t qdata[4]; + struct { + uint32_t:4; + uint32_t t_code:4; + uint32_t rt:2; + uint32_t t_label:6; + uint32_t destination_id:16; + uint32_t:12; + uint32_t r_code:4; + uint32_t source_id:16; + uint32_t:32; + uint32_t:16; + uint32_t data_length:16; + }; +} ohci_rsp_block_packet_t; + +#endif + diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 73afa41..c3baffa 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -1,4 +1,5 @@ devices-dirs-$(call land, $(CONFIG_VIRTIO),$(call land,$(CONFIG_VIRTFS),$(CONFIG_PCI))) += 9pfs/ +devices-dirs-$(CONFIG_SOFTMMU) += 1394/ devices-dirs-$(CONFIG_ACPI) += acpi/ devices-dirs-$(CONFIG_SOFTMMU) += audio/ devices-dirs-$(CONFIG_SOFTMMU) += block/ -- 2.3.4