diff --git a/common/cmdline.c b/common/cmdline.c index bbc48692..d5e50e86 100644 --- a/common/cmdline.c +++ b/common/cmdline.c @@ -58,6 +58,9 @@ bool_cmd("qemu_console", opt_qemu_console); bool opt_poweroff = true; bool_cmd("poweroff", opt_poweroff); +bool opt_power_button = false; +bool_cmd("power_button", opt_power_button); + static char opt_com1[20]; string_cmd("com1", opt_com1); diff --git a/common/cpu.c b/common/cpu.c index 1bd0dc85..8094ab4a 100644 --- a/common/cpu.c +++ b/common/cpu.c @@ -49,6 +49,7 @@ static void init_cpu(cpu_t *cpu, unsigned int id, bool is_bsp, bool enabled) { cpu->percpu = get_percpu_page(id); BUG_ON(!cpu->percpu); + cpu->percpu->cpu = cpu; cpu->lock = SPINLOCK_INIT; list_init(&cpu->task_queue); diff --git a/common/setup.c b/common/setup.c index cae1a7b9..1185c12e 100644 --- a/common/setup.c +++ b/common/setup.c @@ -55,6 +55,7 @@ #include #include #include +#include #include #include @@ -283,6 +284,9 @@ void __noreturn __text_init kernel_start(uint32_t multiboot_magic, unsigned long enable_fpu(); } + if (opt_power_button) + init_power_button(); + #ifdef KTF_PMU printk("Initializing PFM library\n"); diff --git a/drivers/power_button.c b/drivers/power_button.c new file mode 100644 index 00000000..c16edd57 --- /dev/null +++ b/drivers/power_button.c @@ -0,0 +1,68 @@ +#include +#include + +static void default_handler(void *); + +/** power button press handler */ +static pb_handler_t pb_handler = default_handler; +/** context passed to the power button press handler */ +static void *pb_context = NULL; + +#ifdef KTF_ACPICA +static UINT32 button_handler(void *Context) { + if (ACPI_FAILURE(AcpiClearEvent(ACPI_EVENT_POWER_BUTTON))) + panic("PWRB: Failed to clear power button event"); + + if (pb_handler) + pb_handler(pb_context); + + return ACPI_INTERRUPT_HANDLED; +} +#endif + +static void default_handler(void *notused) { +#ifdef KTF_ACPICA + acpi_power_off(); +#endif +} + +void pb_set_handler(pb_handler_t handler, void *context) { + // check that we are on the bsp. Assumes that there is no task migration + ASSERT(is_cpu_bsp(get_this_cpu())); + + unsigned long flags = interrupts_disable_save(); + pb_handler = handler; + pb_context = context; + interrupts_restore(flags); +} + +bool init_power_button() { +#ifdef KTF_ACPICA + ACPI_TABLE_FADT *fadt = acpi_find_table(ACPI_SIG_FADT); + + if (!(fadt->Flags & ACPI_FADT_POWER_BUTTON)) { + printk("PWRB: Configuring ACPI 'fixed' power button handling\n"); + + if (ACPI_FAILURE(AcpiClearEvent(ACPI_EVENT_POWER_BUTTON))) { + warning("PWRB: Failed to clear power button event"); + return false; + } + + if (ACPI_FAILURE(AcpiInstallFixedEventHandler(ACPI_EVENT_POWER_BUTTON, + button_handler, NULL))) { + warning("PWRB: Failed to install power button handler"); + return false; + } + } + else { + warning("PWRB: Non-fixed power button not implemented\n"); + return false; + } + + return true; +#else + warning("PWRB: Power button without ACPICA not implemented\n"); + + return false; +#endif +} diff --git a/include/cmdline.h b/include/cmdline.h index 9b1fba1e..9fba50c2 100644 --- a/include/cmdline.h +++ b/include/cmdline.h @@ -67,6 +67,7 @@ extern bool opt_hpet; extern bool opt_fpu; extern bool opt_qemu_console; extern bool opt_poweroff; +extern bool opt_power_button; extern bool opt_fb_scroll; extern unsigned long opt_reboot_timeout; extern bool opt_tlb_global; diff --git a/include/cpu.h b/include/cpu.h index 31b39d2f..f7c7eeb6 100644 --- a/include/cpu.h +++ b/include/cpu.h @@ -67,6 +67,10 @@ extern void wait_for_all_cpus(void); /* Static declarations */ +static inline cpu_t *get_this_cpu() { + return PERCPU_GET(cpu); +} + static inline bool is_cpu_bsp(cpu_t *cpu) { return cpu->flags.bsp; } diff --git a/include/drivers/power_button.h b/include/drivers/power_button.h new file mode 100644 index 00000000..32a12fa0 --- /dev/null +++ b/include/drivers/power_button.h @@ -0,0 +1,18 @@ +#ifndef KTF_POWER_BUTTON_H +#define KTF_POWER_BUTTON_H + +typedef void (*pb_handler_t)(void *); + +/** + * Set a handler for the power button. Useful to control experiments. + * + * Note: This function MUST be called from the bsp to ensure consistency! + */ +void pb_set_handler(pb_handler_t handler, void *context); + +/** + * Initialize power button handling. Requires ACPICA library. + */ +extern bool init_power_button(); + +#endif /* KTF_POWER_BUTTON_H */ diff --git a/include/percpu.h b/include/percpu.h index f6029ec5..7563e38a 100644 --- a/include/percpu.h +++ b/include/percpu.h @@ -31,6 +31,7 @@ #include #include +struct cpu; struct percpu { list_head_t list; @@ -60,6 +61,7 @@ struct percpu { unsigned long usermode_private; volatile unsigned long apic_ticks; bool apic_timer_enabled; + struct cpu *cpu; } __aligned(PAGE_SIZE); typedef struct percpu percpu_t;