full system SPARC emulation (Blue Swirl)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1085 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
6d5e216de9
commit
420557e898
221
hw/iommu.c
Normal file
221
hw/iommu.c
Normal file
@ -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;
|
||||
}
|
||||
|
472
hw/lance.c
Normal file
472
hw/lance.c
Normal file
@ -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);
|
||||
}
|
||||
|
391
hw/m48t08.c
Normal file
391
hw/m48t08.c
Normal file
@ -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
|
12
hw/m48t08.h
Normal file
12
hw/m48t08.h
Normal file
@ -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__) */
|
341
hw/magic-load.c
Normal file
341
hw/magic-load.c
Normal file
@ -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; i<elf_ex.e_phnum; i++, elf_ppnt++) {
|
||||
bswap_phdr(elf_ppnt);
|
||||
}
|
||||
#endif
|
||||
elf_ppnt = elf_phdata;
|
||||
|
||||
/* Now we do a little grungy work by mmaping the ELF image into
|
||||
* the correct location in memory. At this point, we assume that
|
||||
* the image should be loaded at fixed address, not at a variable
|
||||
* address.
|
||||
*/
|
||||
|
||||
for(i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) {
|
||||
unsigned long error, offset, len;
|
||||
|
||||
if (elf_ppnt->p_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);
|
||||
}
|
||||
|
346
hw/sched.c
Normal file
346
hw/sched.c
Normal file
@ -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;
|
||||
}
|
||||
|
130
hw/sun4m.c
Normal file
130
hw/sun4m.c
Normal file
@ -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
|
||||
}
|
176
hw/tcx.c
Normal file
176
hw/tcx.c
Normal file
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user