Index: qemu/hw/sun4m.c =================================================================== --- qemu.orig/hw/sun4m.c 2007-08-22 20:03:47.000000000 +0000 +++ qemu/hw/sun4m.c 2007-08-23 17:30:22.000000000 +0000 @@ -1,7 +1,7 @@ /* - * QEMU Sun4m System Emulator + * QEMU Sun4m/Sun4c System Emulator * - * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2003-2005, 2007 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 @@ -36,6 +36,13 @@ * SPARCstation 20/xx, SPARCserver 20 * SPARCstation 4 * + * Sun4c architecture was used in the following machines: + * SPARCstation 1/1+, SPARCserver 1/1+ + * SPARCstation SLC + * SPARCstation IPC + * SPARCstation ELC + * SPARCstation IPX + * * See for example: http://www.sunhelp.org/faq/sunref1.html */ @@ -62,6 +69,7 @@ target_phys_addr_t serial_base, fd_base; target_phys_addr_t dma_base, esp_base, le_base; target_phys_addr_t tcx_base, cs_base, power_base; + target_phys_addr_t sun4c_intctl_base, sun4c_counter_base; long vram_size, nvram_size; // IRQ numbers are not PIL ones, but master interrupt controller register // bit numbers @@ -286,6 +294,12 @@ cpu_physical_memory_rw(addr, buf, len, is_write); } +static void sun4c_dma_memory_rw(void *opaque, target_phys_addr_t addr, + uint8_t *buf, int len, int is_write) +{ + cpu_memory_rw_debug(opaque, addr, buf, len, is_write); +} + static void sparc_dummy_memory_rw(void *opaque, target_phys_addr_t addr, uint8_t *buf, int len, int is_write) { @@ -418,6 +432,93 @@ return nvram; } +static void *sun4c_hw_init(const struct hwdef *hwdef, int RAM_size, + DisplayState *ds, const char *cpu_model) + +{ + CPUState *env; + unsigned int i; + void *espdma, *ledma, *main_esp, *nvram; + const sparc_def_t *def; + qemu_irq *cpu_irqs, *slavio_irq, *espdma_irq, *ledma_irq; + qemu_irq *esp_reset, *le_reset; + qemu_dma *physical_dma, *dummy_dma, *mmu_dma, *esp_dvma, *le_dvma; + + /* init CPU */ + sparc_find_by_name(cpu_model, &def); + if (def == NULL) { + fprintf(stderr, "Unable to find Sparc CPU definition\n"); + exit(1); + } + + env = cpu_init(); + cpu_sparc_register(env, def); + qemu_register_reset(main_cpu_reset, env); + register_savevm("cpu", 0, 3, cpu_save, cpu_load, env); + cpu_irqs = qemu_allocate_irqs(cpu_set_irq, env, MAX_PILS); + + /* allocate RAM */ + cpu_register_physical_memory(0, RAM_size, 0); + + physical_dma = qemu_init_dma(sparc_dma_memory_rw, NULL); + dummy_dma = qemu_init_dma(sparc_dummy_memory_rw, NULL); + mmu_dma = qemu_init_dma(sun4c_dma_memory_rw, env); + + slavio_intctl = sun4c_intctl_init(hwdef->sun4c_intctl_base, + &slavio_irq, cpu_irqs); + + espdma = sparc32_dma_init(hwdef->dma_base, slavio_irq[hwdef->esp_irq], + &espdma_irq, mmu_dma, &esp_dvma, 1, &esp_reset); + + ledma = sparc32_dma_init(hwdef->dma_base + 16ULL, + slavio_irq[hwdef->le_irq], &ledma_irq, mmu_dma, + &le_dvma, 0, &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_irq, le_dvma, 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(slavio_irq[0], hwdef->nvram_base, 0, + hwdef->nvram_size, 8); + + slavio_timer_init(hwdef->sun4c_counter_base, + slavio_irq[hwdef->clock_irq], 2); + + slavio_serial_ms_kbd_init(hwdef->ms_kb_base, slavio_irq[hwdef->ms_kb_irq]); + // 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, slavio_irq[hwdef->ser_irq], + serial_hds[1], serial_hds[0]); + fdctrl_init(slavio_irq[hwdef->fd_irq], 0, 1, hwdef->fd_base, fd_table, + dummy_dma); + + main_esp = esp_init(bs_table, hwdef->esp_base, *espdma_irq, esp_dvma, + esp_reset); + + for (i = 0; i < MAX_DISKS; i++) { + if (bs_table[i]) { + esp_scsi_attach(main_esp, bs_table[i], i); + } + } + + return nvram; +} + static void sun4m_load_kernel(long vram_size, int RAM_size, int boot_device, const char *kernel_filename, const char *kernel_cmdline, @@ -501,6 +602,8 @@ .esp_base = 0x78800000, .le_base = 0x78c00000, .power_base = 0x7a000000, + .sun4c_intctl_base = -1, + .sun4c_counter_base = -1, .vram_size = 0x00100000, .nvram_size = 0x2000, .esp_irq = 18, @@ -534,6 +637,8 @@ .esp_base = 0xef0800000ULL, .le_base = 0xef0c00000ULL, .power_base = 0xefa000000ULL, + .sun4c_intctl_base = -1, + .sun4c_counter_base = -1, .vram_size = 0x00100000, .nvram_size = 0x2000, .esp_irq = 18, @@ -551,6 +656,37 @@ 6, 0, 4, 10, 8, 0, 11, 0, 0, 0, 0, 0, 15, 0, 15, 0, }, }, + /* SS-2 */ + { + .iommu_base = 0xf8000000, + .tcx_base = 0xfe000000, + .cs_base = -1, + .slavio_base = 0xf0000000, + .ms_kb_base = 0xf0000000, + .serial_base = 0xf1000000, + .nvram_base = 0xf2000000, + .fd_base = 0xf7200000, + .counter_base = -1, + .intctl_base = -1, + .dma_base = 0xf8400000, + .esp_base = 0xf8800000, + .le_base = 0xf8c00000, + .power_base = -1, + .sun4c_intctl_base = 0xf5000000, + .sun4c_counter_base = 0xf3000000, + .vram_size = 0x00100000, + .nvram_size = 0x2000, // XXX 0x800, + .esp_irq = 2, + .le_irq = 3, + .clock_irq = 5, + .clock1_irq = 7, + .ms_kb_irq = 1, + .ser_irq = 1, + .fd_irq = 1, + .me_irq = 1, + .cs_irq = -1, + .machine_id = 0x55, + }, }; static void sun4m_common_init(int RAM_size, int boot_device, DisplayState *ds, @@ -599,6 +735,32 @@ 1, PROM_ADDR); // XXX prom overlap, actually first 4GB ok } +/* SPARCstation 2 hardware initialisation */ +static void ss2_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, const char *cpu_model) +{ + void *nvram; + const unsigned int max_ram = 0x10000000; + const int machine = 2; + + if (cpu_model == NULL) + cpu_model = "Cypress CY7C601"; + + if ((unsigned int)RAM_size > max_ram) { + fprintf(stderr, "qemu: Too much memory for this machine: %d, maximum %d\n", + (unsigned int)RAM_size / (1024 * 1024), + (unsigned int)max_ram / (1024 * 1024)); + exit(1); + } + nvram = sun4c_hw_init(&hwdefs[machine], RAM_size, ds, cpu_model); + + sun4m_load_kernel(hwdefs[machine].vram_size, RAM_size, boot_device, + kernel_filename, kernel_cmdline, initrd_filename, + hwdefs[machine].machine_id, nvram); +} + QEMUMachine ss5_machine = { "SS-5", "Sun4m platform, SPARCstation 5", @@ -610,3 +772,9 @@ "Sun4m platform, SPARCstation 10", ss10_init, }; + +QEMUMachine ss2_machine = { + "SS-2", + "Sun4c platform, SPARCstation 2", + ss2_init, +}; Index: qemu/target-sparc/translate.c =================================================================== --- qemu.orig/target-sparc/translate.c 2007-08-22 19:56:37.000000000 +0000 +++ qemu/target-sparc/translate.c 2007-08-22 20:03:55.000000000 +0000 @@ -3456,6 +3456,12 @@ }, #else { + .name = "Cypress CY7C601", + .iu_version = 0x11 << 24, /* Impl 1, ver 1 */ + .fpu_version = 3 << 17, /* FPU version 3 (Weitek WTL3170/2) */ + .mmu_version = 0, + }, + { .name = "Fujitsu MB86904", .iu_version = 0x04 << 24, /* Impl 0, ver 4 */ .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */ Index: qemu/vl.c =================================================================== --- qemu.orig/vl.c 2007-08-22 19:56:37.000000000 +0000 +++ qemu/vl.c 2007-08-22 20:03:55.000000000 +0000 @@ -7127,6 +7127,7 @@ #else qemu_register_machine(&ss5_machine); qemu_register_machine(&ss10_machine); + qemu_register_machine(&ss2_machine); #endif #elif defined(TARGET_ARM) qemu_register_machine(&integratorcp_machine); Index: qemu/vl.h =================================================================== --- qemu.orig/vl.h 2007-08-22 20:03:47.000000000 +0000 +++ qemu/vl.h 2007-08-22 20:03:55.000000000 +0000 @@ -1260,7 +1260,7 @@ void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val); /* sun4m.c */ -extern QEMUMachine ss5_machine, ss10_machine; +extern QEMUMachine ss5_machine, ss10_machine, ss2_machine; /* iommu.c */ void *iommu_init(target_phys_addr_t addr, qemu_dma *parent_dma, @@ -1279,6 +1279,10 @@ void slavio_pic_info(void *opaque); void slavio_irq_info(void *opaque); +/* sun4c_intctl.c */ +void *sun4c_intctl_init(target_phys_addr_t addr, qemu_irq **irq, + qemu_irq *parent_irq); + /* loader.c */ int get_image_size(const char *filename); int load_image(const char *filename, uint8_t *addr); Index: qemu/hw/sun4c_intctl.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ qemu/hw/sun4c_intctl.c 2007-08-22 20:03:55.000000000 +0000 @@ -0,0 +1,221 @@ +/* + * QEMU Sparc Sun4c interrupt controller emulation + * + * Copyright (c) 2007 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 DEBUG_IRQ_COUNT +//#define DEBUG_IRQ + +#ifdef DEBUG_IRQ +#define DPRINTF(fmt, args...) \ +do { printf("IRQ: " fmt , ##args); } while (0) +#else +#define DPRINTF(fmt, args...) +#endif + +/* + * Registers of interrupt controller in sun4c. + * + */ + +#define MAX_PILS 16 + +typedef struct Sun4c_INTCTLState { +#ifdef DEBUG_IRQ_COUNT + uint64_t irq_count; +#endif + qemu_irq *cpu_irqs; + const uint32_t *intbit_to_level; + uint32_t pil_out; + uint8_t reg; + uint8_t pending; +} Sun4c_INTCTLState; + +#define INTCTL_MAXADDR 0 +#define INTCTL_SIZE (INTCTL_MAXADDR + 1) + +static void sun4c_check_interrupts(void *opaque); + +static uint32_t sun4c_intctl_mem_readb(void *opaque, target_phys_addr_t addr) +{ + Sun4c_INTCTLState *s = opaque; + uint32_t ret; + + ret = s->reg; + DPRINTF("read reg 0x" TARGET_FMT_plx " = %x\n", addr, ret); + + return ret; +} + +static void sun4c_intctl_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) +{ + Sun4c_INTCTLState *s = opaque; + + DPRINTF("write reg 0x" TARGET_FMT_plx " = %x\n", addr, val); + val &= 0xbf; + s->reg = val; + sun4c_check_interrupts(s); +} + +static CPUReadMemoryFunc *sun4c_intctl_mem_read[3] = { + sun4c_intctl_mem_readb, + sun4c_intctl_mem_readb, + sun4c_intctl_mem_readb, +}; + +static CPUWriteMemoryFunc *sun4c_intctl_mem_write[3] = { + sun4c_intctl_mem_writeb, + sun4c_intctl_mem_writeb, + sun4c_intctl_mem_writeb, +}; + +void sun4c_pic_info(void *opaque) +{ + Sun4c_INTCTLState *s = opaque; + + term_printf("master: pending 0x%2.2x, enabled 0x%2.2x\n", s->pending, s->reg); +} + +void sun4c_irq_info(void *opaque) +{ +#ifndef DEBUG_IRQ_COUNT + term_printf("irq statistic code not compiled.\n"); +#else + Sun4c_INTCTLState *s = opaque; + int64_t count; + + term_printf("IRQ statistics:\n"); + count = s->irq_count[i]; + if (count > 0) + term_printf("%2d: %" PRId64 "\n", i, count); +#endif +} + +static const uint32_t intbit_to_level[] = { 0, 1, 4, 6, 8, 10, 0, 14, }; + +static void sun4c_check_interrupts(void *opaque) +{ + Sun4c_INTCTLState *s = opaque; + uint32_t pil_pending; + unsigned int i; + + DPRINTF("pending %x disabled %x\n", pending, s->intregm_disabled); + pil_pending = 0; + if (s->pending && !(s->reg & 0x80000000)) { + for (i = 0; i < 8; i++) { + if (s->pending & (1 << i)) + pil_pending |= 1 << intbit_to_level[i]; + } + } + + for (i = 0; i < MAX_PILS; i++) { + if (pil_pending & (1 << i)) { + if (!(s->pil_out & (1 << i))) + qemu_irq_raise(s->cpu_irqs[i]); + } else { + if (s->pil_out & (1 << i)) + qemu_irq_lower(s->cpu_irqs[i]); + } + } + s->pil_out = pil_pending; +} + +/* + * "irq" here is the bit number in the system interrupt register + */ +static void sun4c_set_irq(void *opaque, int irq, int level) +{ + Sun4c_INTCTLState *s = opaque; + uint32_t mask = 1 << irq; + uint32_t pil = intbit_to_level[irq]; + + DPRINTF("Set irq %d -> pil %d level %d\n", irq, pil, + level); + if (pil > 0) { + if (level) { +#ifdef DEBUG_IRQ_COUNT + s->irq_count[pil]++; +#endif + s->pending |= mask; + } else { + s->pending &= ~mask; + } + sun4c_check_interrupts(s); + } +} + +static void sun4c_intctl_save(QEMUFile *f, void *opaque) +{ + Sun4c_INTCTLState *s = opaque; + + qemu_put_8s(f, &s->reg); + qemu_put_8s(f, &s->pending); +} + +static int sun4c_intctl_load(QEMUFile *f, void *opaque, int version_id) +{ + Sun4c_INTCTLState *s = opaque; + + if (version_id != 1) + return -EINVAL; + + qemu_get_8s(f, &s->reg); + qemu_get_8s(f, &s->pending); + sun4c_check_interrupts(s); + + return 0; +} + +static void sun4c_intctl_reset(void *opaque) +{ + Sun4c_INTCTLState *s = opaque; + + s->reg = 1; + s->pending = 0; + sun4c_check_interrupts(s); +} + +void *sun4c_intctl_init(target_phys_addr_t addr, qemu_irq **irq, + qemu_irq *parent_irq) +{ + int sun4c_intctl_io_memory; + Sun4c_INTCTLState *s; + + s = qemu_mallocz(sizeof(Sun4c_INTCTLState)); + if (!s) + return NULL; + + sun4c_intctl_io_memory = cpu_register_io_memory(0, sun4c_intctl_mem_read, + sun4c_intctl_mem_write, s); + cpu_register_physical_memory(addr, INTCTL_SIZE, sun4c_intctl_io_memory); + s->cpu_irqs = parent_irq; + + register_savevm("sun4c_intctl", addr, 1, sun4c_intctl_save, + sun4c_intctl_load, s); + + qemu_register_reset(sun4c_intctl_reset, s); + *irq = qemu_allocate_irqs(sun4c_set_irq, s, 8); + + sun4c_intctl_reset(s); + return s; +} + Index: qemu/Makefile.target =================================================================== --- qemu.orig/Makefile.target 2007-08-22 19:56:37.000000000 +0000 +++ qemu/Makefile.target 2007-08-22 20:03:55.000000000 +0000 @@ -454,7 +454,7 @@ 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 +VL_OBJS+= cs4231.o ptimer.o sun4c_intctl.o endif endif ifeq ($(TARGET_BASE_ARCH), arm)