diff options
Diffstat (limited to 'roms/skiboot/include/cpu.h')
-rw-r--r-- | roms/skiboot/include/cpu.h | 334 |
1 files changed, 334 insertions, 0 deletions
diff --git a/roms/skiboot/include/cpu.h b/roms/skiboot/include/cpu.h new file mode 100644 index 000000000..b0c78ce62 --- /dev/null +++ b/roms/skiboot/include/cpu.h @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: Apache-2.0 +/* Copyright 2013-2019 IBM Corp. */ + +#ifndef __CPU_H +#define __CPU_H + +#include <processor.h> +#include <ccan/list/list.h> +#include <lock.h> +#include <device.h> +#include <opal.h> +#include <stack.h> +#include <timer.h> + +/* + * cpu_thread is our internal structure representing each + * thread in the system + */ + +enum cpu_thread_state { + cpu_state_no_cpu = 0, /* Nothing there */ + cpu_state_unknown, /* In PACA, not called in yet */ + cpu_state_unavailable, /* Not available */ + cpu_state_fast_reboot_entry, /* Called back into OPAL, real mode */ + cpu_state_present, /* Assumed to spin in asm entry */ + cpu_state_active, /* Secondary called in */ + cpu_state_os, /* Under OS control */ + cpu_state_disabled, /* Disabled by us due to error */ + cpu_state_rvwinkle, /* Doing an rvwinkle cycle */ +}; + +struct cpu_job; +struct xive_cpu_state; + +struct cpu_thread { + /* + * "stack_guard" must be at offset 0 to match the + * -mstack-protector-guard-offset=0 statement in the Makefile + */ + uint64_t stack_guard; + uint32_t pir; + uint32_t server_no; + uint32_t chip_id; + bool is_secondary; + bool is_fused_core; + struct cpu_thread *primary; + struct cpu_thread *ec_primary; + enum cpu_thread_state state; + struct dt_node *node; + struct trace_info *trace; + uint64_t save_r1; + void *icp_regs; + uint32_t in_opal_call; + uint32_t quiesce_opal_call; + uint64_t entered_opal_call_at; + uint32_t con_suspend; + struct list_head locks_held; + bool con_need_flush; + bool in_mcount; + bool in_poller; + bool in_reinit; + bool in_fast_sleep; + bool in_sleep; + bool in_idle; + uint32_t hbrt_spec_wakeup; /* primary only */ + uint64_t save_l2_fir_action1; + uint64_t current_token; +#ifdef STACK_CHECK_ENABLED + int64_t stack_bot_mark; + uint64_t stack_bot_pc; + uint64_t stack_bot_tok; +#define CPU_BACKTRACE_SIZE 60 + struct bt_entry stack_bot_bt[CPU_BACKTRACE_SIZE]; + struct bt_metadata stack_bot_bt_metadata; +#endif + struct lock job_lock; + struct list_head job_queue; + uint32_t job_count; + bool job_has_no_return; + /* + * Per-core mask tracking for threads in HMI handler and + * a cleanup done bit. + * [D][TTTTTTTT] + * + * The member 'core_hmi_state' is primary only. + * The 'core_hmi_state_ptr' member from all secondry cpus will point + * to 'core_hmi_state' member in primary cpu. + */ + uint32_t core_hmi_state; /* primary only */ + uint32_t *core_hmi_state_ptr; + bool tb_invalid; + bool tb_resynced; + + /* For use by XICS emulation on XIVE */ + struct xive_cpu_state *xstate; + + /* + * For direct controls scoms, including special wakeup. + */ + struct lock dctl_lock; /* primary only */ + bool dctl_stopped; /* per thread */ + uint32_t special_wakeup_count; /* primary */ + + /* + * For reading DTS sensors async + */ + struct lock dts_lock; + struct timer dts_timer; + __be64 *sensor_data; + u32 sensor_attr; + u32 token; + bool dts_read_in_progress; + +#ifdef DEBUG_LOCKS + /* The lock requested by this cpu, used for deadlock detection */ + struct lock *requested_lock; +#endif +}; + +/* This global is set to 1 to allow secondaries to callin, + * typically set after the primary has allocated the cpu_thread + * array and stacks + */ +extern unsigned long cpu_secondary_start; + +/* Max PIR in the system */ +extern unsigned int cpu_max_pir; + +/* Max # of threads per core */ +extern unsigned int cpu_thread_count; + +/* Boot CPU. */ +extern struct cpu_thread *boot_cpu; + +extern void __nomcount cpu_relax(void); + +/* Initialize CPUs */ +void pre_init_boot_cpu(void); +void init_boot_cpu(void); +void init_cpu_max_pir(void); +void init_all_cpus(void); + +/* This brings up our secondaries */ +extern void cpu_bringup(void); + +/* This is called by secondaries as they call in */ +extern void cpu_callin(struct cpu_thread *cpu); + +/* For cpus which fail to call in. */ +extern void cpu_remove_node(const struct cpu_thread *t); + +/* Find CPUs using different methods */ +extern struct cpu_thread *find_cpu_by_chip_id(u32 chip_id); +extern struct cpu_thread *find_cpu_by_node(struct dt_node *cpu); +extern struct cpu_thread *find_cpu_by_server(u32 server_no); +extern struct cpu_thread *find_cpu_by_pir(u32 pir); + +/* Used for lock internals to avoid re-entrancy */ +extern struct cpu_thread __nomcount *find_cpu_by_pir_nomcount(u32 pir); + +extern struct dt_node *get_cpu_node(u32 pir); + +/* Iterator */ +extern struct cpu_thread *first_cpu(void); +extern struct cpu_thread *next_cpu(struct cpu_thread *cpu); + +/* WARNING: CPUs that have been picked up by the OS are no longer + * appearing as available and can not have jobs scheduled + * on them. Essentially that means that after the OS is + * fully started, all CPUs are seen as unavailable from + * this API standpoint. + */ + +static inline bool cpu_is_present(struct cpu_thread *cpu) +{ + return cpu->state >= cpu_state_present; +} + +static inline bool cpu_is_available(struct cpu_thread *cpu) +{ + return cpu->state == cpu_state_active || + cpu->state == cpu_state_rvwinkle; +} + +extern struct cpu_thread *first_available_cpu(void); +extern struct cpu_thread *next_available_cpu(struct cpu_thread *cpu); +extern struct cpu_thread *first_present_cpu(void); +extern struct cpu_thread *next_present_cpu(struct cpu_thread *cpu); +extern struct cpu_thread *first_ungarded_cpu(void); +extern struct cpu_thread *next_ungarded_cpu(struct cpu_thread *cpu); +extern struct cpu_thread *first_ungarded_primary(void); +extern struct cpu_thread *next_ungarded_primary(struct cpu_thread *cpu); + +#define for_each_cpu(cpu) \ + for (cpu = first_cpu(); cpu; cpu = next_cpu(cpu)) + +#define for_each_available_cpu(cpu) \ + for (cpu = first_available_cpu(); cpu; cpu = next_available_cpu(cpu)) + +#define for_each_present_cpu(cpu) \ + for (cpu = first_present_cpu(); cpu; cpu = next_present_cpu(cpu)) + +#define for_each_ungarded_cpu(cpu) \ + for (cpu = first_ungarded_cpu(); cpu; cpu = next_ungarded_cpu(cpu)) + +#define for_each_ungarded_primary(cpu) \ + for (cpu = first_ungarded_primary(); cpu; cpu = next_ungarded_primary(cpu)) + +extern struct cpu_thread *first_available_core_in_chip(u32 chip_id); +extern struct cpu_thread *next_available_core_in_chip(struct cpu_thread *cpu, u32 chip_id); +extern u8 get_available_nr_cores_in_chip(u32 chip_id); + +#define for_each_available_core_in_chip(core, chip_id) \ + for (core = first_available_core_in_chip(chip_id); core; \ + core = next_available_core_in_chip(core, chip_id)) + +/* Return the caller CPU (only after init_cpu_threads) */ +#ifndef __TEST__ +register struct cpu_thread *__this_cpu asm("r16"); +#else +struct cpu_thread *__this_cpu; +#endif + +static inline __nomcount struct cpu_thread *this_cpu(void) +{ + return __this_cpu; +} + +/* + * Note: On POWER9 fused core, cpu_get_thread_index() and cpu_get_core_index() + * return respectively the thread number within a fused core (0..7) and + * the fused core number. If you want the EC (small core) number, you have + * to use the low level pir_to_core_id() and pir_to_thread_id(). + */ +/* Get the thread # of a cpu within the core */ +static inline uint32_t cpu_get_thread_index(struct cpu_thread *cpu) +{ + return cpu->pir - cpu->primary->pir; +} + +/* Get the core # of a cpu within the core */ +extern uint32_t cpu_get_core_index(struct cpu_thread *cpu); + +/* Get the PIR of thread 0 of the same core */ +static inline uint32_t cpu_get_thread0(struct cpu_thread *cpu) +{ + return cpu->primary->pir; +} + +static inline bool cpu_is_thread0(struct cpu_thread *cpu) +{ + return cpu->primary == cpu; +} + +static inline bool cpu_is_core_chiplet_primary(struct cpu_thread *cpu) +{ + return cpu->is_fused_core & (cpu_get_thread_index(cpu) == 1); +} + +static inline bool cpu_is_sibling(struct cpu_thread *cpu1, + struct cpu_thread *cpu2) +{ + return cpu1->primary == cpu2->primary; +} + +/* Called when some error condition requires disabling a core */ +void cpu_disable_all_threads(struct cpu_thread *cpu); + +/* Allocate & queue a job on target CPU */ +extern struct cpu_job *__cpu_queue_job(struct cpu_thread *cpu, + const char *name, + void (*func)(void *data), void *data, + bool no_return); + +static inline struct cpu_job *cpu_queue_job(struct cpu_thread *cpu, + const char *name, + void (*func)(void *data), + void *data) +{ + return __cpu_queue_job(cpu, name, func, data, false); +} + +extern struct cpu_job *cpu_queue_job_on_node(uint32_t chip_id, + const char *name, + void (*func)(void *data), void *data); + + +/* Poll job status, returns true if completed */ +extern bool cpu_poll_job(struct cpu_job *job); + +/* Synchronously wait for a job to complete, this will + * continue handling the FSP mailbox if called from the + * boot CPU. Set free_it to free it automatically. + */ +extern void cpu_wait_job(struct cpu_job *job, bool free_it); + +/* Called by init to process jobs */ +extern void cpu_process_jobs(void); +/* Fallback to running jobs synchronously for global jobs */ +extern void cpu_process_local_jobs(void); +/* Check if there's any job pending */ +bool cpu_check_jobs(struct cpu_thread *cpu); + +/* Set/clear HILE on all CPUs */ +void cpu_set_hile_mode(bool hile); + +/* OPAL sreset vector in place at 0x100 */ +void cpu_set_sreset_enable(bool sreset_enabled); + +/* IPI for PM modes is enabled */ +void cpu_set_ipi_enable(bool sreset_enabled); + +static inline void cpu_give_self_os(void) +{ + __this_cpu->state = cpu_state_os; +} + +extern unsigned long __attrconst cpu_stack_bottom(unsigned int pir); +extern unsigned long __attrconst cpu_stack_top(unsigned int pir); +extern unsigned long __attrconst cpu_emergency_stack_top(unsigned int pir); + +extern void cpu_idle_job(void); +extern void cpu_idle_delay(unsigned long delay); + +extern void cpu_fast_reboot_complete(void); + +int dctl_set_special_wakeup(struct cpu_thread *t); +int dctl_clear_special_wakeup(struct cpu_thread *t); +int dctl_core_is_gated(struct cpu_thread *t); + +extern void exit_uv_mode(int); +void cpu_disable_pef(void); + +#endif /* __CPU_H */ |