aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>2007-10-17 13:39:42 +0000
committerths <ths@c046a42c-6fe2-441c-8c8c-71466251a162>2007-10-17 13:39:42 +0000
commitf0fc6f8fbc195424af8bb9ca7409748f665d7b07 (patch)
tree3c96abf87660b304aa86e3dac4b709300b4e20af /hw
parent6bf5b4e8a85a46b5f41eeea0910327608924b382 (diff)
Second half of mipssim support, plus documentation improvements.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3401 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'hw')
-rw-r--r--hw/mips_mipssim.c167
-rw-r--r--hw/mipsnet.c269
2 files changed, 436 insertions, 0 deletions
diff --git a/hw/mips_mipssim.c b/hw/mips_mipssim.c
new file mode 100644
index 0000000000..6c061636b3
--- /dev/null
+++ b/hw/mips_mipssim.c
@@ -0,0 +1,167 @@
+/*
+ * QEMU/mipssim emulation
+ *
+ * Emulates a very simple machine model similiar to the one use by the
+ * proprietary MIPS emulator.
+ */
+#include "vl.h"
+
+#ifdef TARGET_WORDS_BIGENDIAN
+#define BIOS_FILENAME "mips_bios.bin"
+#else
+#define BIOS_FILENAME "mipsel_bios.bin"
+#endif
+
+#ifdef TARGET_MIPS64
+#define PHYS_TO_VIRT(x) ((x) | ~0x7fffffffULL)
+#else
+#define PHYS_TO_VIRT(x) ((x) | ~0x7fffffffU)
+#endif
+
+#define VIRT_TO_PHYS_ADDEND (-((int64_t)(int32_t)0x80000000))
+
+static void load_kernel (CPUState *env)
+{
+ int64_t entry, kernel_low, kernel_high;
+ long kernel_size;
+ long initrd_size;
+ ram_addr_t initrd_offset;
+
+ kernel_size = load_elf(env->kernel_filename, VIRT_TO_PHYS_ADDEND,
+ &entry, &kernel_low, &kernel_high);
+ if (kernel_size >= 0) {
+ if ((entry & ~0x7fffffffULL) == 0x80000000)
+ entry = (int32_t)entry;
+ env->PC[env->current_tc] = entry;
+ } else {
+ fprintf(stderr, "qemu: could not load kernel '%s'\n",
+ env->kernel_filename);
+ exit(1);
+ }
+
+ /* load initrd */
+ initrd_size = 0;
+ initrd_offset = 0;
+ if (env->initrd_filename) {
+ initrd_size = get_image_size (env->initrd_filename);
+ if (initrd_size > 0) {
+ initrd_offset = (kernel_high + ~TARGET_PAGE_MASK) & TARGET_PAGE_MASK;
+ if (initrd_offset + initrd_size > env->ram_size) {
+ fprintf(stderr,
+ "qemu: memory too small for initial ram disk '%s'\n",
+ env->initrd_filename);
+ exit(1);
+ }
+ initrd_size = load_image(env->initrd_filename,
+ phys_ram_base + initrd_offset);
+ }
+ if (initrd_size == (target_ulong) -1) {
+ fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
+ env->initrd_filename);
+ exit(1);
+ }
+ }
+}
+
+static void main_cpu_reset(void *opaque)
+{
+ CPUState *env = opaque;
+ cpu_reset(env);
+ cpu_mips_register(env, NULL);
+
+ if (env->kernel_filename)
+ load_kernel (env);
+}
+
+static void
+mips_mipssim_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)
+{
+ char buf[1024];
+ unsigned long bios_offset;
+ CPUState *env;
+ int ret;
+ mips_def_t *def;
+
+ /* Init CPUs. */
+ if (cpu_model == NULL) {
+#ifdef TARGET_MIPS64
+ cpu_model = "5Kf";
+#else
+ cpu_model = "24Kf";
+#endif
+ }
+ if (mips_find_by_name(cpu_model, &def) != 0)
+ def = NULL;
+ env = cpu_init();
+ cpu_mips_register(env, def);
+ register_savevm("cpu", 0, 3, cpu_save, cpu_load, env);
+ qemu_register_reset(main_cpu_reset, env);
+
+ /* Allocate RAM. */
+ cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
+
+ /* Map the BIOS / boot exception handler. */
+ bios_offset = ram_size + vga_ram_size;
+
+ /* Load a BIOS / boot exception handler image. */
+ if (bios_name == NULL)
+ bios_name = BIOS_FILENAME;
+ snprintf(buf, sizeof(buf), "%s/%s", bios_dir, bios_name);
+ ret = load_image(buf, phys_ram_base + bios_offset);
+ if ((ret < 0 || ret > BIOS_SIZE) && !kernel_filename) {
+ /* Bail out if we have neither a kernel image nor boot vector code. */
+ fprintf(stderr,
+ "qemu: Could not load MIPS bios '%s', and no -kernel argument was specified\n",
+ buf);
+ exit(1);
+ } else {
+ /* We have a boot vector start address. */
+ env->PC[env->current_tc] = (target_long)0xbfc00000;
+ cpu_register_physical_memory(0x1fc00000LL,
+ ret, bios_offset | IO_MEM_ROM);
+ }
+
+ if (kernel_filename) {
+ env->ram_size = ram_size;
+ env->kernel_filename = kernel_filename;
+ env->kernel_cmdline = kernel_cmdline;
+ env->initrd_filename = initrd_filename;
+ load_kernel(env);
+ }
+
+ /* Init CPU internal devices. */
+ cpu_mips_irq_init_cpu(env);
+ cpu_mips_clock_init(env);
+ cpu_mips_irqctrl_init();
+
+ /* Register 64 KB of ISA IO space at 0x1fd00000. */
+ isa_mmio_init(0x1fd00000, 0x00010000);
+
+ /* A single 16450 sits at offset 0x3f8. It is attached to
+ MIPS CPU INT2, which is interrupt 4. */
+ if (serial_hds[0])
+ serial_init(0x3f8, env->irq[4], serial_hds[0]);
+
+ if (nd_table[0].vlan) {
+ if (nd_table[0].model == NULL
+ || strcmp(nd_table[0].model, "mipsnet") == 0) {
+ /* MIPSnet uses the MIPS CPU INT0, which is interrupt 2. */
+ mipsnet_init(0x4200, env->irq[2], &nd_table[0]);
+ } else if (strcmp(nd_table[0].model, "?") == 0) {
+ fprintf(stderr, "qemu: Supported NICs: mipsnet\n");
+ exit (1);
+ } else {
+ fprintf(stderr, "qemu: Unsupported NIC: %s\n", nd_table[0].model);
+ exit (1);
+ }
+ }
+}
+
+QEMUMachine mips_mipssim_machine = {
+ "mipssim",
+ "MIPS MIPSsim platform",
+ mips_mipssim_init,
+};
diff --git a/hw/mipsnet.c b/hw/mipsnet.c
new file mode 100644
index 0000000000..bebfd4d805
--- /dev/null
+++ b/hw/mipsnet.c
@@ -0,0 +1,269 @@
+#include "vl.h"
+
+#define DEBUG_MIPSNET_SEND
+#define DEBUG_MIPSNET_RECEIVE
+//#define DEBUG_MIPSNET_DATA
+#define DEBUG_MIPSNET_IRQ
+
+/* MIPSnet register offsets */
+
+#define MIPSNET_DEV_ID 0x00
+# define MIPSNET_DEV_ID_STRING "MIPSNET0"
+#define MIPSNET_BUSY 0x08
+#define MIPSNET_RX_DATA_COUNT 0x0c
+#define MIPSNET_TX_DATA_COUNT 0x10
+#define MIPSNET_INT_CTL 0x14
+# define MIPSNET_INTCTL_TXDONE 0x00000001
+# define MIPSNET_INTCTL_RXDONE 0x00000002
+# define MIPSNET_INTCTL_TESTBIT 0x80000000
+#define MIPSNET_INTERRUPT_INFO 0x18
+#define MIPSNET_RX_DATA_BUFFER 0x1c
+#define MIPSNET_TX_DATA_BUFFER 0x20
+
+#define MAX_ETH_FRAME_SIZE 1514
+
+typedef struct MIPSnetState {
+ uint32_t busy;
+ uint32_t rx_count;
+ uint32_t rx_read;
+ uint32_t tx_count;
+ uint32_t tx_written;
+ uint32_t intctl;
+ uint8_t rx_buffer[MAX_ETH_FRAME_SIZE];
+ uint8_t tx_buffer[MAX_ETH_FRAME_SIZE];
+ qemu_irq irq;
+ VLANClientState *vc;
+ NICInfo *nd;
+} MIPSnetState;
+
+static void mipsnet_reset(MIPSnetState *s)
+{
+ s->busy = 1;
+ s->rx_count = 0;
+ s->rx_read = 0;
+ s->tx_count = 0;
+ s->tx_written = 0;
+ s->intctl = 0;
+ memset(s->rx_buffer, 0, MAX_ETH_FRAME_SIZE);
+ memset(s->tx_buffer, 0, MAX_ETH_FRAME_SIZE);
+}
+
+static void mipsnet_update_irq(MIPSnetState *s)
+{
+ int isr = !!s->intctl;
+#ifdef DEBUG_MIPSNET_IRQ
+ printf("mipsnet: Set IRQ to %d (%02x)\n", isr, s->intctl);
+#endif
+ qemu_set_irq(s->irq, isr);
+}
+
+static int mipsnet_buffer_full(MIPSnetState *s)
+{
+ if (s->rx_count >= MAX_ETH_FRAME_SIZE)
+ return 1;
+ return 0;
+}
+
+static int mipsnet_can_receive(void *opaque)
+{
+ MIPSnetState *s = opaque;
+
+ if (s->busy)
+ return 0;
+ return !mipsnet_buffer_full(s);
+}
+
+static void mipsnet_receive(void *opaque, const uint8_t *buf, int size)
+{
+ MIPSnetState *s = opaque;
+
+#ifdef DEBUG_MIPSNET_RECEIVE
+ printf("mipsnet: receiving len=%d\n", size);
+#endif
+ if (!mipsnet_can_receive(opaque))
+ return;
+
+ s->busy = 1;
+
+ /* Just accept everything. */
+
+ /* Write packet data. */
+ memcpy(s->rx_buffer, buf, size);
+
+ s->rx_count = size;
+ s->rx_read = 0;
+
+ /* Now we can signal we have received something. */
+ s->intctl |= MIPSNET_INTCTL_RXDONE;
+ mipsnet_update_irq(s);
+}
+
+static uint32_t mipsnet_ioport_read(void *opaque, uint32_t addr)
+{
+ MIPSnetState *s = opaque;
+ int ret = 0;
+ const char *devid = MIPSNET_DEV_ID_STRING;
+
+ addr &= 0x3f;
+ switch (addr) {
+ case MIPSNET_DEV_ID:
+ ret = *((uint32_t *)&devid);
+ break;
+ case MIPSNET_DEV_ID + 4:
+ ret = *((uint32_t *)(&devid + 4));
+ break;
+ case MIPSNET_BUSY:
+ ret = s->busy;
+ break;
+ case MIPSNET_RX_DATA_COUNT:
+ ret = s->rx_count;
+ break;
+ case MIPSNET_TX_DATA_COUNT:
+ ret = s->tx_count;
+ break;
+ case MIPSNET_INT_CTL:
+ ret = s->intctl;
+ s->intctl &= ~MIPSNET_INTCTL_TESTBIT;
+ break;
+ case MIPSNET_INTERRUPT_INFO:
+ /* XXX: This seems to be a per-VPE interrupt number. */
+ ret = 0;
+ break;
+ case MIPSNET_RX_DATA_BUFFER:
+ if (s->rx_count) {
+ s->rx_count--;
+ ret = s->rx_buffer[s->rx_read++];
+ }
+ break;
+ /* Reads as zero. */
+ case MIPSNET_TX_DATA_BUFFER:
+ default:
+ break;
+ }
+#ifdef DEBUG_MIPSNET_DATA
+ printf("mipsnet: read addr=0x%02x val=0x%02x\n", addr, ret);
+#endif
+ return ret;
+}
+
+static void mipsnet_ioport_write(void *opaque, uint32_t addr, uint32_t val)
+{
+ MIPSnetState *s = opaque;
+
+ addr &= 0x3f;
+#ifdef DEBUG_MIPSNET_DATA
+ printf("mipsnet: write addr=0x%02x val=0x%02x\n", addr, val);
+#endif
+ switch (addr) {
+ case MIPSNET_TX_DATA_COUNT:
+ s->tx_count = (val <= MAX_ETH_FRAME_SIZE) ? val : 0;
+ s->tx_written = 0;
+ break;
+ case MIPSNET_INT_CTL:
+ if (val & MIPSNET_INTCTL_TXDONE) {
+ s->intctl &= ~MIPSNET_INTCTL_TXDONE;
+ } else if (val & MIPSNET_INTCTL_RXDONE) {
+ s->intctl &= ~MIPSNET_INTCTL_RXDONE;
+ } else if (val & MIPSNET_INTCTL_TESTBIT) {
+ mipsnet_reset(s);
+ s->intctl |= MIPSNET_INTCTL_TESTBIT;
+ } else if (!val) {
+ /* ACK testbit interrupt, flag was cleared on read. */
+ }
+ s->busy = !!s->intctl;
+ mipsnet_update_irq(s);
+ break;
+ case MIPSNET_TX_DATA_BUFFER:
+ s->tx_buffer[s->tx_written++] = val;
+ if (s->tx_written == s->tx_count) {
+ /* Send buffer. */
+#ifdef DEBUG_MIPSNET_SEND
+ printf("mipsnet: sending len=%d\n", s->tx_count);
+#endif
+ qemu_send_packet(s->vc, s->tx_buffer, s->tx_count);
+ s->tx_count = s->tx_written = 0;
+ s->intctl |= MIPSNET_INTCTL_TXDONE;
+ s->busy = 1;
+ mipsnet_update_irq(s);
+ }
+ break;
+ /* Read-only registers */
+ case MIPSNET_DEV_ID:
+ case MIPSNET_BUSY:
+ case MIPSNET_RX_DATA_COUNT:
+ case MIPSNET_INTERRUPT_INFO:
+ case MIPSNET_RX_DATA_BUFFER:
+ default:
+ break;
+ }
+}
+
+static void mipsnet_save(QEMUFile *f, void *opaque)
+{
+ MIPSnetState *s = opaque;
+
+ qemu_put_be32s(f, &s->busy);
+ qemu_put_be32s(f, &s->rx_count);
+ qemu_put_be32s(f, &s->rx_read);
+ qemu_put_be32s(f, &s->tx_count);
+ qemu_put_be32s(f, &s->tx_written);
+ qemu_put_be32s(f, &s->intctl);
+ qemu_put_buffer(f, s->rx_buffer, MAX_ETH_FRAME_SIZE);
+ qemu_put_buffer(f, s->tx_buffer, MAX_ETH_FRAME_SIZE);
+}
+
+static int mipsnet_load(QEMUFile *f, void *opaque, int version_id)
+{
+ MIPSnetState *s = opaque;
+
+ if (version_id > 0)
+ return -EINVAL;
+
+ qemu_get_be32s(f, &s->busy);
+ qemu_get_be32s(f, &s->rx_count);
+ qemu_get_be32s(f, &s->rx_read);
+ qemu_get_be32s(f, &s->tx_count);
+ qemu_get_be32s(f, &s->tx_written);
+ qemu_get_be32s(f, &s->intctl);
+ qemu_get_buffer(f, s->rx_buffer, MAX_ETH_FRAME_SIZE);
+ qemu_get_buffer(f, s->tx_buffer, MAX_ETH_FRAME_SIZE);
+
+ return 0;
+}
+
+void mipsnet_init (int base, qemu_irq irq, NICInfo *nd)
+{
+ MIPSnetState *s;
+
+ s = qemu_mallocz(sizeof(MIPSnetState));
+ if (!s)
+ return;
+
+ register_ioport_write(base, 36, 1, mipsnet_ioport_write, s);
+ register_ioport_read(base, 36, 1, mipsnet_ioport_read, s);
+ register_ioport_write(base, 36, 2, mipsnet_ioport_write, s);
+ register_ioport_read(base, 36, 2, mipsnet_ioport_read, s);
+ register_ioport_write(base, 36, 4, mipsnet_ioport_write, s);
+ register_ioport_read(base, 36, 4, mipsnet_ioport_read, s);
+
+ s->irq = irq;
+ s->nd = nd;
+ if (nd && nd->vlan) {
+ s->vc = qemu_new_vlan_client(nd->vlan, mipsnet_receive,
+ mipsnet_can_receive, s);
+ } else {
+ s->vc = NULL;
+ }
+
+ snprintf(s->vc->info_str, sizeof(s->vc->info_str),
+ "mipsnet macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
+ s->nd->macaddr[0],
+ s->nd->macaddr[1],
+ s->nd->macaddr[2],
+ s->nd->macaddr[3],
+ s->nd->macaddr[4],
+ s->nd->macaddr[5]);
+
+ mipsnet_reset(s);
+ register_savevm("mipsnet", 0, 0, mipsnet_save, mipsnet_load, s);
+}