Initial support for Sun4d machines (SS-1000, SS-2000)

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3869 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
blueswir1 2007-12-28 20:57:43 +00:00
parent 8543e2cfce
commit 7d85892b9b
8 changed files with 455 additions and 16 deletions

View File

@ -486,7 +486,7 @@ VL_OBJS+= cirrus_vga.o parallel.o ptimer.o
else
VL_OBJS+= sun4m.o tcx.o pcnet.o iommu.o m48t59.o slavio_intctl.o
VL_OBJS+= slavio_timer.o slavio_serial.o slavio_misc.o fdc.o esp.o sparc32_dma.o
VL_OBJS+= cs4231.o ptimer.o eccmemctl.o
VL_OBJS+= cs4231.o ptimer.o eccmemctl.o sbi.o
endif
endif
ifeq ($(TARGET_BASE_ARCH), arm)

View File

@ -53,6 +53,7 @@ extern QEMUMachine r2d_machine;
/* sun4m.c */
extern QEMUMachine ss5_machine, ss10_machine, ss600mp_machine, ss20_machine;
extern QEMUMachine ss1000_machine, ss2000_machine;
/* sun4u.c */
extern QEMUMachine sun4u_machine;

167
hw/sbi.c Normal file
View File

@ -0,0 +1,167 @@
/*
* QEMU Sparc SBI interrupt controller emulation
*
* Based on slavio_intctl, copyright (c) 2003-2005 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 "hw.h"
#include "sun4m.h"
#include "console.h"
//#define DEBUG_IRQ
#ifdef DEBUG_IRQ
#define DPRINTF(fmt, args...) \
do { printf("IRQ: " fmt , ##args); } while (0)
#else
#define DPRINTF(fmt, args...)
#endif
#define MAX_CPUS 16
#define SBI_NREGS 16
typedef struct SBIState {
uint32_t regs[SBI_NREGS];
uint32_t intreg_pending[MAX_CPUS];
qemu_irq *cpu_irqs[MAX_CPUS];
uint32_t pil_out[MAX_CPUS];
} SBIState;
#define SBI_SIZE (SBI_NREGS * 4)
#define SBI_MASK (SBI_SIZE - 1)
static void sbi_check_interrupts(void *opaque)
{
}
static void sbi_set_irq(void *opaque, int irq, int level)
{
}
static void sbi_set_timer_irq_cpu(void *opaque, int cpu, int level)
{
}
static uint32_t sbi_mem_readl(void *opaque, target_phys_addr_t addr)
{
SBIState *s = opaque;
uint32_t saddr, ret;
saddr = (addr & SBI_MASK) >> 2;
switch (saddr) {
default:
ret = s->regs[saddr];
break;
}
DPRINTF("read system reg 0x" TARGET_FMT_plx " = %x\n", addr, ret);
return ret;
}
static void sbi_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
{
SBIState *s = opaque;
uint32_t saddr;
saddr = (addr & SBI_MASK) >> 2;
DPRINTF("write system reg 0x" TARGET_FMT_plx " = %x\n", addr, val);
switch (saddr) {
default:
s->regs[saddr] = val;
break;
}
}
static CPUReadMemoryFunc *sbi_mem_read[3] = {
sbi_mem_readl,
sbi_mem_readl,
sbi_mem_readl,
};
static CPUWriteMemoryFunc *sbi_mem_write[3] = {
sbi_mem_writel,
sbi_mem_writel,
sbi_mem_writel,
};
static void sbi_save(QEMUFile *f, void *opaque)
{
SBIState *s = opaque;
unsigned int i;
for (i = 0; i < MAX_CPUS; i++) {
qemu_put_be32s(f, &s->intreg_pending[i]);
}
}
static int sbi_load(QEMUFile *f, void *opaque, int version_id)
{
SBIState *s = opaque;
unsigned int i;
if (version_id != 1)
return -EINVAL;
for (i = 0; i < MAX_CPUS; i++) {
qemu_get_be32s(f, &s->intreg_pending[i]);
}
sbi_check_interrupts(s);
return 0;
}
static void sbi_reset(void *opaque)
{
SBIState *s = opaque;
unsigned int i;
for (i = 0; i < MAX_CPUS; i++) {
s->intreg_pending[i] = 0;
}
sbi_check_interrupts(s);
}
void *sbi_init(target_phys_addr_t addr, qemu_irq **irq, qemu_irq **cpu_irq,
qemu_irq **parent_irq)
{
unsigned int i;
int sbi_io_memory;
SBIState *s;
s = qemu_mallocz(sizeof(SBIState));
if (!s)
return NULL;
for (i = 0; i < MAX_CPUS; i++) {
s->cpu_irqs[i] = parent_irq[i];
}
sbi_io_memory = cpu_register_io_memory(0, sbi_mem_read, sbi_mem_write, s);
cpu_register_physical_memory(addr, SBI_SIZE, sbi_io_memory);
register_savevm("sbi", addr, 1, sbi_save, sbi_load, s);
qemu_register_reset(sbi_reset, s);
*irq = qemu_allocate_irqs(sbi_set_irq, s, 32);
*cpu_irq = qemu_allocate_irqs(sbi_set_timer_irq_cpu, s, MAX_CPUS);
sbi_reset(s);
return s;
}

View File

@ -1,5 +1,5 @@
/*
* QEMU Sun4m System Emulator
* QEMU Sun4m & Sun4d System Emulator
*
* Copyright (c) 2003-2005 Fabrice Bellard
*
@ -46,6 +46,11 @@
* SPARCstation 20/xx, SPARCserver 20
* SPARCstation 4
*
* Sun4d architecture was used in the following machines:
*
* SPARCcenter 2000
* SPARCserver 1000
*
* See for example: http://www.sunhelp.org/faq/sunref1.html
*/
@ -86,6 +91,26 @@ struct hwdef {
const char * const default_cpu_model;
};
#define MAX_IOUNITS 5
struct sun4d_hwdef {
target_phys_addr_t iounit_bases[MAX_IOUNITS], slavio_base;
target_phys_addr_t counter_base, nvram_base, ms_kb_base;
target_phys_addr_t serial_base;
target_phys_addr_t espdma_base, esp_base;
target_phys_addr_t ledma_base, le_base;
target_phys_addr_t tcx_base;
target_phys_addr_t sbi_base;
unsigned long vram_size, nvram_size;
// IRQ numbers are not PIL ones, but SBI register bit numbers
int esp_irq, le_irq, clock_irq, clock1_irq;
int ser_irq, ms_kb_irq, me_irq;
int machine_id; // For NVRAM
uint32_t iounit_version;
uint64_t max_mem;
const char * const default_cpu_model;
};
/* TSC handling */
uint64_t cpu_get_tsc()
@ -122,7 +147,7 @@ static void nvram_init(m48t59_t *nvram, uint8_t *macaddr, const char *cmdline,
const char *boot_devices, uint32_t RAM_size,
uint32_t kernel_size,
int width, int height, int depth,
int machine_id)
int machine_id, const char *arch)
{
unsigned int i;
uint32_t start, end;
@ -140,7 +165,7 @@ static void nvram_init(m48t59_t *nvram, uint8_t *macaddr, const char *cmdline,
header->nvram_size = cpu_to_be16(0x2000);
header->nvram_arch_ptr = cpu_to_be16(sizeof(ohwcfg_v3_t));
header->nvram_arch_size = cpu_to_be16(sizeof(struct sparc_arch_cfg));
strcpy(header->arch, "sun4m");
strcpy(header->arch, arch);
header->nb_cpus = smp_cpus & 0xff;
header->RAM0_base = 0;
header->RAM0_size = cpu_to_be64((uint64_t)RAM_size);
@ -203,12 +228,14 @@ static void *slavio_intctl;
void pic_info()
{
slavio_pic_info(slavio_intctl);
if (slavio_intctl)
slavio_pic_info(slavio_intctl);
}
void irq_info()
{
slavio_irq_info(slavio_intctl);
if (slavio_intctl)
slavio_irq_info(slavio_intctl);
}
void cpu_check_irqs(CPUState *env)
@ -488,7 +515,7 @@ static void sun4m_hw_init(const struct hwdef *hwdef, int RAM_size,
nvram_init(nvram, (uint8_t *)&nd_table[0].macaddr, kernel_cmdline,
boot_device, RAM_size, kernel_size, graphic_width,
graphic_height, graphic_depth, hwdef->machine_id);
graphic_height, graphic_depth, hwdef->machine_id, "Sun4m");
if (hwdef->ecc_base != (target_phys_addr_t)-1)
ecc_init(hwdef->ecc_base, hwdef->ecc_version);
@ -716,3 +743,242 @@ QEMUMachine ss20_machine = {
ss20_init,
};
static const struct sun4d_hwdef sun4d_hwdefs[] = {
/* SS-1000 */
{
.iounit_bases = {
0xfe0200000ULL,
0xfe1200000ULL,
0xfe2200000ULL,
0xfe3200000ULL,
-1,
},
.tcx_base = 0x820000000ULL,
.slavio_base = 0xf00000000ULL,
.ms_kb_base = 0xf00240000ULL,
.serial_base = 0xf00200000ULL,
.nvram_base = 0xf00280000ULL,
.counter_base = 0xf00300000ULL,
.espdma_base = 0x800081000ULL,
.esp_base = 0x800080000ULL,
.ledma_base = 0x800040000ULL,
.le_base = 0x800060000ULL,
.sbi_base = 0xf02800000ULL,
.vram_size = 0x00100000,
.nvram_size = 0x2000,
.esp_irq = 3,
.le_irq = 4,
.clock_irq = 14,
.clock1_irq = 10,
.ms_kb_irq = 12,
.ser_irq = 12,
.machine_id = 0x80,
.iounit_version = 0x03000000,
.max_mem = 0xffffffff, // XXX actually first 62GB ok
.default_cpu_model = "TI SuperSparc II",
},
/* SS-2000 */
{
.iounit_bases = {
0xfe0200000ULL,
0xfe1200000ULL,
0xfe2200000ULL,
0xfe3200000ULL,
0xfe4200000ULL,
},
.tcx_base = 0x820000000ULL,
.slavio_base = 0xf00000000ULL,
.ms_kb_base = 0xf00240000ULL,
.serial_base = 0xf00200000ULL,
.nvram_base = 0xf00280000ULL,
.counter_base = 0xf00300000ULL,
.espdma_base = 0x800081000ULL,
.esp_base = 0x800080000ULL,
.ledma_base = 0x800040000ULL,
.le_base = 0x800060000ULL,
.sbi_base = 0xf02800000ULL,
.vram_size = 0x00100000,
.nvram_size = 0x2000,
.esp_irq = 3,
.le_irq = 4,
.clock_irq = 14,
.clock1_irq = 10,
.ms_kb_irq = 12,
.ser_irq = 12,
.machine_id = 0x80,
.iounit_version = 0x03000000,
.max_mem = 0xffffffff, // XXX actually first 62GB ok
.default_cpu_model = "TI SuperSparc II",
},
};
static void sun4d_hw_init(const struct sun4d_hwdef *hwdef, int RAM_size,
const char *boot_device,
DisplayState *ds, const char *kernel_filename,
const char *kernel_cmdline,
const char *initrd_filename, const char *cpu_model)
{
CPUState *env, *envs[MAX_CPUS];
unsigned int i;
void *iounits[MAX_IOUNITS], *espdma, *ledma, *main_esp, *nvram, *sbi;
qemu_irq *cpu_irqs[MAX_CPUS], *sbi_irq, *sbi_cpu_irq,
*espdma_irq, *ledma_irq;
qemu_irq *esp_reset, *le_reset;
unsigned long prom_offset, kernel_size;
int ret;
char buf[1024];
int index;
/* init CPUs */
if (!cpu_model)
cpu_model = hwdef->default_cpu_model;
for (i = 0; i < smp_cpus; i++) {
env = cpu_init(cpu_model);
if (!env) {
fprintf(stderr, "Unable to find Sparc CPU definition\n");
exit(1);
}
cpu_sparc_set_id(env, i);
envs[i] = env;
if (i == 0) {
qemu_register_reset(main_cpu_reset, env);
} else {
qemu_register_reset(secondary_cpu_reset, env);
env->halted = 1;
}
register_savevm("cpu", i, 3, cpu_save, cpu_load, env);
cpu_irqs[i] = qemu_allocate_irqs(cpu_set_irq, envs[i], MAX_PILS);
env->prom_addr = hwdef->slavio_base;
}
for (i = smp_cpus; i < MAX_CPUS; i++)
cpu_irqs[i] = qemu_allocate_irqs(dummy_cpu_set_irq, NULL, MAX_PILS);
/* allocate RAM */
if ((uint64_t)RAM_size > hwdef->max_mem) {
fprintf(stderr, "qemu: Too much memory for this machine: %d, maximum %d\n",
(unsigned int)RAM_size / (1024 * 1024),
(unsigned int)(hwdef->max_mem / (1024 * 1024)));
exit(1);
}
cpu_register_physical_memory(0, RAM_size, 0);
/* load boot prom */
prom_offset = RAM_size + hwdef->vram_size;
cpu_register_physical_memory(hwdef->slavio_base,
(PROM_SIZE_MAX + TARGET_PAGE_SIZE - 1) &
TARGET_PAGE_MASK,
prom_offset | IO_MEM_ROM);
if (bios_name == NULL)
bios_name = PROM_FILENAME;
snprintf(buf, sizeof(buf), "%s/%s", bios_dir, bios_name);
ret = load_elf(buf, hwdef->slavio_base - PROM_VADDR, NULL, NULL, NULL);
if (ret < 0 || ret > PROM_SIZE_MAX)
ret = load_image(buf, phys_ram_base + prom_offset);
if (ret < 0 || ret > PROM_SIZE_MAX) {
fprintf(stderr, "qemu: could not load prom '%s'\n",
buf);
exit(1);
}
/* set up devices */
sbi = sbi_init(hwdef->sbi_base, &sbi_irq, &sbi_cpu_irq, cpu_irqs);
for (i = 0; i < MAX_IOUNITS; i++)
if (hwdef->iounit_bases[i] != (target_phys_addr_t)-1)
iounits[i] = iommu_init(hwdef->iounit_bases[i], hwdef->iounit_version);
espdma = sparc32_dma_init(hwdef->espdma_base, sbi_irq[hwdef->esp_irq],
iounits[0], &espdma_irq, &esp_reset);
ledma = sparc32_dma_init(hwdef->ledma_base, sbi_irq[hwdef->le_irq],
iounits[0], &ledma_irq, &le_reset);
if (graphic_depth != 8 && graphic_depth != 24) {
fprintf(stderr, "qemu: Unsupported depth: %d\n", graphic_depth);
exit (1);
}
tcx_init(ds, hwdef->tcx_base, phys_ram_base + RAM_size, RAM_size,
hwdef->vram_size, graphic_width, graphic_height, graphic_depth);
if (nd_table[0].model == NULL
|| strcmp(nd_table[0].model, "lance") == 0) {
lance_init(&nd_table[0], hwdef->le_base, ledma, *ledma_irq, le_reset);
} else if (strcmp(nd_table[0].model, "?") == 0) {
fprintf(stderr, "qemu: Supported NICs: lance\n");
exit (1);
} else {
fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd_table[0].model);
exit (1);
}
nvram = m48t59_init(sbi_irq[0], hwdef->nvram_base, 0,
hwdef->nvram_size, 8);
slavio_timer_init_all(hwdef->counter_base, sbi_irq[hwdef->clock1_irq],
sbi_cpu_irq, smp_cpus);
slavio_serial_ms_kbd_init(hwdef->ms_kb_base, sbi_irq[hwdef->ms_kb_irq],
nographic);
// Slavio TTYA (base+4, Linux ttyS0) is the first Qemu serial device
// Slavio TTYB (base+0, Linux ttyS1) is the second Qemu serial device
slavio_serial_init(hwdef->serial_base, sbi_irq[hwdef->ser_irq],
serial_hds[1], serial_hds[0]);
if (drive_get_max_bus(IF_SCSI) > 0) {
fprintf(stderr, "qemu: too many SCSI bus\n");
exit(1);
}
main_esp = esp_init(hwdef->esp_base, espdma, *espdma_irq,
esp_reset);
for (i = 0; i < ESP_MAX_DEVS; i++) {
index = drive_get_index(IF_SCSI, 0, i);
if (index == -1)
continue;
esp_scsi_attach(main_esp, drives_table[index].bdrv, i);
}
kernel_size = sun4m_load_kernel(kernel_filename, kernel_cmdline,
initrd_filename);
nvram_init(nvram, (uint8_t *)&nd_table[0].macaddr, kernel_cmdline,
boot_device, RAM_size, kernel_size, graphic_width,
graphic_height, graphic_depth, hwdef->machine_id, "Sun4d");
}
/* SPARCserver 1000 hardware initialisation */
static void ss1000_init(int RAM_size, int vga_ram_size,
const char *boot_device, DisplayState *ds,
const char *kernel_filename, const char *kernel_cmdline,
const char *initrd_filename, const char *cpu_model)
{
sun4d_hw_init(&sun4d_hwdefs[0], RAM_size, boot_device, ds, kernel_filename,
kernel_cmdline, initrd_filename, cpu_model);
}
/* SPARCcenter 2000 hardware initialisation */
static void ss2000_init(int RAM_size, int vga_ram_size,
const char *boot_device, DisplayState *ds,
const char *kernel_filename, const char *kernel_cmdline,
const char *initrd_filename, const char *cpu_model)
{
sun4d_hw_init(&sun4d_hwdefs[1], RAM_size, boot_device, ds, kernel_filename,
kernel_cmdline, initrd_filename, cpu_model);
}
QEMUMachine ss1000_machine = {
"SS-1000",
"Sun4d platform, SPARCserver 1000",
ss1000_init,
};
QEMUMachine ss2000_machine = {
"SS-2000",
"Sun4d platform, SPARCcenter 2000",
ss2000_init,
};

View File

@ -34,6 +34,10 @@ void *slavio_intctl_init(target_phys_addr_t addr, target_phys_addr_t addrg,
void slavio_pic_info(void *opaque);
void slavio_irq_info(void *opaque);
/* sbi.c */
void *sbi_init(target_phys_addr_t addr, qemu_irq **irq, qemu_irq **cpu_irq,
qemu_irq **parent_irq);
/* slavio_timer.c */
void slavio_timer_init_all(target_phys_addr_t base, qemu_irq master_irq,
qemu_irq *cpu_irqs, unsigned int num_cpus);

View File

@ -74,7 +74,7 @@ For system emulation, the following hardware targets are supported:
@item PREP (PowerPC processor)
@item G3 BW PowerMac (PowerPC processor)
@item Mac99 PowerMac (PowerPC processor, in progress)
@item Sun4m (32-bit Sparc processor)
@item Sun4m/Sun4d (32-bit Sparc processor)
@item Sun4u (64-bit Sparc processor, in progress)
@item Malta board (32-bit and 64-bit MIPS processors)
@item ARM Integrator/CP (ARM)
@ -2026,15 +2026,16 @@ More information is available at
@section Sparc32 System emulator
Use the executable @file{qemu-system-sparc} to simulate a SPARCstation
5, SPARCstation 10, or SPARCserver 600MP (sun4m architecture). The
5, SPARCstation 10, SPARCstation 20, SPARCserver 600MP (sun4m architecture),
SPARCserver 1000, or SPARCcenter 2000 (sun4d architecture). The
emulation is somewhat complete. SMP up to 16 CPUs is supported, but
Linux limits the number of usable CPUs to 4.
QEMU emulates the following sun4m peripherals:
QEMU emulates the following sun4m/sun4d peripherals:
@itemize @minus
@item
IOMMU
IOMMU or IO-UNITs
@item
TCX Frame buffer
@item
@ -2054,7 +2055,7 @@ CS4231 sound device (only on SS-5, not working yet)
The number of peripherals is fixed in the architecture. Maximum
memory size depends on the machine type, for SS-5 it is 256MB and for
SS-10 and SS-600MP 2047MB.
others 2047MB.
Since version 0.8.2, QEMU uses OpenBIOS
@url{http://www.openbios.org/}. OpenBIOS is a free (GPL v2) portable
@ -2085,7 +2086,7 @@ qemu-system-sparc -prom-env 'auto-boot?=false' \
-prom-env 'boot-device=sd(0,2,0):d' -prom-env 'boot-args=linux single'
@end example
@item -M [SS-5|SS-10|SS-600MP]
@item -M [SS-5|SS-10|SS-20|SS-600MP|SS-1000|SS-2000]
Set the emulated machine type. Default is SS-5.

View File

@ -418,8 +418,7 @@ void helper_ld_asi(int asi, int size, int sign)
break;
}
break;
case 0x2e: /* MMU passthrough, 0xexxxxxxxx */
case 0x2f: /* MMU passthrough, 0xfxxxxxxxx */
case 0x21 ... 0x2f: /* MMU passthrough, 0x100000000 to 0xfffffffff */
switch(size) {
case 1:
ret = ldub_phys((target_phys_addr_t)T0
@ -445,7 +444,6 @@ void helper_ld_asi(int asi, int size, int sign)
case 0x39: /* data cache diagnostic register */
ret = 0;
break;
case 0x21 ... 0x2d: /* MMU passthrough, unassigned */
default:
do_unassigned_access(T0, 0, 0, asi);
ret = 0;

2
vl.c
View File

@ -7892,6 +7892,8 @@ static void register_machines(void)
qemu_register_machine(&ss10_machine);
qemu_register_machine(&ss600mp_machine);
qemu_register_machine(&ss20_machine);
qemu_register_machine(&ss1000_machine);
qemu_register_machine(&ss2000_machine);
#endif
#elif defined(TARGET_ARM)
qemu_register_machine(&integratorcp_machine);