From 420557e8981347368427849ff89ecd3fe537dafa Mon Sep 17 00:00:00 2001 From: bellard Date: Thu, 30 Sep 2004 22:13:50 +0000 Subject: [PATCH] full system SPARC emulation (Blue Swirl) git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1085 c046a42c-6fe2-441c-8c8c-71466251a162 --- hw/iommu.c | 221 +++++++++++++++++++++++ hw/lance.c | 472 ++++++++++++++++++++++++++++++++++++++++++++++++ hw/m48t08.c | 391 +++++++++++++++++++++++++++++++++++++++ hw/m48t08.h | 12 ++ hw/magic-load.c | 341 ++++++++++++++++++++++++++++++++++ hw/sched.c | 346 +++++++++++++++++++++++++++++++++++ hw/sun4m.c | 130 +++++++++++++ hw/tcx.c | 176 ++++++++++++++++++ 8 files changed, 2089 insertions(+) create mode 100644 hw/iommu.c create mode 100644 hw/lance.c create mode 100644 hw/m48t08.c create mode 100644 hw/m48t08.h create mode 100644 hw/magic-load.c create mode 100644 hw/sched.c create mode 100644 hw/sun4m.c create mode 100644 hw/tcx.c diff --git a/hw/iommu.c b/hw/iommu.c new file mode 100644 index 0000000000..f00bb78b03 --- /dev/null +++ b/hw/iommu.c @@ -0,0 +1,221 @@ +/* + * QEMU SPARC iommu emulation + * + * Copyright (c) 2003 Fabrice Bellard + * + * 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 "vl.h" + +/* debug iommu */ +//#define DEBUG_IOMMU + +/* The IOMMU registers occupy three pages in IO space. */ +struct iommu_regs { + /* First page */ + volatile unsigned long control; /* IOMMU control */ + volatile unsigned long base; /* Physical base of iopte page table */ + volatile unsigned long _unused1[3]; + volatile unsigned long tlbflush; /* write only */ + volatile unsigned long pageflush; /* write only */ + volatile unsigned long _unused2[1017]; + /* Second page */ + volatile unsigned long afsr; /* Async-fault status register */ + volatile unsigned long afar; /* Async-fault physical address */ + volatile unsigned long _unused3[2]; + volatile unsigned long sbuscfg0; /* SBUS configuration registers, per-slot */ + volatile unsigned long sbuscfg1; + volatile unsigned long sbuscfg2; + volatile unsigned long sbuscfg3; + volatile unsigned long mfsr; /* Memory-fault status register */ + volatile unsigned long mfar; /* Memory-fault physical address */ + volatile unsigned long _unused4[1014]; + /* Third page */ + volatile unsigned long mid; /* IOMMU module-id */ +}; + +#define IOMMU_CTRL_IMPL 0xf0000000 /* Implementation */ +#define IOMMU_CTRL_VERS 0x0f000000 /* Version */ +#define IOMMU_CTRL_RNGE 0x0000001c /* Mapping RANGE */ +#define IOMMU_RNGE_16MB 0x00000000 /* 0xff000000 -> 0xffffffff */ +#define IOMMU_RNGE_32MB 0x00000004 /* 0xfe000000 -> 0xffffffff */ +#define IOMMU_RNGE_64MB 0x00000008 /* 0xfc000000 -> 0xffffffff */ +#define IOMMU_RNGE_128MB 0x0000000c /* 0xf8000000 -> 0xffffffff */ +#define IOMMU_RNGE_256MB 0x00000010 /* 0xf0000000 -> 0xffffffff */ +#define IOMMU_RNGE_512MB 0x00000014 /* 0xe0000000 -> 0xffffffff */ +#define IOMMU_RNGE_1GB 0x00000018 /* 0xc0000000 -> 0xffffffff */ +#define IOMMU_RNGE_2GB 0x0000001c /* 0x80000000 -> 0xffffffff */ +#define IOMMU_CTRL_ENAB 0x00000001 /* IOMMU Enable */ + +#define IOMMU_AFSR_ERR 0x80000000 /* LE, TO, or BE asserted */ +#define IOMMU_AFSR_LE 0x40000000 /* SBUS reports error after transaction */ +#define IOMMU_AFSR_TO 0x20000000 /* Write access took more than 12.8 us. */ +#define IOMMU_AFSR_BE 0x10000000 /* Write access received error acknowledge */ +#define IOMMU_AFSR_SIZE 0x0e000000 /* Size of transaction causing error */ +#define IOMMU_AFSR_S 0x01000000 /* Sparc was in supervisor mode */ +#define IOMMU_AFSR_RESV 0x00f00000 /* Reserver, forced to 0x8 by hardware */ +#define IOMMU_AFSR_ME 0x00080000 /* Multiple errors occurred */ +#define IOMMU_AFSR_RD 0x00040000 /* A read operation was in progress */ +#define IOMMU_AFSR_FAV 0x00020000 /* IOMMU afar has valid contents */ + +#define IOMMU_SBCFG_SAB30 0x00010000 /* Phys-address bit 30 when bypass enabled */ +#define IOMMU_SBCFG_BA16 0x00000004 /* Slave supports 16 byte bursts */ +#define IOMMU_SBCFG_BA8 0x00000002 /* Slave supports 8 byte bursts */ +#define IOMMU_SBCFG_BYPASS 0x00000001 /* Bypass IOMMU, treat all addresses + produced by this device as pure + physical. */ + +#define IOMMU_MFSR_ERR 0x80000000 /* One or more of PERR1 or PERR0 */ +#define IOMMU_MFSR_S 0x01000000 /* Sparc was in supervisor mode */ +#define IOMMU_MFSR_CPU 0x00800000 /* CPU transaction caused parity error */ +#define IOMMU_MFSR_ME 0x00080000 /* Multiple parity errors occurred */ +#define IOMMU_MFSR_PERR 0x00006000 /* high bit indicates parity error occurred + on the even word of the access, low bit + indicated odd word caused the parity error */ +#define IOMMU_MFSR_BM 0x00001000 /* Error occurred while in boot mode */ +#define IOMMU_MFSR_C 0x00000800 /* Address causing error was marked cacheable */ +#define IOMMU_MFSR_RTYP 0x000000f0 /* Memory request transaction type */ + +#define IOMMU_MID_SBAE 0x001f0000 /* SBus arbitration enable */ +#define IOMMU_MID_SE 0x00100000 /* Enables SCSI/ETHERNET arbitration */ +#define IOMMU_MID_SB3 0x00080000 /* Enable SBUS device 3 arbitration */ +#define IOMMU_MID_SB2 0x00040000 /* Enable SBUS device 2 arbitration */ +#define IOMMU_MID_SB1 0x00020000 /* Enable SBUS device 1 arbitration */ +#define IOMMU_MID_SB0 0x00010000 /* Enable SBUS device 0 arbitration */ +#define IOMMU_MID_MID 0x0000000f /* Module-id, hardcoded to 0x8 */ + +/* The format of an iopte in the page tables */ +#define IOPTE_PAGE 0x07ffff00 /* Physical page number (PA[30:12]) */ +#define IOPTE_CACHE 0x00000080 /* Cached (in vme IOCACHE or Viking/MXCC) */ +#define IOPTE_WRITE 0x00000004 /* Writeable */ +#define IOPTE_VALID 0x00000002 /* IOPTE is valid */ +#define IOPTE_WAZ 0x00000001 /* Write as zeros */ + +#define PHYS_JJ_IOMMU 0x10000000 /* First page of sun4m IOMMU */ +#define PAGE_SHIFT 12 +#define PAGE_SIZE (1 << PAGE_SHIFT) +#define PAGE_MASK (PAGE_SIZE - 1) + +typedef struct IOMMUState { + uint32_t regs[sizeof(struct iommu_regs)]; +} IOMMUState; + +static IOMMUState *ps; + +static int iommu_io_memory; + +static void iommu_reset(IOMMUState *s) +{ +} + +static uint32_t iommu_mem_readw(void *opaque, target_phys_addr_t addr) +{ + IOMMUState *s = opaque; + uint32_t saddr; + + saddr = (addr - PHYS_JJ_IOMMU) >> 2; + switch (saddr) { + default: + return s->regs[saddr]; + break; + } + return 0; +} + +static void iommu_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + IOMMUState *s = opaque; + uint32_t saddr; + + saddr = (addr - PHYS_JJ_IOMMU) >> 2; + switch (saddr) { + default: + s->regs[saddr] = val; + break; + } +} + +static CPUReadMemoryFunc *iommu_mem_read[3] = { + iommu_mem_readw, + iommu_mem_readw, + iommu_mem_readw, +}; + +static CPUWriteMemoryFunc *iommu_mem_write[3] = { + iommu_mem_writew, + iommu_mem_writew, + iommu_mem_writew, +}; + +uint32_t iommu_translate(uint32_t addr) +{ + uint32_t *iopte = (void *)(ps->regs[1] << 4), pa, iostart; + + switch (ps->regs[0] & IOMMU_CTRL_RNGE) { + case IOMMU_RNGE_16MB: + iostart = 0xff000000; + break; + case IOMMU_RNGE_32MB: + iostart = 0xfe000000; + break; + case IOMMU_RNGE_64MB: + iostart = 0xfc000000; + break; + case IOMMU_RNGE_128MB: + iostart = 0xf8000000; + break; + case IOMMU_RNGE_256MB: + iostart = 0xf0000000; + break; + case IOMMU_RNGE_512MB: + iostart = 0xe0000000; + break; + case IOMMU_RNGE_1GB: + iostart = 0xc0000000; + break; + default: + case IOMMU_RNGE_2GB: + iostart = 0x80000000; + break; + } + + iopte += ((addr - iostart) >> PAGE_SHIFT); + cpu_physical_memory_rw((uint32_t)iopte, (void *) &pa, 4, 0); + bswap32s(&pa); + pa = (pa & IOPTE_PAGE) << 4; /* Loose higher bits of 36 */ + //return pa + PAGE_SIZE; + return pa + (addr & PAGE_MASK); +} + +void iommu_init() +{ + IOMMUState *s; + + s = qemu_mallocz(sizeof(IOMMUState)); + if (!s) + return; + + iommu_io_memory = cpu_register_io_memory(0, iommu_mem_read, iommu_mem_write, s); + cpu_register_physical_memory(PHYS_JJ_IOMMU, sizeof(struct iommu_regs), + iommu_io_memory); + + iommu_reset(s); + ps = s; +} + diff --git a/hw/lance.c b/hw/lance.c new file mode 100644 index 0000000000..e461adead1 --- /dev/null +++ b/hw/lance.c @@ -0,0 +1,472 @@ +/* + * QEMU Lance emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * 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 "vl.h" + +/* debug LANCE card */ +#define DEBUG_LANCE + +#define PHYS_JJ_IOMMU 0x10000000 /* First page of sun4m IOMMU */ +#define PHYS_JJ_LEDMA 0x78400010 /* ledma, off by 10 from unused SCSI */ +#define PHYS_JJ_LE 0x78C00000 /* LANCE, typical sun4m */ + +#ifndef LANCE_LOG_TX_BUFFERS +#define LANCE_LOG_TX_BUFFERS 4 +#define LANCE_LOG_RX_BUFFERS 4 +#endif + +#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */ +#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */ + + +#define LE_CSR0 0 +#define LE_CSR1 1 +#define LE_CSR2 2 +#define LE_CSR3 3 +#define LE_MAXREG (LE_CSR3 + 1) + +#define LE_RDP 0 +#define LE_RAP 1 + +#define LE_MO_PROM 0x8000 /* Enable promiscuous mode */ + +#define LE_C0_ERR 0x8000 /* Error: set if BAB, SQE, MISS or ME is set */ +#define LE_C0_BABL 0x4000 /* BAB: Babble: tx timeout. */ +#define LE_C0_CERR 0x2000 /* SQE: Signal quality error */ +#define LE_C0_MISS 0x1000 /* MISS: Missed a packet */ +#define LE_C0_MERR 0x0800 /* ME: Memory error */ +#define LE_C0_RINT 0x0400 /* Received interrupt */ +#define LE_C0_TINT 0x0200 /* Transmitter Interrupt */ +#define LE_C0_IDON 0x0100 /* IFIN: Init finished. */ +#define LE_C0_INTR 0x0080 /* Interrupt or error */ +#define LE_C0_INEA 0x0040 /* Interrupt enable */ +#define LE_C0_RXON 0x0020 /* Receiver on */ +#define LE_C0_TXON 0x0010 /* Transmitter on */ +#define LE_C0_TDMD 0x0008 /* Transmitter demand */ +#define LE_C0_STOP 0x0004 /* Stop the card */ +#define LE_C0_STRT 0x0002 /* Start the card */ +#define LE_C0_INIT 0x0001 /* Init the card */ + +#define LE_C3_BSWP 0x4 /* SWAP */ +#define LE_C3_ACON 0x2 /* ALE Control */ +#define LE_C3_BCON 0x1 /* Byte control */ + +/* Receive message descriptor 1 */ +#define LE_R1_OWN 0x80 /* Who owns the entry */ +#define LE_R1_ERR 0x40 /* Error: if FRA, OFL, CRC or BUF is set */ +#define LE_R1_FRA 0x20 /* FRA: Frame error */ +#define LE_R1_OFL 0x10 /* OFL: Frame overflow */ +#define LE_R1_CRC 0x08 /* CRC error */ +#define LE_R1_BUF 0x04 /* BUF: Buffer error */ +#define LE_R1_SOP 0x02 /* Start of packet */ +#define LE_R1_EOP 0x01 /* End of packet */ +#define LE_R1_POK 0x03 /* Packet is complete: SOP + EOP */ + +#define LE_T1_OWN 0x80 /* Lance owns the packet */ +#define LE_T1_ERR 0x40 /* Error summary */ +#define LE_T1_EMORE 0x10 /* Error: more than one retry needed */ +#define LE_T1_EONE 0x08 /* Error: one retry needed */ +#define LE_T1_EDEF 0x04 /* Error: deferred */ +#define LE_T1_SOP 0x02 /* Start of packet */ +#define LE_T1_EOP 0x01 /* End of packet */ +#define LE_T1_POK 0x03 /* Packet is complete: SOP + EOP */ + +#define LE_T3_BUF 0x8000 /* Buffer error */ +#define LE_T3_UFL 0x4000 /* Error underflow */ +#define LE_T3_LCOL 0x1000 /* Error late collision */ +#define LE_T3_CLOS 0x0800 /* Error carrier loss */ +#define LE_T3_RTY 0x0400 /* Error retry */ +#define LE_T3_TDR 0x03ff /* Time Domain Reflectometry counter */ + +#define TX_RING_SIZE (1 << (LANCE_LOG_TX_BUFFERS)) +#define TX_RING_MOD_MASK (TX_RING_SIZE - 1) +#define TX_RING_LEN_BITS ((LANCE_LOG_TX_BUFFERS) << 29) + +#define RX_RING_SIZE (1 << (LANCE_LOG_RX_BUFFERS)) +#define RX_RING_MOD_MASK (RX_RING_SIZE - 1) +#define RX_RING_LEN_BITS ((LANCE_LOG_RX_BUFFERS) << 29) + +#define PKT_BUF_SZ 1544 +#define RX_BUFF_SIZE PKT_BUF_SZ +#define TX_BUFF_SIZE PKT_BUF_SZ + +struct lance_rx_desc { + unsigned short rmd0; /* low address of packet */ + unsigned char rmd1_bits; /* descriptor bits */ + unsigned char rmd1_hadr; /* high address of packet */ + short length; /* This length is 2s complement (negative)! + * Buffer length + */ + unsigned short mblength; /* This is the actual number of bytes received */ +}; + +struct lance_tx_desc { + unsigned short tmd0; /* low address of packet */ + unsigned char tmd1_bits; /* descriptor bits */ + unsigned char tmd1_hadr; /* high address of packet */ + short length; /* Length is 2s complement (negative)! */ + unsigned short misc; +}; + +/* The LANCE initialization block, described in databook. */ +/* On the Sparc, this block should be on a DMA region */ +struct lance_init_block { + unsigned short mode; /* Pre-set mode (reg. 15) */ + unsigned char phys_addr[6]; /* Physical ethernet address */ + unsigned filter[2]; /* Multicast filter. */ + + /* Receive and transmit ring base, along with extra bits. */ + unsigned short rx_ptr; /* receive descriptor addr */ + unsigned short rx_len; /* receive len and high addr */ + unsigned short tx_ptr; /* transmit descriptor addr */ + unsigned short tx_len; /* transmit len and high addr */ + + /* The Tx and Rx ring entries must aligned on 8-byte boundaries. */ + struct lance_rx_desc brx_ring[RX_RING_SIZE]; + struct lance_tx_desc btx_ring[TX_RING_SIZE]; + + char tx_buf [TX_RING_SIZE][TX_BUFF_SIZE]; + char pad[2]; /* align rx_buf for copy_and_sum(). */ + char rx_buf [RX_RING_SIZE][RX_BUFF_SIZE]; +}; + +#define LEDMA_REGS 4 +#if 0 +/* Structure to describe the current status of DMA registers on the Sparc */ +struct sparc_dma_registers { + uint32_t cond_reg; /* DMA condition register */ + uint32_t st_addr; /* Start address of this transfer */ + uint32_t cnt; /* How many bytes to transfer */ + uint32_t dma_test; /* DMA test register */ +}; +#endif + +typedef struct LEDMAState { + uint32_t regs[LEDMA_REGS]; +} LEDMAState; + +typedef struct LANCEState { + NetDriverState *nd; + uint32_t leptr; + uint16_t addr; + uint16_t regs[LE_MAXREG]; + uint8_t phys[6]; /* mac address */ + int irq; + LEDMAState *ledma; +} LANCEState; + +static int lance_io_memory; + +static unsigned int rxptr, txptr; + +static void lance_send(void *opaque); + +static void lance_reset(LANCEState *s) +{ + memcpy(s->phys, s->nd->macaddr, 6); + rxptr = 0; + txptr = 0; + s->regs[LE_CSR0] = LE_C0_STOP; +} + +static uint32_t lance_mem_readw(void *opaque, target_phys_addr_t addr) +{ + LANCEState *s = opaque; + uint32_t saddr; + + saddr = addr - PHYS_JJ_LE; + switch (saddr >> 1) { + case LE_RDP: + return s->regs[s->addr]; + case LE_RAP: + return s->addr; + default: + break; + } + return 0; +} + +static void lance_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + LANCEState *s = opaque; + uint32_t saddr; + uint16_t clear, reg; + + saddr = addr - PHYS_JJ_LE; + switch (saddr >> 1) { + case LE_RDP: + switch(s->addr) { + case LE_CSR0: + if (val & LE_C0_STOP) { + s->regs[LE_CSR0] = LE_C0_STOP; + break; + } + + reg = s->regs[LE_CSR0]; + + // 1 = clear for some bits + reg &= ~(val & 0x7f00); + + // generated bits + reg &= ~(LE_C0_ERR | LE_C0_INTR); + if (reg & 0x7100) + reg |= LE_C0_ERR; + if (reg & 0x7f00) + reg |= LE_C0_INTR; + + // direct bit + reg &= ~LE_C0_INEA; + reg |= val & LE_C0_INEA; + + // exclusive bits + if (val & LE_C0_INIT) { + reg |= LE_C0_IDON | LE_C0_INIT; + reg &= ~LE_C0_STOP; + } + else if (val & LE_C0_STRT) { + reg |= LE_C0_STRT | LE_C0_RXON | LE_C0_TXON; + reg &= ~LE_C0_STOP; + } + + s->regs[LE_CSR0] = reg; + + // trigger bits + //if (val & LE_C0_TDMD) + + if ((s->regs[LE_CSR0] & LE_C0_INTR) && (s->regs[LE_CSR0] & LE_C0_INEA)) + pic_set_irq(s->irq, 1); + break; + case LE_CSR1: + s->leptr = (s->leptr & 0xffff0000) | (val & 0xffff); + s->regs[s->addr] = val; + break; + case LE_CSR2: + s->leptr = (s->leptr & 0xffff) | ((val & 0xffff) << 16); + s->regs[s->addr] = val; + break; + case LE_CSR3: + s->regs[s->addr] = val; + break; + } + break; + case LE_RAP: + if (val < LE_MAXREG) + s->addr = val; + break; + default: + break; + } + lance_send(s); +} + +static CPUReadMemoryFunc *lance_mem_read[3] = { + lance_mem_readw, + lance_mem_readw, + lance_mem_readw, +}; + +static CPUWriteMemoryFunc *lance_mem_write[3] = { + lance_mem_writew, + lance_mem_writew, + lance_mem_writew, +}; + + +/* return the max buffer size if the LANCE can receive more data */ +static int lance_can_receive(void *opaque) +{ + LANCEState *s = opaque; + void *dmaptr = (void *) (s->leptr + s->ledma->regs[3]); + struct lance_init_block *ib; + int i; + uint16_t temp; + + if ((s->regs[LE_CSR0] & LE_C0_STOP) == LE_C0_STOP) + return 0; + + ib = (void *) iommu_translate(dmaptr); + + for (i = 0; i < RX_RING_SIZE; i++) { + cpu_physical_memory_read(&ib->brx_ring[i].rmd1_bits, (void *) &temp, 1); + temp &= 0xff; + if (temp == (LE_R1_OWN)) { +#ifdef DEBUG_LANCE + fprintf(stderr, "lance: can receive %d\n", RX_BUFF_SIZE); +#endif + return RX_BUFF_SIZE; + } + } +#ifdef DEBUG_LANCE + fprintf(stderr, "lance: cannot receive\n"); +#endif + return 0; +} + +#define MIN_BUF_SIZE 60 + +static void lance_receive(void *opaque, const uint8_t *buf, int size) +{ + LANCEState *s = opaque; + void *dmaptr = (void *) (s->leptr + s->ledma->regs[3]); + struct lance_init_block *ib; + unsigned int i, old_rxptr, j; + uint16_t temp; + + if ((s->regs[LE_CSR0] & LE_C0_STOP) == LE_C0_STOP) + return; + + ib = (void *) iommu_translate(dmaptr); + + old_rxptr = rxptr; + for (i = rxptr; i != ((old_rxptr - 1) & RX_RING_MOD_MASK); i = (i + 1) & RX_RING_MOD_MASK) { + cpu_physical_memory_read(&ib->brx_ring[i].rmd1_bits, (void *) &temp, 1); + if (temp == (LE_R1_OWN)) { + rxptr = (rxptr + 1) & RX_RING_MOD_MASK; + temp = size; + bswap16s(&temp); + cpu_physical_memory_write(&ib->brx_ring[i].mblength, (void *) &temp, 2); +#if 0 + cpu_physical_memory_write(&ib->rx_buf[i], buf, size); +#else + for (j = 0; j < size; j++) { + cpu_physical_memory_write(((void *)&ib->rx_buf[i]) + j, &buf[j], 1); + } +#endif + temp = LE_R1_POK; + cpu_physical_memory_write(&ib->brx_ring[i].rmd1_bits, (void *) &temp, 1); + s->regs[LE_CSR0] |= LE_C0_RINT | LE_C0_INTR; + if ((s->regs[LE_CSR0] & LE_C0_INTR) && (s->regs[LE_CSR0] & LE_C0_INEA)) + pic_set_irq(s->irq, 1); +#ifdef DEBUG_LANCE + fprintf(stderr, "lance: got packet, len %d\n", size); +#endif + return; + } + } +} + +static void lance_send(void *opaque) +{ + LANCEState *s = opaque; + void *dmaptr = (void *) (s->leptr + s->ledma->regs[3]); + struct lance_init_block *ib; + unsigned int i, old_txptr, j; + uint16_t temp; + char pkt_buf[PKT_BUF_SZ]; + + if ((s->regs[LE_CSR0] & LE_C0_STOP) == LE_C0_STOP) + return; + + ib = (void *) iommu_translate(dmaptr); + + old_txptr = txptr; + for (i = txptr; i != ((old_txptr - 1) & TX_RING_MOD_MASK); i = (i + 1) & TX_RING_MOD_MASK) { + cpu_physical_memory_read(&ib->btx_ring[i].tmd1_bits, (void *) &temp, 1); + if (temp == (LE_T1_POK|LE_T1_OWN)) { + cpu_physical_memory_read(&ib->btx_ring[i].length, (void *) &temp, 2); + bswap16s(&temp); + temp = (~temp) + 1; +#if 0 + cpu_physical_memory_read(&ib->tx_buf[i], pkt_buf, temp); +#else + for (j = 0; j < temp; j++) { + cpu_physical_memory_read(((void *)&ib->tx_buf[i]) + j, &pkt_buf[j], 1); + } +#endif + +#ifdef DEBUG_LANCE + fprintf(stderr, "lance: sending packet, len %d\n", temp); +#endif + qemu_send_packet(s->nd, pkt_buf, temp); + temp = LE_T1_POK; + cpu_physical_memory_write(&ib->btx_ring[i].tmd1_bits, (void *) &temp, 1); + txptr = (txptr + 1) & TX_RING_MOD_MASK; + s->regs[LE_CSR0] |= LE_C0_TINT | LE_C0_INTR; + } + } +} + +static int ledma_io_memory; + +static uint32_t ledma_mem_readl(void *opaque, target_phys_addr_t addr) +{ + LEDMAState *s = opaque; + uint32_t saddr; + + saddr = (addr - PHYS_JJ_LEDMA) >> 2; + if (saddr < LEDMA_REGS) + return s->regs[saddr]; + else + return 0; +} + +static void ledma_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + LEDMAState *s = opaque; + uint32_t saddr; + + saddr = (addr - PHYS_JJ_LEDMA) >> 2; + if (saddr < LEDMA_REGS) + s->regs[saddr] = val; +} + +static CPUReadMemoryFunc *ledma_mem_read[3] = { + ledma_mem_readl, + ledma_mem_readl, + ledma_mem_readl, +}; + +static CPUWriteMemoryFunc *ledma_mem_write[3] = { + ledma_mem_writel, + ledma_mem_writel, + ledma_mem_writel, +}; + +void lance_init(NetDriverState *nd, int irq) +{ + LANCEState *s; + LEDMAState *led; + + s = qemu_mallocz(sizeof(LANCEState)); + if (!s) + return; + + lance_io_memory = cpu_register_io_memory(0, lance_mem_read, lance_mem_write, s); + cpu_register_physical_memory(PHYS_JJ_LE, 8, + lance_io_memory); + led = qemu_mallocz(sizeof(LEDMAState)); + if (!led) + return; + + ledma_io_memory = cpu_register_io_memory(0, ledma_mem_read, ledma_mem_write, led); + cpu_register_physical_memory(PHYS_JJ_LEDMA, 16, + ledma_io_memory); + + s->nd = nd; + s->ledma = led; + s->irq = irq; + + lance_reset(s); + qemu_add_read_packet(nd, lance_can_receive, lance_receive, s); +} + diff --git a/hw/m48t08.c b/hw/m48t08.c new file mode 100644 index 0000000000..c5b6e7a728 --- /dev/null +++ b/hw/m48t08.c @@ -0,0 +1,391 @@ +/* + * QEMU M48T08 NVRAM emulation for Sparc platform + * + * Copyright (c) 2003-2004 Jocelyn Mayer + * + * 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 "vl.h" +#include "m48t08.h" + +//#define DEBUG_NVRAM + +#if defined(DEBUG_NVRAM) +#define NVRAM_PRINTF(fmt, args...) do { printf(fmt , ##args); } while (0) +#else +#define NVRAM_PRINTF(fmt, args...) do { } while (0) +#endif + +#define NVRAM_MAX_MEM 0xfff0 + +struct m48t08_t { + /* Hardware parameters */ + int mem_index; + uint32_t mem_base; + uint16_t size; + /* RTC management */ + time_t time_offset; + time_t stop_time; + /* NVRAM storage */ + uint8_t lock; + uint16_t addr; + uint8_t *buffer; +}; + +/* Fake timer functions */ +/* Generic helpers for BCD */ +static inline uint8_t toBCD (uint8_t value) +{ + return (((value / 10) % 10) << 4) | (value % 10); +} + +static inline uint8_t fromBCD (uint8_t BCD) +{ + return ((BCD >> 4) * 10) + (BCD & 0x0F); +} + +/* RTC management helpers */ +static void get_time (m48t08_t *NVRAM, struct tm *tm) +{ + time_t t; + + t = time(NULL) + NVRAM->time_offset; +#ifdef _WIN32 + memcpy(tm,localtime(&t),sizeof(*tm)); +#else + localtime_r (&t, tm) ; +#endif +} + +static void set_time (m48t08_t *NVRAM, struct tm *tm) +{ + time_t now, new_time; + + new_time = mktime(tm); + now = time(NULL); + NVRAM->time_offset = new_time - now; +} + +/* Direct access to NVRAM */ +void m48t08_write (m48t08_t *NVRAM, uint32_t val) +{ + struct tm tm; + int tmp; + + if (NVRAM->addr > NVRAM_MAX_MEM && NVRAM->addr < 0x2000) + NVRAM_PRINTF("%s: 0x%08x => 0x%08x\n", __func__, NVRAM->addr, val); + switch (NVRAM->addr) { + case 0x1FF8: + /* control */ + NVRAM->buffer[0x1FF8] = (val & ~0xA0) | 0x90; + break; + case 0x1FF9: + /* seconds (BCD) */ + tmp = fromBCD(val & 0x7F); + if (tmp >= 0 && tmp <= 59) { + get_time(NVRAM, &tm); + tm.tm_sec = tmp; + set_time(NVRAM, &tm); + } + if ((val & 0x80) ^ (NVRAM->buffer[0x1FF9] & 0x80)) { + if (val & 0x80) { + NVRAM->stop_time = time(NULL); + } else { + NVRAM->time_offset += NVRAM->stop_time - time(NULL); + NVRAM->stop_time = 0; + } + } + NVRAM->buffer[0x1FF9] = val & 0x80; + break; + case 0x1FFA: + /* minutes (BCD) */ + tmp = fromBCD(val & 0x7F); + if (tmp >= 0 && tmp <= 59) { + get_time(NVRAM, &tm); + tm.tm_min = tmp; + set_time(NVRAM, &tm); + } + break; + case 0x1FFB: + /* hours (BCD) */ + tmp = fromBCD(val & 0x3F); + if (tmp >= 0 && tmp <= 23) { + get_time(NVRAM, &tm); + tm.tm_hour = tmp; + set_time(NVRAM, &tm); + } + break; + case 0x1FFC: + /* day of the week / century */ + tmp = fromBCD(val & 0x07); + get_time(NVRAM, &tm); + tm.tm_wday = tmp; + set_time(NVRAM, &tm); + NVRAM->buffer[0x1FFC] = val & 0x40; + break; + case 0x1FFD: + /* date */ + tmp = fromBCD(val & 0x1F); + if (tmp != 0) { + get_time(NVRAM, &tm); + tm.tm_mday = tmp; + set_time(NVRAM, &tm); + } + break; + case 0x1FFE: + /* month */ + tmp = fromBCD(val & 0x1F); + if (tmp >= 1 && tmp <= 12) { + get_time(NVRAM, &tm); + tm.tm_mon = tmp - 1; + set_time(NVRAM, &tm); + } + break; + case 0x1FFF: + /* year */ + tmp = fromBCD(val); + if (tmp >= 0 && tmp <= 99) { + get_time(NVRAM, &tm); + tm.tm_year = fromBCD(val); + set_time(NVRAM, &tm); + } + break; + default: + /* Check lock registers state */ + if (NVRAM->addr >= 0x20 && NVRAM->addr <= 0x2F && (NVRAM->lock & 1)) + break; + if (NVRAM->addr >= 0x30 && NVRAM->addr <= 0x3F && (NVRAM->lock & 2)) + break; + if (NVRAM->addr < NVRAM_MAX_MEM || + (NVRAM->addr > 0x1FFF && NVRAM->addr < NVRAM->size)) { + NVRAM->buffer[NVRAM->addr] = val & 0xFF; + } + break; + } +} + +uint32_t m48t08_read (m48t08_t *NVRAM) +{ + struct tm tm; + uint32_t retval = 0xFF; + + switch (NVRAM->addr) { + case 0x1FF8: + /* control */ + goto do_read; + case 0x1FF9: + /* seconds (BCD) */ + get_time(NVRAM, &tm); + retval = (NVRAM->buffer[0x1FF9] & 0x80) | toBCD(tm.tm_sec); + break; + case 0x1FFA: + /* minutes (BCD) */ + get_time(NVRAM, &tm); + retval = toBCD(tm.tm_min); + break; + case 0x1FFB: + /* hours (BCD) */ + get_time(NVRAM, &tm); + retval = toBCD(tm.tm_hour); + break; + case 0x1FFC: + /* day of the week / century */ + get_time(NVRAM, &tm); + retval = NVRAM->buffer[0x1FFC] | tm.tm_wday; + break; + case 0x1FFD: + /* date */ + get_time(NVRAM, &tm); + retval = toBCD(tm.tm_mday); + break; + case 0x1FFE: + /* month */ + get_time(NVRAM, &tm); + retval = toBCD(tm.tm_mon + 1); + break; + case 0x1FFF: + /* year */ + get_time(NVRAM, &tm); + retval = toBCD(tm.tm_year); + break; + default: + /* Check lock registers state */ + if (NVRAM->addr >= 0x20 && NVRAM->addr <= 0x2F && (NVRAM->lock & 1)) + break; + if (NVRAM->addr >= 0x30 && NVRAM->addr <= 0x3F && (NVRAM->lock & 2)) + break; + if (NVRAM->addr < NVRAM_MAX_MEM || + (NVRAM->addr > 0x1FFF && NVRAM->addr < NVRAM->size)) { + do_read: + retval = NVRAM->buffer[NVRAM->addr]; + } + break; + } + if (NVRAM->addr > NVRAM_MAX_MEM + 1 && NVRAM->addr < 0x2000) + NVRAM_PRINTF("0x%08x <= 0x%08x\n", NVRAM->addr, retval); + + return retval; +} + +void m48t08_set_addr (m48t08_t *NVRAM, uint32_t addr) +{ + NVRAM->addr = addr; +} + +void m48t08_toggle_lock (m48t08_t *NVRAM, int lock) +{ + NVRAM->lock ^= 1 << lock; +} + +static void nvram_writeb (void *opaque, target_phys_addr_t addr, uint32_t value) +{ + m48t08_t *NVRAM = opaque; + + addr -= NVRAM->mem_base; + if (addr < NVRAM_MAX_MEM) + NVRAM->buffer[addr] = value; +} + +static void nvram_writew (void *opaque, target_phys_addr_t addr, uint32_t value) +{ + m48t08_t *NVRAM = opaque; + + addr -= NVRAM->mem_base; + if (addr < NVRAM_MAX_MEM) { + NVRAM->buffer[addr] = value >> 8; + NVRAM->buffer[addr + 1] = value; + } +} + +static void nvram_writel (void *opaque, target_phys_addr_t addr, uint32_t value) +{ + m48t08_t *NVRAM = opaque; + + addr -= NVRAM->mem_base; + if (addr < NVRAM_MAX_MEM) { + NVRAM->buffer[addr] = value >> 24; + NVRAM->buffer[addr + 1] = value >> 16; + NVRAM->buffer[addr + 2] = value >> 8; + NVRAM->buffer[addr + 3] = value; + } +} + +static uint32_t nvram_readb (void *opaque, target_phys_addr_t addr) +{ + m48t08_t *NVRAM = opaque; + uint32_t retval = 0; + + addr -= NVRAM->mem_base; + if (addr < NVRAM_MAX_MEM) + retval = NVRAM->buffer[addr]; + + return retval; +} + +static uint32_t nvram_readw (void *opaque, target_phys_addr_t addr) +{ + m48t08_t *NVRAM = opaque; + uint32_t retval = 0; + + addr -= NVRAM->mem_base; + if (addr < NVRAM_MAX_MEM) { + retval = NVRAM->buffer[addr] << 8; + retval |= NVRAM->buffer[addr + 1]; + } + + return retval; +} + +static uint32_t nvram_readl (void *opaque, target_phys_addr_t addr) +{ + m48t08_t *NVRAM = opaque; + uint32_t retval = 0; + + addr -= NVRAM->mem_base; + if (addr < NVRAM_MAX_MEM) { + retval = NVRAM->buffer[addr] << 24; + retval |= NVRAM->buffer[addr + 1] << 16; + retval |= NVRAM->buffer[addr + 2] << 8; + retval |= NVRAM->buffer[addr + 3]; + } + + return retval; +} + +static CPUWriteMemoryFunc *nvram_write[] = { + &nvram_writeb, + &nvram_writew, + &nvram_writel, +}; + +static CPUReadMemoryFunc *nvram_read[] = { + &nvram_readb, + &nvram_readw, + &nvram_readl, +}; + +/* Initialisation routine */ +m48t08_t *m48t08_init(uint32_t mem_base, uint16_t size) +{ + m48t08_t *s; + int i; + unsigned char tmp = 0; + + s = qemu_mallocz(sizeof(m48t08_t)); + if (!s) + return NULL; + s->buffer = qemu_mallocz(size); + if (!s->buffer) { + qemu_free(s); + return NULL; + } + s->size = size; + s->mem_base = mem_base; + s->addr = 0; + if (mem_base != 0) { + s->mem_index = cpu_register_io_memory(0, nvram_read, nvram_write, s); + cpu_register_physical_memory(mem_base, 0x4000, s->mem_index); + } + s->lock = 0; + + i = 0x1fd8; + s->buffer[i++] = 0x01; + s->buffer[i++] = 0x80; /* Sun4m OBP */ + /* XXX: Ethernet address, etc */ + + /* Calculate checksum */ + for (i = 0x1fd8; i < 0x1fe7; i++) { + tmp ^= s->buffer[i]; + } + s->buffer[0x1fe7] = tmp; + return s; +} + +#if 0 +struct idprom +{ + unsigned char id_format; /* Format identifier (always 0x01) */ + unsigned char id_machtype; /* Machine type */ + unsigned char id_ethaddr[6]; /* Hardware ethernet address */ + long id_date; /* Date of manufacture */ + unsigned int id_sernum:24; /* Unique serial number */ + unsigned char id_cksum; /* Checksum - xor of the data bytes */ + unsigned char reserved[16]; +}; +#endif diff --git a/hw/m48t08.h b/hw/m48t08.h new file mode 100644 index 0000000000..2a754b698e --- /dev/null +++ b/hw/m48t08.h @@ -0,0 +1,12 @@ +#if !defined (__M48T08_H__) +#define __M48T08_H__ + +typedef struct m48t08_t m48t08_t; + +void m48t08_write (m48t08_t *NVRAM, uint32_t val); +uint32_t m48t08_read (m48t08_t *NVRAM); +void m48t08_set_addr (m48t08_t *NVRAM, uint32_t addr); +void m48t08_toggle_lock (m48t08_t *NVRAM, int lock); +m48t08_t *m48t08_init(uint32_t mem_base, uint16_t size); + +#endif /* !defined (__M48T08_H__) */ diff --git a/hw/magic-load.c b/hw/magic-load.c new file mode 100644 index 0000000000..7365183da2 --- /dev/null +++ b/hw/magic-load.c @@ -0,0 +1,341 @@ +/* This is the Linux kernel elf-loading code, ported into user space */ +#include "vl.h" +#include "disas.h" + +/* XXX: this code is not used as it is under the GPL license. Please + remove or recode it */ +//#define USE_ELF_LOADER + +#ifdef USE_ELF_LOADER +/* should probably go in elf.h */ +#ifndef ELIBBAD +#define ELIBBAD 80 +#endif + + +#define ELF_START_MMAP 0x80000000 + +#define elf_check_arch(x) ( (x) == EM_SPARC ) + +#define ELF_CLASS ELFCLASS32 +#define ELF_DATA ELFDATA2MSB +#define ELF_ARCH EM_SPARC + +#include "elf.h" + +/* + * This structure is used to hold the arguments that are + * used when loading binaries. + */ +struct linux_binprm { + char buf[128]; + int fd; +}; + +#define TARGET_ELF_EXEC_PAGESIZE TARGET_PAGE_SIZE +#define TARGET_ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(TARGET_ELF_EXEC_PAGESIZE-1)) +#define TARGET_ELF_PAGEOFFSET(_v) ((_v) & (TARGET_ELF_EXEC_PAGESIZE-1)) + +#ifdef BSWAP_NEEDED +static void bswap_ehdr(Elf32_Ehdr *ehdr) +{ + bswap16s(&ehdr->e_type); /* Object file type */ + bswap16s(&ehdr->e_machine); /* Architecture */ + bswap32s(&ehdr->e_version); /* Object file version */ + bswap32s(&ehdr->e_entry); /* Entry point virtual address */ + bswap32s(&ehdr->e_phoff); /* Program header table file offset */ + bswap32s(&ehdr->e_shoff); /* Section header table file offset */ + bswap32s(&ehdr->e_flags); /* Processor-specific flags */ + bswap16s(&ehdr->e_ehsize); /* ELF header size in bytes */ + bswap16s(&ehdr->e_phentsize); /* Program header table entry size */ + bswap16s(&ehdr->e_phnum); /* Program header table entry count */ + bswap16s(&ehdr->e_shentsize); /* Section header table entry size */ + bswap16s(&ehdr->e_shnum); /* Section header table entry count */ + bswap16s(&ehdr->e_shstrndx); /* Section header string table index */ +} + +static void bswap_phdr(Elf32_Phdr *phdr) +{ + bswap32s(&phdr->p_type); /* Segment type */ + bswap32s(&phdr->p_offset); /* Segment file offset */ + bswap32s(&phdr->p_vaddr); /* Segment virtual address */ + bswap32s(&phdr->p_paddr); /* Segment physical address */ + bswap32s(&phdr->p_filesz); /* Segment size in file */ + bswap32s(&phdr->p_memsz); /* Segment size in memory */ + bswap32s(&phdr->p_flags); /* Segment flags */ + bswap32s(&phdr->p_align); /* Segment alignment */ +} + +static void bswap_shdr(Elf32_Shdr *shdr) +{ + bswap32s(&shdr->sh_name); + bswap32s(&shdr->sh_type); + bswap32s(&shdr->sh_flags); + bswap32s(&shdr->sh_addr); + bswap32s(&shdr->sh_offset); + bswap32s(&shdr->sh_size); + bswap32s(&shdr->sh_link); + bswap32s(&shdr->sh_info); + bswap32s(&shdr->sh_addralign); + bswap32s(&shdr->sh_entsize); +} + +static void bswap_sym(Elf32_Sym *sym) +{ + bswap32s(&sym->st_name); + bswap32s(&sym->st_value); + bswap32s(&sym->st_size); + bswap16s(&sym->st_shndx); +} +#endif + +static int prepare_binprm(struct linux_binprm *bprm) +{ + int retval; + + memset(bprm->buf, 0, sizeof(bprm->buf)); + retval = lseek(bprm->fd, 0L, SEEK_SET); + if(retval >= 0) { + retval = read(bprm->fd, bprm->buf, 128); + } + if(retval < 0) { + perror("prepare_binprm"); + exit(-1); + /* return(-errno); */ + } + else { + return(retval); + } +} + +/* Best attempt to load symbols from this ELF object. */ +static void load_symbols(struct elfhdr *hdr, int fd) +{ + unsigned int i; + struct elf_shdr sechdr, symtab, strtab; + char *strings; + + lseek(fd, hdr->e_shoff, SEEK_SET); + for (i = 0; i < hdr->e_shnum; i++) { + if (read(fd, &sechdr, sizeof(sechdr)) != sizeof(sechdr)) + return; +#ifdef BSWAP_NEEDED + bswap_shdr(&sechdr); +#endif + if (sechdr.sh_type == SHT_SYMTAB) { + symtab = sechdr; + lseek(fd, hdr->e_shoff + + sizeof(sechdr) * sechdr.sh_link, SEEK_SET); + if (read(fd, &strtab, sizeof(strtab)) + != sizeof(strtab)) + return; +#ifdef BSWAP_NEEDED + bswap_shdr(&strtab); +#endif + goto found; + } + } + return; /* Shouldn't happen... */ + + found: + /* Now know where the strtab and symtab are. Snarf them. */ + disas_symtab = qemu_malloc(symtab.sh_size); + disas_strtab = strings = qemu_malloc(strtab.sh_size); + if (!disas_symtab || !disas_strtab) + return; + + lseek(fd, symtab.sh_offset, SEEK_SET); + if (read(fd, disas_symtab, symtab.sh_size) != symtab.sh_size) + return; + +#ifdef BSWAP_NEEDED + for (i = 0; i < symtab.sh_size / sizeof(struct elf_sym); i++) + bswap_sym(disas_symtab + sizeof(struct elf_sym)*i); +#endif + + lseek(fd, strtab.sh_offset, SEEK_SET); + if (read(fd, strings, strtab.sh_size) != strtab.sh_size) + return; + disas_num_syms = symtab.sh_size / sizeof(struct elf_sym); +} + +static int load_elf_binary(struct linux_binprm * bprm, uint8_t *addr) +{ + struct elfhdr elf_ex; + unsigned long startaddr = addr; + int i; + struct elf_phdr * elf_ppnt; + struct elf_phdr *elf_phdata; + int retval; + + elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */ +#ifdef BSWAP_NEEDED + bswap_ehdr(&elf_ex); +#endif + + if (elf_ex.e_ident[0] != 0x7f || + strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) { + return -ENOEXEC; + } + + /* First of all, some simple consistency checks */ + if (! elf_check_arch(elf_ex.e_machine)) { + return -ENOEXEC; + } + + /* Now read in all of the header information */ + elf_phdata = (struct elf_phdr *)qemu_malloc(elf_ex.e_phentsize*elf_ex.e_phnum); + if (elf_phdata == NULL) { + return -ENOMEM; + } + + retval = lseek(bprm->fd, elf_ex.e_phoff, SEEK_SET); + if(retval > 0) { + retval = read(bprm->fd, (char *) elf_phdata, + elf_ex.e_phentsize * elf_ex.e_phnum); + } + + if (retval < 0) { + perror("load_elf_binary"); + exit(-1); + qemu_free (elf_phdata); + return -errno; + } + +#ifdef BSWAP_NEEDED + elf_ppnt = elf_phdata; + for (i=0; ip_type != PT_LOAD) + continue; +#if 0 + error = target_mmap(TARGET_ELF_PAGESTART(load_bias + elf_ppnt->p_vaddr), + elf_prot, + (MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE), + bprm->fd, + (elf_ppnt->p_offset - + TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr))); +#endif + //offset = elf_ppnt->p_offset - TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr); + offset = 0x4000; + lseek(bprm->fd, offset, SEEK_SET); + len = elf_ppnt->p_filesz + TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr); + error = read(bprm->fd, addr, len); + + if (error == -1) { + perror("mmap"); + exit(-1); + } + addr += len; + } + + qemu_free(elf_phdata); + + load_symbols(&elf_ex, bprm->fd); + + return addr-startaddr; +} + +int elf_exec(const char * filename, uint8_t *addr) +{ + struct linux_binprm bprm; + int retval; + + retval = open(filename, O_RDONLY); + if (retval < 0) + return retval; + bprm.fd = retval; + + retval = prepare_binprm(&bprm); + + if(retval>=0) { + retval = load_elf_binary(&bprm, addr); + } + return retval; +} +#endif + +int load_kernel(const char *filename, uint8_t *addr) +{ + int fd, size; + + fd = open(filename, O_RDONLY | O_BINARY); + if (fd < 0) + return -1; + /* load 32 bit code */ + size = read(fd, addr, 16 * 1024 * 1024); + if (size < 0) + goto fail; + close(fd); + return size; + fail: + close(fd); + return -1; +} + +static char saved_kfn[1024]; +static uint32_t saved_addr; +static int magic_state; + +static uint32_t magic_mem_readl(void *opaque, target_phys_addr_t addr) +{ + int ret; + + if (magic_state == 0) { +#ifdef USE_ELF_LOADER + ret = elf_exec(saved_kfn, saved_addr); +#else + ret = load_kernel(saved_kfn, (uint8_t *)saved_addr); +#endif + if (ret < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + saved_kfn); + } + magic_state = 1; /* No more magic */ + tb_flush(); + } + return ret; +} + +static void magic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ +} + + +static CPUReadMemoryFunc *magic_mem_read[3] = { + magic_mem_readl, + magic_mem_readl, + magic_mem_readl, +}; + +static CPUWriteMemoryFunc *magic_mem_write[3] = { + magic_mem_writel, + magic_mem_writel, + magic_mem_writel, +}; + +void magic_init(const char *kfn, int kloadaddr) +{ + int magic_io_memory; + + strcpy(saved_kfn, kfn); + saved_addr = kloadaddr; + magic_state = 0; + magic_io_memory = cpu_register_io_memory(0, magic_mem_read, magic_mem_write, 0); + cpu_register_physical_memory(0x20000000, 4, + magic_io_memory); +} + diff --git a/hw/sched.c b/hw/sched.c new file mode 100644 index 0000000000..c9a685d44a --- /dev/null +++ b/hw/sched.c @@ -0,0 +1,346 @@ +/* + * QEMU interrupt controller & timer emulation + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * 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 "vl.h" + +#define PHYS_JJ_CLOCK 0x71D00000 +#define PHYS_JJ_CLOCK1 0x71D10000 +#define PHYS_JJ_INTR0 0x71E00000 /* CPU0 interrupt control registers */ +#define PHYS_JJ_INTR_G 0x71E10000 /* Master interrupt control registers */ + +/* These registers are used for sending/receiving irqs from/to + * different cpu's. + */ +struct sun4m_intreg_percpu { + unsigned int tbt; /* Intrs pending for this cpu, by PIL. */ + /* These next two registers are WRITE-ONLY and are only + * "on bit" sensitive, "off bits" written have NO affect. + */ + unsigned int clear; /* Clear this cpus irqs here. */ + unsigned int set; /* Set this cpus irqs here. */ +}; +/* + * djhr + * Actually the clear and set fields in this struct are misleading.. + * according to the SLAVIO manual (and the same applies for the SEC) + * the clear field clears bits in the mask which will ENABLE that IRQ + * the set field sets bits in the mask to DISABLE the IRQ. + * + * Also the undirected_xx address in the SLAVIO is defined as + * RESERVED and write only.. + * + * DAVEM_NOTE: The SLAVIO only specifies behavior on uniprocessor + * sun4m machines, for MP the layout makes more sense. + */ +struct sun4m_intreg_master { + unsigned int tbt; /* IRQ's that are pending, see sun4m masks. */ + unsigned int irqs; /* Master IRQ bits. */ + + /* Again, like the above, two these registers are WRITE-ONLY. */ + unsigned int clear; /* Clear master IRQ's by setting bits here. */ + unsigned int set; /* Set master IRQ's by setting bits here. */ + + /* This register is both READ and WRITE. */ + unsigned int undirected_target; /* Which cpu gets undirected irqs. */ +}; +/* + * Registers of hardware timer in sun4m. + */ +struct sun4m_timer_percpu { + volatile unsigned int l14_timer_limit; /* Initial value is 0x009c4000 */ + volatile unsigned int l14_cur_count; +}; + +struct sun4m_timer_global { + volatile unsigned int l10_timer_limit; + volatile unsigned int l10_cur_count; +}; + +#define SUN4M_INT_ENABLE 0x80000000 +#define SUN4M_INT_E14 0x00000080 +#define SUN4M_INT_E10 0x00080000 + +#define SUN4M_HARD_INT(x) (0x000000001 << (x)) +#define SUN4M_SOFT_INT(x) (0x000010000 << (x)) + +#define SUN4M_INT_MASKALL 0x80000000 /* mask all interrupts */ +#define SUN4M_INT_MODULE_ERR 0x40000000 /* module error */ +#define SUN4M_INT_M2S_WRITE 0x20000000 /* write buffer error */ +#define SUN4M_INT_ECC 0x10000000 /* ecc memory error */ +#define SUN4M_INT_FLOPPY 0x00400000 /* floppy disk */ +#define SUN4M_INT_MODULE 0x00200000 /* module interrupt */ +#define SUN4M_INT_VIDEO 0x00100000 /* onboard video */ +#define SUN4M_INT_REALTIME 0x00080000 /* system timer */ +#define SUN4M_INT_SCSI 0x00040000 /* onboard scsi */ +#define SUN4M_INT_AUDIO 0x00020000 /* audio/isdn */ +#define SUN4M_INT_ETHERNET 0x00010000 /* onboard ethernet */ +#define SUN4M_INT_SERIAL 0x00008000 /* serial ports */ +#define SUN4M_INT_SBUSBITS 0x00003F80 /* sbus int bits */ + +#define SUN4M_INT_SBUS(x) (1 << (x+7)) +#define SUN4M_INT_VME(x) (1 << (x)) + +typedef struct SCHEDState { + uint32_t intreg_pending; + uint32_t intreg_enabled; + uint32_t intregm_pending; + uint32_t intregm_enabled; + uint32_t timer_regs[2]; + uint32_t timerm_regs[2]; +} SCHEDState; + +static SCHEDState *ps; + +static int intreg_io_memory, intregm_io_memory, + timer_io_memory, timerm_io_memory; + +static void sched_reset(SCHEDState *s) +{ +} + +static uint32_t intreg_mem_readl(void *opaque, target_phys_addr_t addr) +{ + SCHEDState *s = opaque; + uint32_t saddr; + + saddr = (addr - PHYS_JJ_INTR0) >> 2; + switch (saddr) { + case 0: + return s->intreg_pending; + break; + default: + break; + } + return 0; +} + +static void intreg_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + SCHEDState *s = opaque; + uint32_t saddr; + + saddr = (addr - PHYS_JJ_INTR0) >> 2; + switch (saddr) { + case 0: + s->intreg_pending = val; + break; + case 1: // clear + s->intreg_enabled &= ~val; + break; + case 2: // set + s->intreg_enabled |= val; + break; + default: + break; + } +} + +static CPUReadMemoryFunc *intreg_mem_read[3] = { + intreg_mem_readl, + intreg_mem_readl, + intreg_mem_readl, +}; + +static CPUWriteMemoryFunc *intreg_mem_write[3] = { + intreg_mem_writel, + intreg_mem_writel, + intreg_mem_writel, +}; + +static uint32_t intregm_mem_readl(void *opaque, target_phys_addr_t addr) +{ + SCHEDState *s = opaque; + uint32_t saddr; + + saddr = (addr - PHYS_JJ_INTR_G) >> 2; + switch (saddr) { + case 0: + return s->intregm_pending; + break; + case 1: + return s->intregm_enabled; + break; + default: + break; + } + return 0; +} + +static void intregm_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + SCHEDState *s = opaque; + uint32_t saddr; + + saddr = (addr - PHYS_JJ_INTR_G) >> 2; + switch (saddr) { + case 0: + s->intregm_pending = val; + break; + case 1: + s->intregm_enabled = val; + break; + case 2: // clear + s->intregm_enabled &= ~val; + break; + case 3: // set + s->intregm_enabled |= val; + break; + default: + break; + } +} + +static CPUReadMemoryFunc *intregm_mem_read[3] = { + intregm_mem_readl, + intregm_mem_readl, + intregm_mem_readl, +}; + +static CPUWriteMemoryFunc *intregm_mem_write[3] = { + intregm_mem_writel, + intregm_mem_writel, + intregm_mem_writel, +}; + +static uint32_t timer_mem_readl(void *opaque, target_phys_addr_t addr) +{ + SCHEDState *s = opaque; + uint32_t saddr; + + saddr = (addr - PHYS_JJ_CLOCK) >> 2; + switch (saddr) { + default: + return s->timer_regs[saddr]; + break; + } + return 0; +} + +static void timer_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + SCHEDState *s = opaque; + uint32_t saddr; + + saddr = (addr - PHYS_JJ_CLOCK) >> 2; + switch (saddr) { + default: + s->timer_regs[saddr] = val; + break; + } +} + +static CPUReadMemoryFunc *timer_mem_read[3] = { + timer_mem_readl, + timer_mem_readl, + timer_mem_readl, +}; + +static CPUWriteMemoryFunc *timer_mem_write[3] = { + timer_mem_writel, + timer_mem_writel, + timer_mem_writel, +}; + +static uint32_t timerm_mem_readl(void *opaque, target_phys_addr_t addr) +{ + SCHEDState *s = opaque; + uint32_t saddr; + + saddr = (addr - PHYS_JJ_CLOCK1) >> 2; + switch (saddr) { + default: + return s->timerm_regs[saddr]; + break; + } + return 0; +} + +static void timerm_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + SCHEDState *s = opaque; + uint32_t saddr; + + saddr = (addr - PHYS_JJ_CLOCK1) >> 2; + switch (saddr) { + default: + s->timerm_regs[saddr] = val; + break; + } +} + +static CPUReadMemoryFunc *timerm_mem_read[3] = { + timerm_mem_readl, + timerm_mem_readl, + timerm_mem_readl, +}; + +static CPUWriteMemoryFunc *timerm_mem_write[3] = { + timerm_mem_writel, + timerm_mem_writel, + timerm_mem_writel, +}; + +void pic_info() {} +void irq_info() {} + +static const unsigned int intr_to_mask[16] = { + 0, 0, 0, 0, 0, 0, SUN4M_INT_ETHERNET, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +void pic_set_irq(int irq, int level) +{ + if (irq < 16) { + unsigned int mask = intr_to_mask[irq]; + ps->intreg_pending |= 1 << irq; + if (ps->intregm_enabled & mask) { + cpu_single_env->interrupt_index = irq; + cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HARD); + } + } +} + +void sched_init() +{ + SCHEDState *s; + + s = qemu_mallocz(sizeof(SCHEDState)); + if (!s) + return; + + intreg_io_memory = cpu_register_io_memory(0, intreg_mem_read, intreg_mem_write, s); + cpu_register_physical_memory(PHYS_JJ_INTR0, 3, intreg_io_memory); + + intregm_io_memory = cpu_register_io_memory(0, intregm_mem_read, intregm_mem_write, s); + cpu_register_physical_memory(PHYS_JJ_INTR_G, 5, intregm_io_memory); + + timer_io_memory = cpu_register_io_memory(0, timer_mem_read, timer_mem_write, s); + cpu_register_physical_memory(PHYS_JJ_CLOCK, 2, timer_io_memory); + + timerm_io_memory = cpu_register_io_memory(0, timerm_mem_read, timerm_mem_write, s); + cpu_register_physical_memory(PHYS_JJ_CLOCK1, 2, timerm_io_memory); + + sched_reset(s); + ps = s; +} + diff --git a/hw/sun4m.c b/hw/sun4m.c new file mode 100644 index 0000000000..05dbd56a5f --- /dev/null +++ b/hw/sun4m.c @@ -0,0 +1,130 @@ +/* + * QEMU Sun4m System Emulator + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * 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 "vl.h" +#include "m48t08.h" + +#define KERNEL_LOAD_ADDR 0x00004000 +#define MMU_CONTEXT_TBL 0x00003000 +#define MMU_L1PTP (MMU_CONTEXT_TBL + 0x0400) +#define MMU_L2PTP (MMU_CONTEXT_TBL + 0x0800) +#define ROMVEC_DATA (MMU_CONTEXT_TBL + 0x1800) +#define PROM_ADDR 0xffd04000 +#define PROM_FILENAME "proll.bin" +#define PHYS_JJ_EEPROM 0x71200000 /* [2000] MK48T08 */ +#define PHYS_JJ_IDPROM_OFF 0x1FD8 +#define PHYS_JJ_EEPROM_SIZE 0x2000 + +/* TSC handling */ + +uint64_t cpu_get_tsc() +{ + return qemu_get_clock(vm_clock); +} + +void DMA_run() {} +void SB16_run() {} +void vga_invalidate_display() {} +void vga_screen_dump(const char *filename) {} +int serial_can_receive(SerialState *s) { return 0; } +void serial_receive_byte(SerialState *s, int ch) {} +void serial_receive_break(SerialState *s) {} + +static m48t08_t *nvram; + +/* Sun4m hardware initialisation */ +void sun4m_init(int ram_size, int vga_ram_size, int boot_device, + DisplayState *ds, const char **fd_filename, int snapshot, + const char *kernel_filename, const char *kernel_cmdline, + const char *initrd_filename) +{ + char buf[1024]; + int ret, linux_boot, bios_size; + unsigned long bios_offset; + + linux_boot = (kernel_filename != NULL); + + /* allocate RAM */ + cpu_register_physical_memory(0, ram_size, 0); + bios_offset = ram_size; + + iommu_init(); + sched_init(); + tcx_init(ds); + lance_init(&nd_table[0], 6); + nvram = m48t08_init(PHYS_JJ_EEPROM, PHYS_JJ_EEPROM_SIZE); + + magic_init(kernel_filename, phys_ram_base + KERNEL_LOAD_ADDR); + +#if 0 + snprintf(buf, sizeof(buf), "%s/%s", bios_dir, PROM_FILENAME); + bios_size = get_image_size(buf); + ret = load_image(buf, phys_ram_base + bios_offset); + if (ret != bios_size) { + fprintf(stderr, "qemu: could not load prom '%s'\n", buf); + exit(1); + } + cpu_register_physical_memory(PROM_ADDR, + bios_size, bios_offset | IO_MEM_ROM); +#endif + + /* We load Proll as the kernel and start it. It will issue a magic + IO to load the real kernel */ + if (linux_boot) { + snprintf(buf, sizeof(buf), "%s/%s", bios_dir, PROM_FILENAME); + ret = load_kernel(buf, + phys_ram_base + KERNEL_LOAD_ADDR); + if (ret < 0) { + fprintf(stderr, "qemu: could not load kernel '%s'\n", + buf); + exit(1); + } + } + /* Setup a MMU entry for entire address space */ + stl_raw(phys_ram_base + MMU_CONTEXT_TBL, (MMU_L1PTP >> 4) | 1); + stl_raw(phys_ram_base + MMU_L1PTP, (MMU_L2PTP >> 4) | 1); +#if 0 + stl_raw(phys_ram_base + MMU_L1PTP + (0x50 << 2), (MMU_L2PTP >> 4) | 1); // frame buffer at 50.. +#endif + stl_raw(phys_ram_base + MMU_L1PTP + (0xff << 2), (MMU_L2PTP >> 4) | 1); // ff.. == 00.. + /* 3 = U:RWX S:RWX */ + stl_raw(phys_ram_base + MMU_L2PTP, (3 << PTE_ACCESS_SHIFT) | 2); +#if 0 + stl_raw(phys_ram_base + MMU_L2PTP + 0x84, (PHYS_JJ_TCX_FB >> 4) \ + | (3 << PTE_ACCESS_SHIFT) | 2); // frame buf + stl_raw(phys_ram_base + MMU_L2PTP + 0x88, (PHYS_JJ_TCX_FB >> 4) \ + | (3 << PTE_ACCESS_SHIFT) | 2); // frame buf + stl_raw(phys_ram_base + MMU_L2PTP + 0x140, (PHYS_JJ_TCX_FB >> 4) \ + | (3 << PTE_ACCESS_SHIFT) | 2); // frame buf + // "Empirical constant" + stl_raw(phys_ram_base + ROMVEC_DATA, 0x10010407); + + // Version: V3 prom + stl_raw(phys_ram_base + ROMVEC_DATA + 4, 3); + + stl_raw(phys_ram_base + ROMVEC_DATA + 0x1c, ROMVEC_DATA+0x400); + stl_raw(phys_ram_base + ROMVEC_DATA + 0x400, ROMVEC_DATA+0x404); + stl_raw(phys_ram_base + ROMVEC_DATA + 0x404, 0x81c3e008); // retl + stl_raw(phys_ram_base + ROMVEC_DATA + 0x408, 0x01000000); // nop +#endif +} diff --git a/hw/tcx.c b/hw/tcx.c new file mode 100644 index 0000000000..d9b91c68ff --- /dev/null +++ b/hw/tcx.c @@ -0,0 +1,176 @@ +/* + * QEMU Sun4m System Emulator + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * 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 "vl.h" + +#define PHYS_JJ_TCX_FB 0x50800000 /* Start address, frame buffer body */ +#define PHYS_JJ_TCX_0E 0x5E000000 /* Top address, one byte used. */ + +#define MAXX 1024 +#define MAXY 768 +#define XSZ (8*80) +#define YSZ (24*11) +#define XOFF (MAXX-XSZ) +#define YOFF (MAXY-YSZ) + +#define DEBUG_VGA_MEM + +typedef struct TCXState { + uint8_t *vram_ptr; + unsigned long vram_offset; + unsigned int vram_size; + DisplayState *ds; +} TCXState; + +static TCXState *ts; + +static int tcx_io_memory; + +void vga_update_display() +{ + dpy_update(ts->ds, 0, 0, XSZ, YSZ); +} + +static uint32_t tcx_mem_readb(void *opaque, target_phys_addr_t addr) +{ + TCXState *s = opaque; + uint32_t saddr; + unsigned int x, y; + char *sptr; + + saddr = addr - PHYS_JJ_TCX_FB - YOFF*MAXX - XOFF; + y = saddr / MAXX; + x = saddr - y * MAXX; + if (x < MAXX && y < MAXY) { + sptr = s->ds->data; + if (sptr) + return sptr[y * s->ds->linesize + x*4]; + } + return 0; +} + +static uint32_t tcx_mem_readw(void *opaque, target_phys_addr_t addr) +{ + uint32_t v; +#ifdef TARGET_WORDS_BIGENDIAN + v = tcx_mem_readb(opaque, addr) << 8; + v |= tcx_mem_readb(opaque, addr + 1); +#else + v = tcx_mem_readb(opaque, addr); + v |= tcx_mem_readb(opaque, addr + 1) << 8; +#endif + return v; +} + +static uint32_t tcx_mem_readl(void *opaque, target_phys_addr_t addr) +{ + uint32_t v; +#ifdef TARGET_WORDS_BIGENDIAN + v = tcx_mem_readb(opaque, addr) << 24; + v |= tcx_mem_readb(opaque, addr + 1) << 16; + v |= tcx_mem_readb(opaque, addr + 2) << 8; + v |= tcx_mem_readb(opaque, addr + 3); +#else + v = tcx_mem_readb(opaque, addr); + v |= tcx_mem_readb(opaque, addr + 1) << 8; + v |= tcx_mem_readb(opaque, addr + 2) << 16; + v |= tcx_mem_readb(opaque, addr + 3) << 24; +#endif + return v; +} + +/* called for accesses between 0xa0000 and 0xc0000 */ +static void tcx_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + TCXState *s = opaque; + uint32_t saddr; + unsigned int x, y; + char *sptr; + + saddr = addr - PHYS_JJ_TCX_FB - YOFF*MAXX - XOFF; + y = saddr / MAXX; + x = saddr - y * MAXX; + if (x < MAXX && y < MAXY) { + sptr = s->ds->data; + if (sptr) { + sptr[y * s->ds->linesize + x*4] = val; + sptr[y * s->ds->linesize + x*4+1] = val; + sptr[y * s->ds->linesize + x*4+2] = val; + cpu_physical_memory_set_dirty(addr); + } + } +} + +static void tcx_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) +{ +#ifdef TARGET_WORDS_BIGENDIAN + tcx_mem_writeb(opaque, addr, (val >> 8) & 0xff); + tcx_mem_writeb(opaque, addr + 1, val & 0xff); +#else + tcx_mem_writeb(opaque, addr, val & 0xff); + tcx_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff); +#endif +} + +static void tcx_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) +{ +#ifdef TARGET_WORDS_BIGENDIAN + tcx_mem_writeb(opaque, addr, (val >> 24) & 0xff); + tcx_mem_writeb(opaque, addr + 1, (val >> 16) & 0xff); + tcx_mem_writeb(opaque, addr + 2, (val >> 8) & 0xff); + tcx_mem_writeb(opaque, addr + 3, val & 0xff); +#else + tcx_mem_writeb(opaque, addr, val & 0xff); + tcx_mem_writeb(opaque, addr + 1, (val >> 8) & 0xff); + tcx_mem_writeb(opaque, addr + 2, (val >> 16) & 0xff); + tcx_mem_writeb(opaque, addr + 3, (val >> 24) & 0xff); +#endif +} + +static CPUReadMemoryFunc *tcx_mem_read[3] = { + tcx_mem_readb, + tcx_mem_readw, + tcx_mem_readl, +}; + +static CPUWriteMemoryFunc *tcx_mem_write[3] = { + tcx_mem_writeb, + tcx_mem_writew, + tcx_mem_writel, +}; + +void tcx_init(DisplayState *ds) +{ + TCXState *s; + + s = qemu_mallocz(sizeof(TCXState)); + if (!s) + return; + s->ds = ds; + ts = s; + tcx_io_memory = cpu_register_io_memory(0, tcx_mem_read, tcx_mem_write, s); + cpu_register_physical_memory(PHYS_JJ_TCX_FB, 0x100000, + tcx_io_memory); + dpy_resize(s->ds, XSZ, YSZ); +} +