]> git.proxmox.com Git - qemu.git/blobdiff - hw/pc.c
isa-bus: Remove bogus IRQ sharing check
[qemu.git] / hw / pc.c
diff --git a/hw/pc.c b/hw/pc.c
index 20dc7fdb5190d33aeaaa4f4396daf9dbbfb5b530..9f1b7765f77432d209049020730573e9ce177ec6 100644 (file)
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -25,6 +25,7 @@
 #include "pc.h"
 #include "apic.h"
 #include "fdc.h"
+#include "ide.h"
 #include "pci.h"
 #include "vmware_vga.h"
 #include "monitor.h"
 #include "elf.h"
 #include "multiboot.h"
 #include "mc146818rtc.h"
+#include "msix.h"
+#include "sysbus.h"
+#include "sysemu.h"
+#include "blockdev.h"
+#include "ui/qemu-spice.h"
 
 /* output Bochs bios info messages */
 //#define DEBUG_BIOS
 
+/* debug PC/ISA interrupts */
+//#define DEBUG_IRQ
+
+#ifdef DEBUG_IRQ
+#define DPRINTF(fmt, ...)                                       \
+    do { printf("CPUIRQ: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...)
+#endif
+
 #define BIOS_FILENAME "bios.bin"
 
 #define PC_MAX_BIOS_SIZE (4 * 1024 * 1024)
@@ -50,6 +66,9 @@
 #define FW_CFG_SMBIOS_ENTRIES (FW_CFG_ARCH_LOCAL + 1)
 #define FW_CFG_IRQ0_OVERRIDE (FW_CFG_ARCH_LOCAL + 2)
 #define FW_CFG_E820_TABLE (FW_CFG_ARCH_LOCAL + 3)
+#define FW_CFG_HPET (FW_CFG_ARCH_LOCAL + 4)
+
+#define MSI_ADDR_BASE 0xfee00000
 
 #define E820_NR_ENTRIES                16
 
@@ -57,12 +76,12 @@ struct e820_entry {
     uint64_t address;
     uint64_t length;
     uint32_t type;
-};
+} __attribute((__packed__, __aligned__(4)));
 
 struct e820_table {
     uint32_t count;
     struct e820_entry entry[E820_NR_ENTRIES];
-};
+} __attribute((__packed__, __aligned__(4)));
 
 static struct e820_table e820_table;
 
@@ -70,6 +89,7 @@ void isa_irq_handler(void *opaque, int n, int level)
 {
     IsaIrqState *isa = (IsaIrqState *)opaque;
 
+    DPRINTF("isa_irqs: %s irq %d\n", level? "raise" : "lower", n);
     if (n < 16) {
         qemu_set_irq(isa->i8259[n], level);
     }
@@ -131,7 +151,7 @@ int cpu_get_pic_interrupt(CPUState *env)
 {
     int intno;
 
-    intno = apic_get_interrupt(env);
+    intno = apic_get_interrupt(env->apic_state);
     if (intno >= 0) {
         /* set irq request if a PIC irq is still pending */
         /* XXX: improve that */
@@ -139,8 +159,9 @@ int cpu_get_pic_interrupt(CPUState *env)
         return intno;
     }
     /* read the irq from the PIC */
-    if (!apic_accept_pic_intr(env))
+    if (!apic_accept_pic_intr(env->apic_state)) {
         return -1;
+    }
 
     intno = pic_read_irq(isa_pic);
     return intno;
@@ -150,10 +171,12 @@ static void pic_irq_request(void *opaque, int irq, int level)
 {
     CPUState *env = first_cpu;
 
+    DPRINTF("pic_irqs: %s irq %d\n", level? "raise" : "lower", irq);
     if (env->apic_state) {
         while (env) {
-            if (apic_accept_pic_intr(env))
-                apic_deliver_pic_intr(env, level);
+            if (apic_accept_pic_intr(env->apic_state)) {
+                apic_deliver_pic_intr(env->apic_state, level);
+            }
             env = env->next_cpu;
         }
     } else {
@@ -255,14 +278,65 @@ static int pc_boot_set(void *opaque, const char *boot_device)
     return set_boot_dev(opaque, boot_device, 0);
 }
 
-/* hd_table must contain 4 block drivers */
+typedef struct pc_cmos_init_late_arg {
+    ISADevice *rtc_state;
+    BusState *idebus0, *idebus1;
+} pc_cmos_init_late_arg;
+
+static void pc_cmos_init_late(void *opaque)
+{
+    pc_cmos_init_late_arg *arg = opaque;
+    ISADevice *s = arg->rtc_state;
+    int val;
+    BlockDriverState *hd_table[4];
+    int i;
+
+    ide_get_bs(hd_table, arg->idebus0);
+    ide_get_bs(hd_table + 2, arg->idebus1);
+
+    rtc_set_memory(s, 0x12, (hd_table[0] ? 0xf0 : 0) | (hd_table[1] ? 0x0f : 0));
+    if (hd_table[0])
+        cmos_init_hd(0x19, 0x1b, hd_table[0], s);
+    if (hd_table[1])
+        cmos_init_hd(0x1a, 0x24, hd_table[1], s);
+
+    val = 0;
+    for (i = 0; i < 4; i++) {
+        if (hd_table[i]) {
+            int cylinders, heads, sectors, translation;
+            /* NOTE: bdrv_get_geometry_hint() returns the physical
+                geometry.  It is always such that: 1 <= sects <= 63, 1
+                <= heads <= 16, 1 <= cylinders <= 16383. The BIOS
+                geometry can be different if a translation is done. */
+            translation = bdrv_get_translation_hint(hd_table[i]);
+            if (translation == BIOS_ATA_TRANSLATION_AUTO) {
+                bdrv_get_geometry_hint(hd_table[i], &cylinders, &heads, &sectors);
+                if (cylinders <= 1024 && heads <= 16 && sectors <= 63) {
+                    /* No translation. */
+                    translation = 0;
+                } else {
+                    /* LBA translation. */
+                    translation = 1;
+                }
+            } else {
+                translation--;
+            }
+            val |= translation << (i * 2);
+        }
+    }
+    rtc_set_memory(s, 0x39, val);
+
+    qemu_unregister_reset(pc_cmos_init_late, opaque);
+}
+
 void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
-                  const char *boot_device, DriveInfo **hd_table,
+                  const char *boot_device,
+                  BusState *idebus0, BusState *idebus1,
                   FDCtrl *floppy_controller, ISADevice *s)
 {
     int val;
     int fd0, fd1, nb;
-    int i;
+    static pc_cmos_init_late_arg arg;
 
     /* various important CMOS locations needed by PC/Bochs bios */
 
@@ -331,60 +405,99 @@ void pc_cmos_init(ram_addr_t ram_size, ram_addr_t above_4g_mem_size,
     rtc_set_memory(s, REG_EQUIPMENT_BYTE, val);
 
     /* hard drives */
+    arg.rtc_state = s;
+    arg.idebus0 = idebus0;
+    arg.idebus1 = idebus1;
+    qemu_register_reset(pc_cmos_init_late, &arg);
+}
 
-    rtc_set_memory(s, 0x12, (hd_table[0] ? 0xf0 : 0) | (hd_table[1] ? 0x0f : 0));
-    if (hd_table[0])
-        cmos_init_hd(0x19, 0x1b, hd_table[0]->bdrv, s);
-    if (hd_table[1])
-        cmos_init_hd(0x1a, 0x24, hd_table[1]->bdrv, s);
+/* port 92 stuff: could be split off */
+typedef struct Port92State {
+    ISADevice dev;
+    uint8_t outport;
+    qemu_irq *a20_out;
+} Port92State;
 
-    val = 0;
-    for (i = 0; i < 4; i++) {
-        if (hd_table[i]) {
-            int cylinders, heads, sectors, translation;
-            /* NOTE: bdrv_get_geometry_hint() returns the physical
-                geometry.  It is always such that: 1 <= sects <= 63, 1
-                <= heads <= 16, 1 <= cylinders <= 16383. The BIOS
-                geometry can be different if a translation is done. */
-            translation = bdrv_get_translation_hint(hd_table[i]->bdrv);
-            if (translation == BIOS_ATA_TRANSLATION_AUTO) {
-                bdrv_get_geometry_hint(hd_table[i]->bdrv, &cylinders, &heads, &sectors);
-                if (cylinders <= 1024 && heads <= 16 && sectors <= 63) {
-                    /* No translation. */
-                    translation = 0;
-                } else {
-                    /* LBA translation. */
-                    translation = 1;
-                }
-            } else {
-                translation--;
-            }
-            val |= translation << (i * 2);
-        }
+static void port92_write(void *opaque, uint32_t addr, uint32_t val)
+{
+    Port92State *s = opaque;
+
+    DPRINTF("port92: write 0x%02x\n", val);
+    s->outport = val;
+    qemu_set_irq(*s->a20_out, (val >> 1) & 1);
+    if (val & 1) {
+        qemu_system_reset_request();
     }
-    rtc_set_memory(s, 0x39, val);
 }
 
-void ioport_set_a20(int enable)
+static uint32_t port92_read(void *opaque, uint32_t addr)
 {
-    /* XXX: send to all CPUs ? */
-    cpu_x86_set_a20(first_cpu, enable);
+    Port92State *s = opaque;
+    uint32_t ret;
+
+    ret = s->outport;
+    DPRINTF("port92: read 0x%02x\n", ret);
+    return ret;
 }
 
-int ioport_get_a20(void)
+static void port92_init(ISADevice *dev, qemu_irq *a20_out)
 {
-    return ((first_cpu->a20_mask >> 20) & 1);
+    Port92State *s = DO_UPCAST(Port92State, dev, dev);
+
+    s->a20_out = a20_out;
 }
 
-static void ioport92_write(void *opaque, uint32_t addr, uint32_t val)
+static const VMStateDescription vmstate_port92_isa = {
+    .name = "port92",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_UINT8(outport, Port92State),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void port92_reset(DeviceState *d)
 {
-    ioport_set_a20((val >> 1) & 1);
-    /* XXX: bit 0 is fast reset */
+    Port92State *s = container_of(d, Port92State, dev.qdev);
+
+    s->outport &= ~1;
 }
 
-static uint32_t ioport92_read(void *opaque, uint32_t addr)
+static int port92_initfn(ISADevice *dev)
 {
-    return ioport_get_a20() << 1;
+    Port92State *s = DO_UPCAST(Port92State, dev, dev);
+
+    register_ioport_read(0x92, 1, 1, port92_read, s);
+    register_ioport_write(0x92, 1, 1, port92_write, s);
+    isa_init_ioport(dev, 0x92);
+    s->outport = 0;
+    return 0;
+}
+
+static ISADeviceInfo port92_info = {
+    .qdev.name     = "port92",
+    .qdev.size     = sizeof(Port92State),
+    .qdev.vmsd     = &vmstate_port92_isa,
+    .qdev.no_user  = 1,
+    .qdev.reset    = port92_reset,
+    .init          = port92_initfn,
+};
+
+static void port92_register(void)
+{
+    isa_qdev_register(&port92_info);
+}
+device_init(port92_register)
+
+static void handle_a20_line_change(void *opaque, int irq, int level)
+{
+    CPUState *cpu = opaque;
+
+    /* XXX: send to all CPUs ? */
+    /* XXX: add logic to handle multiple A20 line sources */
+    cpu_x86_set_a20(cpu, level);
 }
 
 /***********************************************************/
@@ -399,8 +512,8 @@ static void bochs_bios_write(void *opaque, uint32_t addr, uint32_t val)
         /* Bochs BIOS messages */
     case 0x400:
     case 0x401:
-        fprintf(stderr, "BIOS panic at rombios.c, line %d\n", val);
-        exit(1);
+        /* used to be panic, now unused */
+        break;
     case 0x402:
     case 0x403:
 #ifdef DEBUG_BIOS
@@ -436,19 +549,19 @@ static void bochs_bios_write(void *opaque, uint32_t addr, uint32_t val)
 
 int e820_add_entry(uint64_t address, uint64_t length, uint32_t type)
 {
-    int index = e820_table.count;
+    int index = le32_to_cpu(e820_table.count);
     struct e820_entry *entry;
 
     if (index >= E820_NR_ENTRIES)
         return -EBUSY;
-    entry = &e820_table.entry[index];
+    entry = &e820_table.entry[index++];
 
-    entry->address = address;
-    entry->length = length;
-    entry->type = type;
+    entry->address = cpu_to_le64(address);
+    entry->length = cpu_to_le64(length);
+    entry->type = cpu_to_le32(type);
 
-    e820_table.count++;
-    return e820_table.count;
+    e820_table.count = cpu_to_le32(index);
+    return index;
 }
 
 static void *bochs_bios_init(void)
@@ -485,6 +598,8 @@ static void *bochs_bios_init(void)
     fw_cfg_add_bytes(fw_cfg, FW_CFG_E820_TABLE, (uint8_t *)&e820_table,
                      sizeof(struct e820_table));
 
+    fw_cfg_add_bytes(fw_cfg, FW_CFG_HPET, (uint8_t *)&hpet_cfg,
+                     sizeof(struct hpet_fw_config));
     /* allocate memory for the NUMA channel: one (64bit) word for the number
      * of nodes, one word for each VCPU->node and one word for each node to
      * hold the amount of memory.
@@ -700,7 +815,8 @@ static void load_linux(void *fw_cfg,
     fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, setup_size);
     fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, setup, setup_size);
 
-    option_rom[nb_option_roms] = "linuxboot.bin";
+    option_rom[nb_option_roms].name = "linuxboot.bin";
+    option_rom[nb_option_roms].bootindex = 0;
     nb_option_roms++;
 }
 
@@ -713,25 +829,6 @@ static const int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 };
 static const int parallel_io[MAX_PARALLEL_PORTS] = { 0x378, 0x278, 0x3bc };
 static const int parallel_irq[MAX_PARALLEL_PORTS] = { 7, 7, 7 };
 
-#ifdef HAS_AUDIO
-void pc_audio_init (PCIBus *pci_bus, qemu_irq *pic)
-{
-    struct soundhw *c;
-
-    for (c = soundhw; c->name; ++c) {
-        if (c->enabled) {
-            if (c->isa) {
-                c->init.init_isa(pic);
-            } else {
-                if (pci_bus) {
-                    c->init.init_pci(pci_bus);
-                }
-            }
-        }
-    }
-}
-#endif
-
 void pc_init_ne2k_isa(NICInfo *nd)
 {
     static int nb_ne2k = 0;
@@ -749,6 +846,41 @@ int cpu_is_bsp(CPUState *env)
     return env->cpu_index == 0;
 }
 
+DeviceState *cpu_get_current_apic(void)
+{
+    if (cpu_single_env) {
+        return cpu_single_env->apic_state;
+    } else {
+        return NULL;
+    }
+}
+
+static DeviceState *apic_init(void *env, uint8_t apic_id)
+{
+    DeviceState *dev;
+    SysBusDevice *d;
+    static int apic_mapped;
+
+    dev = qdev_create(NULL, "apic");
+    qdev_prop_set_uint8(dev, "id", apic_id);
+    qdev_prop_set_ptr(dev, "cpu_env", env);
+    qdev_init_nofail(dev);
+    d = sysbus_from_qdev(dev);
+
+    /* XXX: mapping more APICs at the same memory location */
+    if (apic_mapped == 0) {
+        /* NOTE: the APIC is directly connected to the CPU - it is not
+           on the global memory bus. */
+        /* XXX: what if the base changes? */
+        sysbus_mmio_map(d, 0, MSI_ADDR_BASE);
+        apic_mapped = 1;
+    }
+
+    msix_supported = 1;
+
+    return dev;
+}
+
 /* set CMOS shutdown status register (index 0xF) as S3_resume(0xFE)
    BIOS will read it and start S3 resume at POST Entry */
 void pc_cmos_set_s3_resume(void *opaque, int irq, int level)
@@ -769,6 +901,14 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level)
     }
 }
 
+static void pc_cpu_reset(void *opaque)
+{
+    CPUState *env = opaque;
+
+    cpu_reset(env);
+    env->halted = !cpu_is_bsp(env);
+}
+
 static CPUState *pc_new_cpu(const char *cpu_model)
 {
     CPUState *env;
@@ -780,11 +920,10 @@ static CPUState *pc_new_cpu(const char *cpu_model)
     }
     if ((env->cpuid_features & CPUID_APIC) || smp_cpus > 1) {
         env->cpuid_apic_id = env->cpu_index;
-        /* APIC reset callback resets cpu */
-        apic_init(env);
-    } else {
-        qemu_register_reset((QEMUResetHandler*)cpu_reset, env);
+        env->apic_state = apic_init(env, env->cpuid_apic_id);
     }
+    qemu_register_reset(pc_cpu_reset, env);
+    pc_cpu_reset(env);
     return env;
 }
 
@@ -818,7 +957,7 @@ void pc_memory_init(ram_addr_t ram_size,
     ram_addr_t ram_addr, bios_offset, option_rom_offset;
     ram_addr_t below_4g_mem_size, above_4g_mem_size = 0;
     int bios_size, isa_bios_size;
-    void **fw_cfg;
+    void *fw_cfg;
 
     if (ram_size >= 0xe0000000 ) {
         above_4g_mem_size = ram_size - 0xe0000000;
@@ -829,27 +968,26 @@ void pc_memory_init(ram_addr_t ram_size,
     *above_4g_mem_size_p = above_4g_mem_size;
     *below_4g_mem_size_p = below_4g_mem_size;
 
+#if TARGET_PHYS_ADDR_BITS == 32
+    if (above_4g_mem_size > 0) {
+        hw_error("To much RAM for 32-bit physical address");
+    }
+#endif
     linux_boot = (kernel_filename != NULL);
 
     /* allocate RAM */
-    ram_addr = qemu_ram_alloc(below_4g_mem_size);
+    ram_addr = qemu_ram_alloc(NULL, "pc.ram",
+                              below_4g_mem_size + above_4g_mem_size);
     cpu_register_physical_memory(0, 0xa0000, ram_addr);
     cpu_register_physical_memory(0x100000,
                  below_4g_mem_size - 0x100000,
                  ram_addr + 0x100000);
-
-    /* above 4giga memory allocation */
+#if TARGET_PHYS_ADDR_BITS > 32
     if (above_4g_mem_size > 0) {
-#if TARGET_PHYS_ADDR_BITS == 32
-        hw_error("To much RAM for 32-bit physical address");
-#else
-        ram_addr = qemu_ram_alloc(above_4g_mem_size);
-        cpu_register_physical_memory(0x100000000ULL,
-                                     above_4g_mem_size,
-                                     ram_addr);
-#endif
+        cpu_register_physical_memory(0x100000000ULL, above_4g_mem_size,
+                                     ram_addr + below_4g_mem_size);
     }
-
+#endif
 
     /* BIOS load */
     if (bios_name == NULL)
@@ -864,8 +1002,8 @@ void pc_memory_init(ram_addr_t ram_size,
         (bios_size % 65536) != 0) {
         goto bios_error;
     }
-    bios_offset = qemu_ram_alloc(bios_size);
-    ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size));
+    bios_offset = qemu_ram_alloc(NULL, "pc.bios", bios_size);
+    ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1);
     if (ret != 0) {
     bios_error:
         fprintf(stderr, "qemu: could not load PC BIOS '%s'\n", bios_name);
@@ -882,7 +1020,7 @@ void pc_memory_init(ram_addr_t ram_size,
                                  isa_bios_size,
                                  (bios_offset + bios_size - isa_bios_size) | IO_MEM_ROM);
 
-    option_rom_offset = qemu_ram_alloc(PC_ROM_SIZE);
+    option_rom_offset = qemu_ram_alloc(NULL, "pc.rom", PC_ROM_SIZE);
     cpu_register_physical_memory(PC_ROM_MIN_VGA, PC_ROM_SIZE, option_rom_offset);
 
     /* map all the bios at the top of memory */
@@ -893,11 +1031,11 @@ void pc_memory_init(ram_addr_t ram_size,
     rom_set_fw(fw_cfg);
 
     if (linux_boot) {
-        load_linux(*fw_cfg, kernel_filename, initrd_filename, kernel_cmdline, below_4g_mem_size);
+        load_linux(fw_cfg, kernel_filename, initrd_filename, kernel_cmdline, below_4g_mem_size);
     }
 
     for (i = 0; i < nb_option_roms; i++) {
-        rom_add_option(option_rom[i]);
+        rom_add_option(option_rom[i].name, option_rom[i].bootindex);
     }
 }
 
@@ -919,15 +1057,31 @@ void pc_vga_init(PCIBus *pci_bus)
             pci_vmsvga_init(pci_bus);
         else
             fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __FUNCTION__);
+#ifdef CONFIG_SPICE
+    } else if (qxl_enabled) {
+        if (pci_bus)
+            pci_create_simple(pci_bus, -1, "qxl-vga");
+        else
+            fprintf(stderr, "%s: qxl: no PCI bus\n", __FUNCTION__);
+#endif
     } else if (std_vga_enabled) {
         if (pci_bus) {
-            pci_vga_init(pci_bus, 0, 0);
+            pci_vga_init(pci_bus);
         } else {
             isa_vga_init();
         }
     }
 }
 
+static void cpu_request_exit(void *opaque, int irq, int level)
+{
+    CPUState *env = cpu_single_env;
+
+    if (env && level) {
+        cpu_exit(env);
+    }
+}
+
 void pc_basic_device_init(qemu_irq *isa_irq,
                           FDCtrl **floppy_controller,
                           ISADevice **rtc_state)
@@ -935,23 +1089,29 @@ void pc_basic_device_init(qemu_irq *isa_irq,
     int i;
     DriveInfo *fd[MAX_FD];
     PITState *pit;
+    qemu_irq rtc_irq = NULL;
+    qemu_irq *a20_line;
+    ISADevice *i8042, *port92;
+    qemu_irq *cpu_exit_irq;
 
     register_ioport_write(0x80, 1, 1, ioport80_write, NULL);
 
     register_ioport_write(0xf0, 1, 1, ioportF0_write, NULL);
 
-    *rtc_state = rtc_init(2000);
+    if (!no_hpet) {
+        DeviceState *hpet = sysbus_create_simple("hpet", HPET_BASE, NULL);
 
-    qemu_register_boot_set(pc_boot_set, *rtc_state);
+        for (i = 0; i < 24; i++) {
+            sysbus_connect_irq(sysbus_from_qdev(hpet), i, isa_irq[i]);
+        }
+        rtc_irq = qdev_get_gpio_in(hpet, 0);
+    }
+    *rtc_state = rtc_init(2000, rtc_irq);
 
-    register_ioport_read(0x92, 1, 1, ioport92_read, NULL);
-    register_ioport_write(0x92, 1, 1, ioport92_write, NULL);
+    qemu_register_boot_set(pc_boot_set, *rtc_state);
 
-    pit = pit_init(0x40, isa_reserve_irq(0));
+    pit = pit_init(0x40, isa_get_irq(0));
     pcspk_init(pit);
-    if (!no_hpet) {
-        hpet_init(isa_irq);
-    }
 
     for(i = 0; i < MAX_SERIAL_PORTS; i++) {
         if (serial_hds[i]) {
@@ -965,8 +1125,15 @@ void pc_basic_device_init(qemu_irq *isa_irq,
         }
     }
 
-    isa_create_simple("i8042");
-    DMA_init(0);
+    a20_line = qemu_allocate_irqs(handle_a20_line_change, first_cpu, 2);
+    i8042 = isa_create_simple("i8042");
+    i8042_setup_a20_line(i8042, &a20_line[0]);
+    vmmouse_init(i8042);
+    port92 = isa_create_simple("port92");
+    port92_init(port92, &a20_line[1]);
+
+    cpu_exit_irq = qemu_allocate_irqs(cpu_request_exit, NULL, 1);
+    DMA_init(0, cpu_exit_irq);
 
     for(i = 0; i < MAX_FD; i++) {
         fd[i] = drive_get(IF_FLOPPY, 0, i);