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; }
- typedef enum
- {
- APIC_NONE,
- APIC_P6, // Intel PPro, PIII
- APIC_P4, // Intel PIV
- APIC_K7 // AMD Athlon Model 2
- } Apic_type;
+ 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;
static const Address io_base;
static Address phys_base;
static unsigned timer_divisor;
- static Apic_type type;
static unsigned frequency_khz;
static Unsigned64 scaler_us_to_apic;
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]:
const Address Apic::io_base = Mem_layout::Local_apic_page;
Address Apic::phys_base;
unsigned Apic::timer_divisor = 1;
-Apic::Apic_type Apic::type = APIC_NONE;
unsigned Apic::frequency_khz;
Unsigned64 Apic::scaler_us_to_apic;
Unsigned32
Apic::get_num_errors()
{
+ reg_write(APIC_esr, 0);
return reg_read(APIC_esr);
}
Apic::clear_num_errors()
{
reg_write(APIC_esr, 0);
+ reg_write(APIC_esr, 0);
}
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()
{
Address offs;
- Address base = Cpu::rdmsr(APIC_base_msr) & 0xfffff000;
+ Address base = apic_page_phys();
// We should not change the physical address of the Local APIC page if
// possible since some versions of VMware would complain about a
// non-implemented feature
}
// check CPU type if APIC could be present
-static FIASCO_INIT
+static FIASCO_INIT_AND_PM
int
Apic::test_cpu()
{
if (cpu().vendor() == Cpu::Vendor_intel)
{
if (cpu().family() == 15)
- {
- // Intel PIV
- type = APIC_P4;
- return 1;
- }
- else if (cpu().family() >= 6)
- {
- // Intel PPro, PIII
- type = APIC_P6;
- return 1;
- }
+ return 1;
+ if (cpu().family() >= 6)
+ return 1;
}
if (cpu().vendor() == Cpu::Vendor_amd && cpu().family() >= 6)
- {
- // >= AMD K7 Model 2
- type = APIC_K7;
- return 1;
- }
+ return 1;
return 0;
}
{
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()
{
if (get_max_lvt() > 3)
clear_num_errors();
after = get_num_errors();
- printf("APIC ESR value before/after enabling: %08x/%08x\n",
+ printf("APIC ESR value before/after enabling: %08x/%08x\n",
before, after);
}
}
// 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
// yes, succesful
return 1;
} while (Cpu::rdtsc() < tsc_until);
-
+
// timeout
return 0;
}
return present;
}
-PUBLIC static
-inline int
-Apic::cpu_type()
-{
- return type;
-}
-
PUBLIC static
void
Apic::set_perf_nmi()
reg_write(APIC_lvtpc, 0x400);
}
-static FIASCO_INIT_CPU
+static FIASCO_INIT_CPU_AND_PM
void
Apic::calibrate_timer()
{
Unsigned32 count, tt1, tt2, result, dummy;
Unsigned32 runs = 0, frequency_ok;
- do
+ do
{
frequency_khz = 0;
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("APIC invalid register access error at "L4_PTR_FMT"\n",
- 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("APIC error %08x(%08x)\n", 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()
{
{
printf("Local APIC[%02x]: version=%02x max_lvt=%d\n",
get_id() >> 24, get_version(), get_max_lvt());
-
}
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();
- good_cpu = test_cpu();
+ if (!was_present)
+ {
+ good_cpu = test_cpu();
+
+ if (good_cpu && Config::apic)
+ {
+ // activate; this could lead an disabled APIC to appear
+ // set base address of I/O registers to be able to access the registers
+ activate_by_msr();
+ present = test_present();
+ }
+ }
if (!Config::apic)
return;
- // check CPU type
- if ((present = good_cpu))
+ // initialize if available
+ if (present)
{
- // check cpu features of cpuid
- was_present = test_present();
-
- // activate; this could lead an disabled APIC to appear
- // set base address of I/O registers to be able to access the registers
- activate_by_msr();
+ // map the Local APIC device registers
+ if (!resume)
+ map_apic_page();
- // previous test_present() could have failed but we could have it
- // activated by writing the msr so we have to test again
- if ((present = test_present()))
- {
- // map the Local APIC device registers
- map_apic_page();
+ // set some interrupt vectors to appropriate values
+ init_lvt();
- // set some interrupt vectors to appropriate values
- init_lvt();
+ // initialize APIC_spiv register
+ init_spiv();
- // initialize APIC_spiv register
- init_spiv();
+ // initialize task-priority register
+ init_tpr();
- // initialize task-priority register
- init_tpr();
-
- // test if local timer counts down
- if ((present = check_working()))
- {
- if (!was_present)
- // APIC _was_ not present before writing to msr so we have
- // to set APIC_lvt0 and APIC_lvt1 to appropriate values
- route_pic_through_apic();
- }
- }
+ // test if local timer counts down
+ if ((present = check_working()))
+ {
+ if (!was_present)
+ // APIC _was_ not present before writing to msr so we have
+ // to set APIC_lvt0 and APIC_lvt1 to appropriate values
+ route_pic_through_apic();
+ }
}
if (!present)