Logo Search packages:      
Sourcecode: faumachine version File versions  Download package

arch_gen_cpu_x86_apic.c

/* $Id: arch_gen_cpu_x86_apic.c,v 1.34 2009-01-28 12:59:15 potyra Exp $ 
 *
 * Copyright (C) 2007-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#define DEBUG_CONTROL_FLOW    0

#if 80486 <= CONFIG_CPU && CONFIG_CPU_APIC_SUPPORT

#define CPU_APIC_VERSION      1
#define CPU_APIC_MAX_LVT      4

/*forward*/ static void
NAME_(apic_msg0_receive)(
      unsigned int destination_mode,
      unsigned int delivery_mode,
      unsigned int level,
      unsigned int trigger_mode,
      uint8_t vector,
      uint8_t destination
);

static int
NAME_(apic_bit_find)(uint32_t *v, unsigned int vsize)
{
      int i;
      int j;

      for (i = (vsize / 32) - 1; ; i--) {
            uint32_t m;

            if (i < 0) {
                  return -1;
            }
            m = v[i];
            if (m == 0) {
                  continue;
            }
            for (j = 31; ; j--) {
                  if (m & (1 << j)) {
                        return i * 32 + j;
                  }
            }
      }
}

static void
NAME_(apic_bit_set)(uint32_t *v, unsigned int bitno)
{
      v[bitno / 32] |= 1 << (bitno & 0x1f);
}

static void
NAME_(apic_bit_clr)(uint32_t *v, unsigned int bitno)
{
      v[bitno / 32] &= ~(1 << (bitno & 0x1f));
}

static int
NAME_(apic_bit_test)(uint32_t *v, unsigned int bitno)
{
      return v[bitno / 32] >> (bitno & 0x1f) & 1;
}

static inline int
NAME_(apic_extint_pri)(void)
{
      if (env->apic.extint_pending) {
            return env->apic.extint_pri;
      } else {
            return -1;
      }
}

static inline int
NAME_(apic_smp_pri)(void)
{
      int smp_irr;
      int smp_isr;
      int smp_ppr;

      smp_irr = NAME_(apic_bit_find)(env->apic.irr, 256);
      smp_isr = NAME_(apic_bit_find)(env->apic.isr, 256);
      smp_ppr = env->apic.tpr;

      if (0 <= smp_irr
       && smp_isr < smp_irr
       && smp_ppr <= smp_irr) {
            return smp_irr;
      } else {
            return -1;
      }
}

static void
NAME_(apic_irq_update)(void)
{
      int extint_pri;
      int smp_pri;

      extint_pri = NAME_(apic_extint_pri)();
      smp_pri = NAME_(apic_smp_pri)();

      if (0 <= extint_pri
       || (env->apic.apic_enable
        && env->apic.svr.apic_enabled
        && 0 <= smp_pri)) {
            env->interrupt_request |= CPU_INTERRUPT_IRQ;
            NAME_(interrupt)();
      } else {
            env->interrupt_request &= ~CPU_INTERRUPT_IRQ;
      }
}

static void
NAME_(apic_nmi_update)(void)
{
      if (env->apic.nmi_pending) {
            env->interrupt_request |= CPU_INTERRUPT_NMI;
            NAME_(interrupt)();
      } else {
            env->interrupt_request &= ~CPU_INTERRUPT_NMI;
      }
}

static void
NAME_(apic_smi_update)(void)
{
      if (env->apic.smi_pending) {
            env->interrupt_request |= CPU_INTERRUPT_SMI;
            NAME_(interrupt)();
      } else {
            env->interrupt_request &= ~CPU_INTERRUPT_SMI;
      }
}

int
NAME_(apic_irq_ack)(void)
{
      int extint_pri;
      int smp_pri;
      uint8_t vec;

      /* Get external interrupt priority. */
      extint_pri = NAME_(apic_extint_pri)();

      /* Get SMP interrupt priority */
      smp_pri = NAME_(apic_smp_pri)();

      if (extint_pri < 0
       && smp_pri < 0) {
            /*
             * Spurious interrupt.
             */
            vec = env->apic.svr.spurious_vector;

      } else if (extint_pri < smp_pri) {
            /*
             * SMP with high priority.
             */
            vec = smp_pri;

            NAME_(apic_bit_clr)(env->apic.irr, vec);
            NAME_(apic_bit_set)(env->apic.isr, vec);
            if (NAME_(apic_bit_test)(env->apic.tmr, vec)) {
                  NAME_(apic_bit_clr)(env->apic.tmr, vec);
                  /* Send EOI to all IOAPICs. */
                  sig_icc_bus_eoi(env->icc_bus, env, vec);
            }

      } else { assert(smp_pri <= extint_pri);
            /*
             * EXTINT with high priority.
             */
            env->apic.extint_pending = 0;

            NAME_(ack)(&vec);
      }

      NAME_(apic_irq_update)();

      return vec;
}

void
NAME_(apic_nmi_ack)(void)
{
      env->apic.nmi_pending = 0;
      NAME_(apic_nmi_update)();
}

void
NAME_(apic_smi_ack)(void)
{
      env->apic.smi_pending = 0;
      NAME_(apic_smi_update)();
}

static int
NAME_(apic_affected)(
      unsigned int destination_mode,
      uint8_t destination
)
{
      if (destination_mode == 0) {
            /*
             * Physical Destination
             */
            /* High order bits are ignored. */
            destination &= 0x0f;

            if (destination == env->apic.phys_apic_id
             || destination == 0xf) {
                  return 1;
            } else {
                  return 0;
            }
      } else {
            /*
             * Logical Destination
             */
            switch (env->apic.dfr_model) {
            case 0x0: /* Cluster Model */
                  if ((destination >> 4) == (env->apic.ldr >> 4)
                   || (destination >> 4) == 0xf) {
                        return (destination & env->apic.ldr & 0xf) ? 1 : 0;
                  } else {
                        return 0;
                  }
            case 0xf: /* Flat Model */
                  return (destination & env->apic.ldr) != 0;
            default:
                  assert(0);
            }
      }
}

static void
NAME_(apic_deliver_irq_local)(
      unsigned int delivery_mode,
      unsigned int level,
      unsigned int trigger_mode,
      uint8_t vector
)
{
      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "%s: delivery_mode=%d, level=%d, trigger_mode=%d, vector=0x%02x\n",
                        __FUNCTION__,
                        delivery_mode,
                        level, trigger_mode, vector);
      }

      switch (delivery_mode) {
      case 0: /* FIXED */
            NAME_(apic_bit_set)(env->apic.irr, vector);
            if (trigger_mode) {
                  NAME_(apic_bit_set)(env->apic.tmr, vector);
            } else {
                  NAME_(apic_bit_clr)(env->apic.tmr, vector);
            }
            NAME_(apic_irq_update)();
            break;

      case 1: /* Lowest Priority */
            /* Same as FIXED for now. FIXME */
            NAME_(apic_bit_set)(env->apic.irr, vector);
            if (trigger_mode) {
                  NAME_(apic_bit_set)(env->apic.tmr, vector);
            } else {
                  NAME_(apic_bit_clr)(env->apic.tmr, vector);
            }
            NAME_(apic_irq_update)();
            break;

      case 2: /* SMI */
            env->apic.smi_pending = 1;
            NAME_(apic_smi_update)();
            break;

      case 4: /* NMI */
            env->apic.nmi_pending = 1;
            NAME_(apic_nmi_update)();
            break;

      case 5: /* INIT */
            if (level == 0) {
                  /* Set arbitration ID to value of APIC ID. */
                  /* Nothing to do, yet... */
            } else {
                  NAME_(core_init)();
            }
            break;

      case 6: /* STARTUP */
            NAME_(core_startup)(vector);
            break;

      case 7: /* EXTINT */
            env->apic.extint_pending = 1;
            env->apic.extint_pri = vector;
            NAME_(apic_irq_update)();
            break;

      default:
            fprintf(stderr, "delivery_mode=%d\n", delivery_mode);
            assert(0); /* Cannot happen. */
      }
}

static void
NAME_(apic_deliver_eoi_local)(uint8_t vec)
{
      /* FIXME VOSSI */
}

static void
NAME_(apic_deliver_eoi)(uint8_t vec)
{
      NAME_(apic_deliver_eoi_local)(vec);
      /* FIXME VOSSI */
}

static void
NAME_(apic_lintX_set)(unsigned int nr, unsigned int val)
{
      if (env->apic.apic_enable
       && env->apic.svr.apic_enabled) {
            /*
             * APIC enabled.
             */
            val ^= env->apic.lvt_lint[nr].polarity;
            val &= ! env->apic.lvt_lint[nr].mask;

            if (val) {
                  NAME_(apic_deliver_irq_local)(
                        env->apic.lvt_lint[nr].delivery_mode,
                        1, /* Level */
                        env->apic.lvt_lint[nr].trigger,
                        env->apic.lvt_lint[nr].vector);
            }
      } else {
            /*
             * APIC disabled.
             */
            if (val) {
                  switch (nr) {
                  case 0: /* IRQ */
                        NAME_(apic_deliver_irq_local)(
                                    7, 1, 0, 0x00);
                        break;
                  case 1: /* NMI */
                        NAME_(apic_deliver_irq_local)(
                                    4, 1, 1, 0x00);
                        break;
                  default:
                        assert(0);
                  }
            }
      }
}

static void
NAME_(apic_lint0_set)(unsigned int val)
{
      NAME_(apic_lintX_set)(0, val);
}

static void
NAME_(apic_lint1_set)(unsigned int val)
{
      NAME_(apic_lintX_set)(1, val);
}

static void
NAME_(apic_smi_set)(unsigned int val)
{
      env->apic.smi_pending = val;
      NAME_(apic_smi_update)();
}

static void
NAME_(apic_eoi_receive)(uint8_t vector)
{
      fixme();
}

static void
NAME_(apic_msg0_receive)(
      unsigned int destination_mode,
      unsigned int delivery_mode,
      unsigned int level,
      unsigned int trigger_mode,
      uint8_t vector,
      uint8_t destination
)
{
      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "%s: destination_mode=%d, delivery_mode=%d, level=%d, trigger_mode=%d, vector=0x%02x, destination=0x%02x\n",
                        __FUNCTION__,
                        destination_mode, delivery_mode,
                        level, trigger_mode, vector, destination);
      }

      if (NAME_(apic_affected)(destination_mode, destination)) {
            NAME_(apic_deliver_irq_local)(delivery_mode, level,
                        trigger_mode, vector);
      }
}

static void
NAME_(apic_status0_receive)(unsigned int status)
{
      fixme();
}

static void
NAME_(apic_msg1_receive)(uint8_t processor_priority)
{
      fixme();
}

static void
NAME_(apic_status1_receive)(unsigned int status)
{
      fixme();
}

#if 0
static unsigned int
NAME_(apic_timer_base)(struct cpu *css)
{
      unsigned int value;
      unsigned int mask;
      unsigned int base;

      value = css->apic.timer_dcr;

      mask = (value & 0x3) | ((value & 0x8) >> 1);

      switch (mask) {
      case 0: base = 2; break;
      case 1: base = 4; break;
      case 2: base = 8; break;
      case 3: base = 16; break;
      case 4: base = 32; break;
      case 5: base = 64; break;
      case 6: base = 128; break;
      case 7: base = 1; break;
      default: assert(0); /* Cannot happen. */
      }

      return base;
}
#endif

/*forward*/ static void
NAME_(apic_timer_event)(void *_env);

static void
NAME_(apic_timer_update)(void)
{
      unsigned long long tsc;
      unsigned long long ticks;
      int irq;

      tsc = time_virt() - env->apic.timer_event;
      ticks = (tsc / env->apic.tsc_to_bus) >> env->apic.timer_dcr;
      env->apic.timer_event += (ticks << env->apic.timer_dcr) * env->apic.tsc_to_bus;

      irq = 0;
      while (env->apic.timer_ccr != 0
          && 0 < ticks) {
            if (ticks < env->apic.timer_ccr) {
                  env->apic.timer_ccr -= ticks;
                  ticks = 0;
            } else {
                  ticks -= env->apic.timer_ccr;
                  env->apic.timer_ccr = 0;
                  if (env->apic.lvt_timer.timer_mode) {
                        /* Periodic Timer */
                        env->apic.timer_ccr = env->apic.timer_icr;
                  }
                  irq = 1;
            }
      }
      if (irq
       && ! env->apic.lvt_timer.mask) {
            NAME_(apic_deliver_irq_local)(
                        0, /* Delivery Mode FIXED */
                        1, /* Level */
                        0, /* Edge Triggered */
                        env->apic.lvt_timer.vector);
      }
}

static void
NAME_(apic_timer_stop)(void)
{
      if (env->apic.timer_running) {
            int ret;

            ret = time_call_delete(NAME_(apic_timer_event), env);
            assert(ret == 0);

            env->apic.timer_running = 0;
      }
}

static void
NAME_(apic_timer_start)(void)
{
      if (env->apic.timer_ccr != 0) {
            unsigned long long ticks;
            unsigned long long tsc;

            ticks = env->apic.timer_ccr << env->apic.timer_dcr;
            tsc = env->apic.timer_event + ticks * env->apic.tsc_to_bus;
            time_call_at(tsc, NAME_(apic_timer_event), env);

            env->apic.timer_running = 1;
      }
}

static void
NAME_(apic_timer_event)(void *_env)
{
      env = (struct cpu *) _env;

      /* Timer already stopped by time code. */
      env->apic.timer_running = 0;

      NAME_(apic_timer_update)();

      NAME_(apic_timer_start)();
}

static uint32_t
NAME_(_apic_read)(uint32_t reg)
{
      unsigned long val;

      switch (reg) {
      case APIC_ID: /* 0x020 */
            val = env->apic.phys_apic_id << 24;
            break;

      case APIC_LVR: /* 0x030 */
            val = CPU_APIC_MAX_LVT << 16;
            val |= 1 << 4; /* Internal APIC */
            val |= CPU_APIC_VERSION << 0;
            break;

      case APIC_TASKPRI: /* 0x080 */
            val = env->apic.tpr;
            break;

      case APIC_ARBPRI: /* 0x090 */
            val = 0; /* FIXME VOSSI */
            break;

      case APIC_PROCPRI: /* 0x0a0 */
            val = 0; /* FIXME VOSSI */
            break;

      case APIC_EOI: /* 0x0b0 */
            /* Write-only. */
            val = 0;
            break;

      case APIC_LDR: /* 0x0d0 */
            val = env->apic.ldr << 24;
            break;

      case APIC_DFR: /* 0x0e0 */
            val = env->apic.dfr_model << 28;
            val |= 0x0fffffff;
            break;

      case APIC_SPIV: /* 0x0f0 */
            val = env->apic.svr.focus_cpu << 9;
            val |= env->apic.svr.apic_enabled << 8;
            val |= env->apic.svr.spurious_vector << 0;
            break;

      case APIC_ISR + 0x00: /* 0x100 */
      case APIC_ISR + 0x10:
      case APIC_ISR + 0x20:
      case APIC_ISR + 0x30:
      case APIC_ISR + 0x40:
      case APIC_ISR + 0x50:
      case APIC_ISR + 0x60:
      case APIC_ISR + 0x70:
            val = env->apic.isr[(reg >> 4) & 7];
            break;

      case APIC_TMR + 0x00: /* 0x180 */
      case APIC_TMR + 0x10:
      case APIC_TMR + 0x20:
      case APIC_TMR + 0x30:
      case APIC_TMR + 0x40:
      case APIC_TMR + 0x50:
      case APIC_TMR + 0x60:
      case APIC_TMR + 0x70:
            val = env->apic.tmr[(reg >> 4) & 7];
            break;

      case APIC_IRR + 0x00: /* 0x200 */
      case APIC_IRR + 0x10:
      case APIC_IRR + 0x20:
      case APIC_IRR + 0x30:
      case APIC_IRR + 0x40:
      case APIC_IRR + 0x50:
      case APIC_IRR + 0x60:
      case APIC_IRR + 0x70:
            val = env->apic.irr[(reg >> 4) & 7];
            break;

      case APIC_ESR: /* 0x280 */
            val = 0;
            val |= env->apic.illegal_register_address << 7;
            val |= env->apic.receive_illegal_vector << 6;
            val |= env->apic.send_illegal_vector << 5;
            /* Bit 4: reserved */
            val |= env->apic.receive_accept_error << 3;
            val |= env->apic.send_accept_error << 2;
            val |= env->apic.receive_cs_error << 1;
            val |= env->apic.send_cs_error << 0;
            break;

      case APIC_ICR: /* 0x300 */
            val = 0;
            val |= env->apic.icr1.shorthand << 18;
            val |= env->apic.icr1.trigger << 15;
            val |= env->apic.icr1.level << 14;
            val |= env->apic.icr1.delivery_status << 12;
            val |= env->apic.icr1.destination_mode << 11;
            val |= env->apic.icr1.delivery_mode << 8;
            val |= env->apic.icr1.vector << 0;
            break;

      case APIC_ICR2: /* 0x310 */
            val = 0;
            val |= env->apic.icr2.destination << 24;
            break;

      case APIC_LVTT: /* 0x320 */
            val = 0;
            val |= env->apic.lvt_timer.timer_mode << 17;
            val |= env->apic.lvt_timer.mask << 16;
            val |= env->apic.lvt_timer.delivery_status << 12;
            val |= env->apic.lvt_timer.vector << 0;
            break;

#if 4 <= CPU_APIC_MAX_LVT
      case APIC_LVTPC: /* 0x340 */
            val = 0;
            val |= env->apic.lvt_pc.mask << 16;
            val |= env->apic.lvt_pc.delivery_status << 12;
            val |= env->apic.lvt_pc.delivery_mode << 8;
            val |= env->apic.lvt_pc.vector << 0;
            break;
#endif
      case APIC_LVT0: /* 0x350 */
            val = 0;
            val |= env->apic.lvt_lint[0].mask << 16;
            val |= env->apic.lvt_lint[0].trigger << 15;
            val |= env->apic.lvt_lint[0].remote_irr << 14;
            val |= env->apic.lvt_lint[0].polarity << 13;
            val |= env->apic.lvt_lint[0].delivery_status << 12;
            val |= env->apic.lvt_lint[0].delivery_mode << 8;
            val |= env->apic.lvt_lint[0].vector << 0;
            break;

      case APIC_LVT1: /* 0x360 */
            val = 0;
            val |= env->apic.lvt_lint[1].mask << 16;
            val |= env->apic.lvt_lint[1].trigger << 15;
            val |= env->apic.lvt_lint[1].remote_irr << 14;
            val |= env->apic.lvt_lint[1].polarity << 13;
            val |= env->apic.lvt_lint[1].delivery_status << 12;
            val |= env->apic.lvt_lint[1].delivery_mode << 8;
            val |= env->apic.lvt_lint[1].vector << 0;
            break;

#if 3 <= CPU_APIC_MAX_LVT
      case APIC_LVTERR: /* 0x370 */
            val = 0;
            val |= env->apic.lvt_error.mask << 16;
            val |= env->apic.lvt_error.delivery_status << 12;
            val |= env->apic.lvt_error.vector << 0;
            break;
#endif

      case APIC_TMICT: /* 0x380 */
            val = env->apic.timer_icr;
            break;

      case APIC_TMCCT: /* 0x390 */
            NAME_(apic_timer_stop)();
            NAME_(apic_timer_update)();
            val = env->apic.timer_ccr;
            NAME_(apic_timer_start)();
            break;

      case APIC_TDCR: /* 0x3e0 */
            val = 0;
            val |= (((env->apic.timer_dcr - 1) >> 2) & 1) << 3;
            val |= (((env->apic.timer_dcr - 1) >> 0) & 3) << 0;
            break;

      default:
            assert(0); /* FIXME VOSSI */
      }

      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "APIC: Reading 0x%08lx from register 0x%03x\n",
                        (unsigned long) val, (unsigned int) reg);
      }

      return val;
}

static void
NAME_(_apic_write)(uint32_t reg, uint32_t val)
{
      int vec;
      unsigned int destination_mode;
      uint8_t destination;

      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "APIC: Writing 0x%08lx to register 0x%03x\n",
                        (unsigned long) val, (unsigned int) reg);
      }

      switch (reg) {
      case APIC_ID: /* 0x020 */
#if 1 /* Pentium and P6 family */
            env->apic.phys_apic_id = (val >> 24) & 0xf;
#elif 0     /* Pentium 4 and Xeon */
            env->apic.phys_apic_id = (val >> 24) & 0xff;
#endif
            break;

      case APIC_LVR: /* 0x030 */
            /* Read-only. */
            break;

      case APIC_TASKPRI: /* 0x080 */
            env->apic.tpr = (val >> 0) & 0xff;
            break;

      case APIC_ARBPRI: /* 0x090 */
            /* Read-only. */
            break;

      case APIC_PROCPRI: /* 0x0a0 */
            /* Read-only. */
            break;

      case APIC_EOI: /* 0x0b0 */
            vec = NAME_(apic_bit_find)(env->apic.isr, 256);
            if (0 <= vec) {
                  NAME_(apic_bit_clr)(env->apic.isr, vec);
                  if (NAME_(apic_bit_test)(env->apic.tmr, vec)) {
                        NAME_(apic_deliver_eoi)(vec);
                  }
            }

            NAME_(apic_irq_update)();
            break;

      case APIC_LDR: /* 0x0d0 */
            env->apic.ldr = (val >> 24) & 0xff;
            break;

      case APIC_DFR: /* 0x0e0 */
            env->apic.dfr_model = (val >> 28) & 0xf;
            break;

      case APIC_SPIV: /* 0x0f0 */
            val |= 0xf; /* Bit 3-0: 1111; read-only */
            env->apic.svr.focus_cpu = (val >> 9) & 1;
            env->apic.svr.apic_enabled = (val >> 8) & 1;
            env->apic.svr.spurious_vector = (val >> 0) & 0xff;
            break;

      case APIC_ISR + 0x00: /* 0x100 */
      case APIC_ISR + 0x10:
      case APIC_ISR + 0x20:
      case APIC_ISR + 0x30:
      case APIC_ISR + 0x40:
      case APIC_ISR + 0x50:
      case APIC_ISR + 0x60:
      case APIC_ISR + 0x70:
            /* Read-only. */
            break;

      case APIC_TMR + 0x00: /* 0x180 */
      case APIC_TMR + 0x10:
      case APIC_TMR + 0x20:
      case APIC_TMR + 0x30:
      case APIC_TMR + 0x40:
      case APIC_TMR + 0x50:
      case APIC_TMR + 0x60:
      case APIC_TMR + 0x70:
            /* Read-only. */
            break;

      case APIC_IRR + 0x00: /* 0x200 */
      case APIC_IRR + 0x10:
      case APIC_IRR + 0x20:
      case APIC_IRR + 0x30:
      case APIC_IRR + 0x40:
      case APIC_IRR + 0x50:
      case APIC_IRR + 0x60:
      case APIC_IRR + 0x70:
            /* Read-only. */
            break;

      case APIC_ESR: /* 0x280 */
            /* Read-only. */
            break;

      case APIC_ICR: /* 0x300 */
            env->apic.icr1.shorthand = (val >> 18) & 3;
            env->apic.icr1.trigger = (val >> 15) & 1;
            env->apic.icr1.level = (val >> 14) & 1;
            /* Writing to ICR1 always triggers an interrupt */
            env->apic.icr1.destination_mode = (val >> 11) & 1;
            env->apic.icr1.delivery_mode = (val >> 8) & 7;
            env->apic.icr1.vector = (val >> 0) & 0xff;

            switch (env->apic.icr1.shorthand) {
            case 0: /* No Shorthand */
                  destination_mode = env->apic.icr1.destination_mode;
                  destination = env->apic.icr2.destination;
                  break;
            case 1: /* Self */
            case 2: /* All Including Self */
            case 3: /* All Excluding Self */
                  destination_mode = 0;
                  destination = 0xff;
                  break;
            default:
                  assert(0); /* Cannot happen. */
            }

            env->apic.icr1.delivery_status = 1;

            if (env->apic.icr1.shorthand != 3) {
                  /* Send IRQ to own APIC. */
                  NAME_(apic_msg0_receive)(
                              destination_mode,
                              env->apic.icr1.delivery_mode, 
                              env->apic.icr1.level, 
                              env->apic.icr1.trigger, 
                              env->apic.icr1.vector,
                              destination);
            }
            if (env->apic.icr1.shorthand != 1) {
                  /* Send IRQ to other APICs. */
                  sig_icc_bus_msg0(env->icc_bus, env,
                              destination_mode,
                              env->apic.icr1.delivery_mode, 
                              env->apic.icr1.level, 
                              env->apic.icr1.trigger, 
                              env->apic.icr1.vector,
                              destination);
            }

            env->apic.icr1.delivery_status = 0;
            break;

      case APIC_ICR2: /* 0x310 */
            env->apic.icr2.destination = (val >> 24) & 0xff;
            break;

      case APIC_LVTT: /* 0x320 */
            env->apic.lvt_timer.timer_mode = (val >> 17) & 1;
            env->apic.lvt_timer.mask = (val >> 16) & 1;
            /* Bit 12 (delivery status) is read only! */
            env->apic.lvt_timer.vector = (val >> 0) & 0xff;
            break;

#if 4 <= CPU_APIC_MAX_LVT
      case APIC_LVTPC: /* 0x340 */
            env->apic.lvt_pc.mask = (val >> 16) & 1;
            /* Bit 12 (delivery status) is read only! */
            env->apic.lvt_pc.delivery_mode = (val >> 8) & 7;
            env->apic.lvt_pc.vector = (val >> 0) & 0xff;
            break;
#endif

      case APIC_LVT0: /* 0x350 */
            env->apic.lvt_lint[0].mask = (val >> 16) & 1;
            env->apic.lvt_lint[0].trigger = (val >> 15) & 1;
            env->apic.lvt_lint[0].remote_irr = (val >> 14) & 1;
            env->apic.lvt_lint[0].polarity = (val >> 13) & 1;
            /* Bit 12 (delivery status) is read only! */
            env->apic.lvt_lint[0].delivery_mode = (val >> 8) & 7;
            env->apic.lvt_lint[0].vector = (val >> 0) & 0xff;
            break;

      case APIC_LVT1: /* 0x360 */
            env->apic.lvt_lint[1].mask = (val >> 16) & 1;
            env->apic.lvt_lint[1].trigger = (val >> 15) & 1;
            env->apic.lvt_lint[1].remote_irr = (val >> 14) & 1;
            env->apic.lvt_lint[1].polarity = (val >> 13) & 1;
            /* Bit 12 (delivery status) is read only! */
            env->apic.lvt_lint[1].delivery_mode = (val >> 8) & 7;
            env->apic.lvt_lint[1].vector = (val >> 0) & 0xff;
            break;

#if 3 <= CPU_APIC_MAX_LVT
      case APIC_LVTERR: /* 0x370 */
            env->apic.lvt_error.mask = (val >> 16) & 1;
            /* Bit 12 (delivery status) is read only! */
            env->apic.lvt_error.vector = (val >> 0) & 0xff;
            break;
#endif

      case APIC_TMICT: /* 0x380 */
            /* Writing to TMICT starts the timer */
            NAME_(apic_timer_stop)();
            env->apic.timer_event = time_virt();
            env->apic.timer_icr = val;
            env->apic.timer_ccr = val;
            NAME_(apic_timer_start)();
            break;

      case APIC_TMCCT: /* 0x390 */
            /* Read-only. */
            break;

      case APIC_TDCR: /* 0x3e0 */
            /*
             * 0000 -> 1
             * 0001 -> 2
             * 0010 -> 3
             * 0011 -> 4
             * 1000 -> 5
             * 1001 -> 6
             * 1010 -> 7
             * 1011 -> 0
             */
            val |= (val >> 1) & 4;
            val += 1;
            val &= 7;
            env->apic.timer_dcr = val;
            break;

      default:
            assert(0); /* FIXME VOSSI */
      }
}

void
NAME_(apic_base_msr_set)(uint64_t val)
{
      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "APIC: Writing 0x%08lx to msr\n",
                        (unsigned long) val);
      }

      if (env->apic.apic_enable) {
            /* Invalidate old mapping. */
            NAME_(mmu_unmap_range)(env->apic.base, 0x1000);
      }

      env->apic.base = val & ~0xfff;
      env->apic.apic_enable = (val >> 11) & 1;

      if (env->apic.apic_enable) {
            /* Register new mapping. */
            NAME_(mmu_unmap_range)(env->apic.base, 0x1000);
      }
}

uint64_t
NAME_(apic_base_msr_get)(void)
{
      uint64_t val;

      val = env->apic.base
            | (env->apic.apic_enable << 11)
            | (env->apic.bsp << 8);

      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "APIC: Reading 0x%08lx from msr\n",
                        (unsigned long) val);
      }

      return val;
}

void
NAME_(set_apic_tpr)(uint8_t val)
{
      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "APIC: Writing 0x%02x to tpr\n",
                        (unsigned int) val);
      }

      env->apic.tpr = (val & 0x0f) << 4;
      NAME_(apic_irq_update)();
}

uint8_t
NAME_(get_apic_tpr)(void)
{
      uint8_t val;
      
      val = env->apic.tpr >> 4;

      if (DEBUG_CONTROL_FLOW) {
            fprintf(stderr, "APIC: Reading 0x%02x from tpr\n",
                        (unsigned int) val);
      }

      return val;
}

int
NAME_(apic_read)(
      unsigned long pa,
      void *to,
      unsigned long len
)
{
      if (env->apic.base != (pa & ~0xfff)) {
            /* Wrong address range. */
            return -1;
      }

      if ((pa & 0xf) != 0
       || len != 4) {
            /*
             * "All registers must be accessed using 128-bit aligned
             * 32-bit loads or stores."
             */
            faum_log(FAUM_LOG_INFO, "APIC", "",
                        "Reading %ld byte(s) from address 0x%03lx.\n",
                        len, pa & 0xfff);
            memset(to, 0, len); /* FIXME */
            return 0;
      }

      *(uint32_t *) to = NAME_(_apic_read)(pa & 0xff0);
      return 0;
}

int
NAME_(apic_write)(
      unsigned long pa,
      const void *from,
      unsigned long len
)
{
      if (env->apic.base != (pa & ~0xfff)) {
            /* Wrong address range. */
            return -1;
      }

      if ((pa & 0xf) != 0
       || len != 4) {
            /*
             * "All registers must be accessed using 128-bit aligned
             * 32-bit loads or stores."
             */
            faum_log(FAUM_LOG_INFO, "APIC", "",
                        "Writing %ld byte(s) to address 0x%03lx.\n",
                        len, pa & 0xfff);
            return 0;
      }

      NAME_(_apic_write)(pa & 0xff0, *(const uint32_t *) from);
      return 0;
}

int
NAME_(apic_map)(
      unsigned long pa,
      uint32_t **haddrp,
      unsigned long *lenp,
      int *wflagp
) {
      if ((env->apic.base & ~0xfff) == (pa & ~0xfff)) {
            /* APIC mustn't be mapped. Simulate access instead. */
            *haddrp = NULL;
            *lenp = 0x1000;
            *wflagp = 1;

            return 0;

      } else {
            return 1;
      }
}

void
NAME_(apic_reset)(void)
{
      env->apic.phys_apic_id = env->apic_arbitration_id;
      env->apic.dfr_model = 0xF;
      env->apic.svr.spurious_vector = 0xff;
      env->apic.svr.apic_enabled = 0;
      env->apic.svr.focus_cpu = 1;
      env->apic.lvt_timer.timer_mode = 0;
      env->apic.lvt_timer.mask = 1;
      env->apic.lvt_timer.vector = 0x00;
      env->apic.lvt_pc.mask = 1;
      env->apic.lvt_pc.delivery_mode = 0;
      env->apic.lvt_pc.vector = 0x00;
      env->apic.lvt_lint[0].mask = 1;
      env->apic.lvt_lint[0].trigger = 0;
      env->apic.lvt_lint[0].polarity = 0;
      env->apic.lvt_lint[0].delivery_mode = 0;
      env->apic.lvt_lint[0].vector = 0x00;
      env->apic.lvt_lint[1].mask = 1;
      env->apic.lvt_lint[1].trigger = 0;
      env->apic.lvt_lint[1].polarity = 0;
      env->apic.lvt_lint[1].delivery_mode = 0;
      env->apic.lvt_lint[1].vector = 0x00;
      env->apic.lvt_error.mask = 1;
      env->apic.lvt_error.vector = 0x00;
      env->apic.base = 0xfee00000;
      env->apic.apic_enable = 1;

      env->apic.timer_event = 0;
      env->apic.timer_running = 0;

      env->apic.tsc_to_bus = TIME_HZ / SIG_HOST_BUS_HZ;
}

void
NAME_(apic_init)(struct cpu *css)
{
}

void
NAME_(apic_create)(struct cpu *css)
{
      static int count = 0;

      css->apic.bsp = 1;
      count++;
}

void
NAME_(apic_destroy)(struct cpu *css)
{
      /* Don't use css! See cpu.c -- FIXME */
}

#else /* 80486 <= CONFIG_CPU && CONFIG_CPU_APIC_SUPPORT */

static void
NAME_(apic_lint0_set)(unsigned int val)
{
      if (val) {
            env->interrupt_request |= CPU_INTERRUPT_IRQ;
            NAME_(interrupt)();
      } else {
            env->interrupt_request &= ~CPU_INTERRUPT_IRQ;
      }
}

static void
NAME_(apic_lint1_set)(unsigned int val)
{
      if (val) {
            env->interrupt_request |= CPU_INTERRUPT_NMI;
            NAME_(interrupt)();
      }
}

#if 80386 <= CONFIG_CPU
static void
NAME_(apic_smi_set)(unsigned int val)
{
      if (val) {
            env->interrupt_request |= CPU_INTERRUPT_SMI;
            NAME_(interrupt)();
      }
}
#endif

int
NAME_(apic_irq_ack)(void)
{
      uint8_t vec;

      NAME_(ack)(&vec);

      return vec;
}

void
NAME_(apic_nmi_ack)(void)
{
      env->interrupt_request &= ~CPU_INTERRUPT_NMI;
}

#if 80386 <= CONFIG_CPU
void
NAME_(apic_smi_ack)(void)
{
      env->interrupt_request &= ~CPU_INTERRUPT_SMI;
}
#endif

void
NAME_(apic_reset)(void) { }
void
NAME_(apic_init)(struct cpu *css) { }
void
NAME_(apic_create)(struct cpu *css) { }
void
NAME_(apic_destroy)(struct cpu *css) { }

#endif /* 80486 <= CONFIG_CPU && CONFIG_CPU_APIC_SUPPORT */

#undef DEBUG_CONTROL_FLOW

Generated by  Doxygen 1.6.0   Back to index