INTERFACE:
+#include "per_cpu_data.h"
#include "types.h"
#include "initcalls.h"
+#include "pm.h"
class Return_frame;
class Cpu;
-class Apic
+class Apic : public Pm_object
{
public:
- static void init() FIASCO_INIT;
+ static void init(bool resume = false) FIASCO_INIT_AND_PM;
+ Unsigned32 apic_id() const { return _id; }
+ Unsigned32 cpu_id() const { return _id >> 24; }
+
+ static Per_cpu<Static_object<Apic> > apic;
private:
- Apic(); // default constructors are undefined
- Apic(const Apic&);
+ Apic(const Apic&) = delete;
+ Apic &operator = (Apic const &) = delete;
+
+ Unsigned32 _id;
+ Unsigned32 _saved_apic_timer;
+
static void error_interrupt(Return_frame *regs)
asm ("apic_error_interrupt") FIASCO_FASTCALL;
APIC_tpri_mask = 0xFF,
APIC_eoi = 0xB0,
APIC_ldr = 0xD0,
- APIC_ldr_mask = (0xFF<<24),
+ APIC_ldr_mask = 0xFFul << 24,
APIC_dfr = 0xE0,
APIC_spiv = 0xF0,
APIC_isr = 0x100,
APIC_tmcct = 0x390,
APIC_tdcr = 0x3E0,
- APIC_snd_pending = (1<<12),
- APIC_input_polarity = (1<<13),
- APIC_lvt_remote_irr = (1<<14),
- APIC_lvt_level_trigger = (1<<15),
- APIC_lvt_masked = (1<<16),
- APIC_lvt_timer_periodic = (1<<17),
+ APIC_snd_pending = 1 << 12,
+ APIC_input_polarity = 1 << 13,
+ APIC_lvt_remote_irr = 1 << 14,
+ APIC_lvt_level_trigger = 1 << 15,
+ APIC_lvt_masked = 1 << 16,
+ APIC_lvt_timer_periodic = 1 << 17,
APIC_tdr_div_1 = 0xB,
APIC_tdr_div_2 = 0x0,
APIC_tdr_div_4 = 0x1,
IMPLEMENTATION:
#include "cpu.h"
+DEFINE_PER_CPU Per_cpu<Static_object<Apic> > Apic::apic;
+
+PUBLIC inline
+Apic::Apic(Cpu_number cpu) : _id(get_id()) { register_pm(cpu); }
+
+
PRIVATE static
Cpu &
Apic::cpu() { return *Cpu::boot_cpu(); }
+// FIXME: workaround for missing lambdas in gcc < 4.5
+namespace {
+struct By_id
+{
+ Unsigned32 p;
+ By_id(Unsigned32 p) : p(p) {}
+ bool operator () (Apic const *a) const { return a && a->apic_id() == p; }
+};
+}
+
+PUBLIC static
+Cpu_number
+Apic::find_cpu(Unsigned32 phys_id)
+{
+ return apic.find_cpu(By_id(phys_id));
+}
+
+PUBLIC void
+Apic::pm_on_suspend(Cpu_number)
+{
+ _saved_apic_timer = timer_reg_read();
+}
+
+//----------------------------------------------------------------------------
+IMPLEMENTATION [!mp]:
+
+PUBLIC void
+Apic::pm_on_resume(Cpu_number)
+{
+ Apic::init(true);
+ timer_reg_write(_saved_apic_timer);
+}
+
+//----------------------------------------------------------------------------
+IMPLEMENTATION [mp]:
+
+PUBLIC void
+Apic::pm_on_resume(Cpu_number cpu)
+{
+ if (cpu == Cpu_number::boot_cpu())
+ Apic::init(true);
+ else
+ Apic::init_ap();
+ timer_reg_write(_saved_apic_timer);
+}
+
//----------------------------------------------------------------------------
IMPLEMENTATION[ia32]:
Unsigned32
Apic::reg_read(unsigned reg)
{
- return *((volatile Unsigned32*)(io_base + reg));
+ Unsigned32 val;
+ /* assembly-encoded to match the Jailhouse hypervisor MMIO parser support */
+ void *address = (void*)(/*(Unsigned32*)*/(io_base + reg));
+ asm volatile("mov (%1),%0" : "=r" (val) : "r" (address));
+ return val;
}
PUBLIC static inline
void
Apic::reg_write(unsigned reg, Unsigned32 val)
{
- *((volatile Unsigned32*)(io_base + reg)) = val;
+ /* assembly-encoded to match the Jailhouse hypervisor MMIO parser support */
+ void *address = (void*)(/*(Unsigned32*)*/(io_base + reg));
+ asm volatile("mov %0,(%1)" : : "r" (val), "r" (address));
}
PUBLIC static inline
// set the global pagetable entry for the Local APIC device registers
PUBLIC
-static
+static FIASCO_INIT_AND_PM
void
Apic::map_apic_page()
{
}
// check CPU type if APIC could be present
-static FIASCO_INIT
+static FIASCO_INIT_AND_PM
int
Apic::test_cpu()
{
{
if (divval & ~1)
{
- printf("bad APIC divisor %d\n", newdiv);
+ printf("bad APIC divisor %u\n", newdiv);
return;
}
div = divisor_tab[i];
}
// check if APIC is working (check timer functionality)
-static FIASCO_INIT
+static FIASCO_INIT_AND_PM
int
Apic::check_working()
{
timer_disable_irq();
timer_set_divisor(1);
timer_reg_write(0x10000000);
-
- tsc_until = Cpu::rdtsc() + 0x100; // we only have to wait for one bus cycle
- do
+ tsc_until = Cpu::rdtsc() + 0x400; // we only have to wait for one bus cycle
+
+ do
{
if (timer_reg_read() != 0x10000000)
- return 1;
+ return 1;
} while (Cpu::rdtsc() < tsc_until);
return 0;
}
-static FIASCO_INIT_CPU
+static FIASCO_INIT_CPU_AND_PM
void
Apic::init_spiv()
{
Unsigned32 tmp_val;
-
+
tmp_val = reg_read(APIC_spiv);
tmp_val |= (1<<8); // enable APIC
tmp_val &= ~(1<<9); // enable Focus Processor Checking
Apic::tpr()
{ return reg_read(APIC_tpr); }
-static FIASCO_INIT_CPU
+static FIASCO_INIT_CPU_AND_PM
void
Apic::init_tpr()
{
- reg_write (APIC_tpr, 0);
+ reg_write(APIC_tpr, 0);
}
// activate APIC error interrupt
-static FIASCO_INIT_CPU
+static FIASCO_INIT_CPU_AND_PM
void
Apic::enable_errors()
{
// activate APIC after activating by MSR was successful
// see "Intel Architecture Software Developer's Manual,
// Volume 3: System Programming Guide, Appendix E"
-static FIASCO_INIT
+static FIASCO_INIT_AND_PM
void
Apic::route_pic_through_apic()
{
Unsigned32 tmp_val;
- Lock_guard <Cpu_lock> guard(&cpu_lock);
+ auto guard = lock_guard(cpu_lock);
// mask 8259 interrupts
- Pic::Status old_irqs = Pic::disable_all_save();
+ Unsigned16 old_irqs = Pic::disable_all_save();
// set LINT0 to ExtINT, edge triggered
tmp_val = reg_read(APIC_lvt0);
tmp_val &= 0xfffe5800;
tmp_val |= 0x00000700;
reg_write(APIC_lvt0, tmp_val);
-
+
// set LINT1 to NMI, edge triggered
tmp_val = reg_read(APIC_lvt1);
tmp_val &= 0xfffe5800;
printf("APIC was disabled --- routing PIC through APIC\n");
}
-static FIASCO_INIT_CPU
+static FIASCO_INIT_CPU_AND_PM
void
Apic::init_lvt()
{
- Unsigned32 tmp_val;
- Lock_guard <Cpu_lock> guard(&cpu_lock);
+ auto guard = lock_guard(cpu_lock);
// mask timer interrupt and set vector to _not_ invalid value
- tmp_val = reg_read(APIC_lvtt);
- tmp_val |= APIC_lvt_masked;
- tmp_val |= 0xff;
- reg_write(APIC_lvtt, tmp_val);
+ reg_write(APIC_lvtt, reg_read(APIC_lvtt) | APIC_lvt_masked | 0xff);
+
if (have_pcint())
- {
- // mask performance interrupt and set vector to a valid value
- tmp_val = reg_read(APIC_lvtpc);
- tmp_val |= APIC_lvt_masked;
- tmp_val |= 0xff;
- reg_write(APIC_lvtpc, tmp_val);
- }
+ // mask performance interrupt and set vector to a valid value
+ reg_write(APIC_lvtpc, reg_read(APIC_lvtpc) | APIC_lvt_masked | 0xff);
+
if (have_tsint())
- {
- // mask thermal sensor interrupt and set vector to a valid value
- tmp_val = reg_read(APIC_lvtthmr);
- tmp_val |= APIC_lvt_masked;
- tmp_val |= 0xff;
- reg_write(APIC_lvtthmr, tmp_val);
- }
+ // mask thermal sensor interrupt and set vector to a valid value
+ reg_write(APIC_lvtthmr, reg_read(APIC_lvtthmr) | APIC_lvt_masked | 0xff);
}
// give us a hint if we have an APIC but it is disabled
}
// activate APIC by writing to appropriate MSR
-static FIASCO_INIT_CPU
+static FIASCO_INIT_CPU_AND_PM
void
Apic::activate_by_msr()
{
Cpu::wrmsr(msr, APIC_base_msr);
// now the CPU feature flags may have changed
- cpu().identify();
+ cpu().update_features_info();
}
// check if we still receive interrupts after we changed the IRQ routing
reg_write(APIC_lvtpc, 0x400);
}
-static FIASCO_INIT_CPU
+static FIASCO_INIT_CPU_AND_PM
void
Apic::calibrate_timer()
{
timer_reg_write(1000000000);
{
- Lock_guard <Cpu_lock> guard(&cpu_lock);
+ auto guard = lock_guard(cpu_lock);
Pit::setup_channel2_to_20hz();
if (ignore_invalid_apic_reg_access)
return;
- printf("cpu%d: APIC invalid register access error at "L4_PTR_FMT"\n",
- current_cpu(), regs->ip());
+ printf("CPU%u: APIC invalid register access error at " L4_PTR_FMT "\n",
+ cxx::int_value<Cpu_number>(current_cpu()), regs->ip());
return;
}
apic_error_cnt++;
- printf("cpu%d: APIC error %08x(%08x)\n", current_cpu(), err1, err2);
+ printf("CPU%u: APIC error %08x(%08x)\n",
+ cxx::int_value<Cpu_number>(current_cpu()), err1, err2);
}
// deactivate APIC by writing to appropriate MSR
Cpu::wrmsr(val, APIC_base_msr);
}
-PRIVATE static FIASCO_INIT_CPU
+PRIVATE static FIASCO_INIT_CPU_AND_PM
void
Apic::init_timer()
{
IMPLEMENT
void
-Apic::init()
+Apic::init(bool resume)
{
int was_present;
+ // FIXME: reset cached CPU features, we should add a special function
+ // for the apic bit
+ if(resume)
+ cpu().update_features_info();
was_present = present = test_present();
if (present)
{
// map the Local APIC device registers
- map_apic_page();
+ if (!resume)
+ map_apic_page();
// set some interrupt vectors to appropriate values
init_lvt();