/* * Copyright (c) 2014-2017, ARM Limited and Contributors. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of ARM nor the names of its contributors may be used * to endorse or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include /* For __dead2 */ #include #include #include #include #include #include #include #include #include #include #include /* * Affinity info map of CPUs as seen by TFTF * - Set cpus_status_map[i].state to TFTF_AFFINITY_STATE_ON to mark CPU i * as ON. * - Set cpus_status_map[i].state to TFTF_AFFINITY_STATE_ON_PENDING to mark * CPU i as ON_PENDING. * - Set cpus_status_map[i].state to TFTF_AFFINITY_STATE_OFF to mark CPU i * as OFF. */ static tftf_cpu_state_t cpus_status_map[PLATFORM_CORE_COUNT]; static int cpus_status_init_done; /* * Reference count keeping track of the number of CPUs participating in * a test. */ static volatile unsigned int ref_cnt; /* Lock to prevent concurrent accesses to the reference count */ static spinlock_t ref_cnt_lock; /* Per-cpu test entrypoint */ volatile test_function_t test_entrypoint[PLATFORM_CORE_COUNT]; u_register_t tftf_primary_core = INVALID_MPID; unsigned int tftf_inc_ref_cnt(void) { unsigned int cnt; spin_lock(&ref_cnt_lock); assert(ref_cnt < PLATFORM_CORE_COUNT); cnt = ++ref_cnt; spin_unlock(&ref_cnt_lock); VERBOSE("Entering the test (%u CPUs in the test now)\n", cnt); return cnt; } unsigned int tftf_dec_ref_cnt(void) { unsigned int cnt; spin_lock(&ref_cnt_lock); assert(ref_cnt != 0); cnt = --ref_cnt; spin_unlock(&ref_cnt_lock); VERBOSE("Exiting the test (%u CPUs in the test now)\n", cnt); return cnt; } unsigned int tftf_get_ref_cnt(void) { return ref_cnt; } void tftf_init_cpus_status_map(void) { unsigned int mpid = read_mpidr_el1(); unsigned int core_pos = platform_get_core_pos(mpid); /* Check only primary does the initialisation */ assert((mpid & MPID_MASK) == tftf_primary_core); /* Check init is done only once */ assert(!cpus_status_init_done); cpus_status_init_done = 1; /* * cpus_status_map already initialised to zero as part of BSS init, * just set the primary to ON state */ cpus_status_map[core_pos].state = TFTF_AFFINITY_STATE_ON; } void tftf_set_cpu_online(void) { unsigned int mpid = read_mpidr_el1(); unsigned int core_pos = platform_get_core_pos(mpid); /* * Wait here till the `tftf_try_cpu_on` has had a chance to update the * the cpu state. */ while (cpus_status_map[core_pos].state == TFTF_AFFINITY_STATE_OFF) ; spin_lock(&cpus_status_map[core_pos].lock); assert(cpus_status_map[core_pos].state == TFTF_AFFINITY_STATE_ON_PENDING); cpus_status_map[core_pos].state = TFTF_AFFINITY_STATE_ON; spin_unlock(&cpus_status_map[core_pos].lock); } void tftf_set_cpu_offline(void) { unsigned int mpid = read_mpidr_el1(); unsigned int core_pos = platform_get_core_pos(mpid); spin_lock(&cpus_status_map[core_pos].lock); assert(tftf_is_cpu_online(mpid)); cpus_status_map[core_pos].state = TFTF_AFFINITY_STATE_OFF; spin_unlock(&cpus_status_map[core_pos].lock); } unsigned int tftf_is_cpu_online(unsigned int mpid) { unsigned int core_pos = platform_get_core_pos(mpid); return cpus_status_map[core_pos].state == TFTF_AFFINITY_STATE_ON; } unsigned int tftf_is_core_pos_online(unsigned int core_pos) { return cpus_status_map[core_pos].state == TFTF_AFFINITY_STATE_ON; } int32_t tftf_cpu_on(u_register_t target_cpu, uintptr_t entrypoint, u_register_t context_id) { int32_t ret; tftf_affinity_info_t cpu_state; unsigned int core_pos = platform_get_core_pos(target_cpu); spin_lock(&cpus_status_map[core_pos].lock); cpu_state = cpus_status_map[core_pos].state; if (cpu_state == TFTF_AFFINITY_STATE_ON) { spin_unlock(&cpus_status_map[core_pos].lock); return PSCI_E_ALREADY_ON; } if (cpu_state == TFTF_AFFINITY_STATE_ON_PENDING) { spin_unlock(&cpus_status_map[core_pos].lock); return PSCI_E_SUCCESS; } assert(cpu_state == TFTF_AFFINITY_STATE_OFF); do { ret = tftf_psci_cpu_on(target_cpu, (uintptr_t) tftf_hotplug_entry, context_id); /* Check if multiple CPU_ON calls are done for same CPU */ assert(ret != PSCI_E_ON_PENDING); } while (ret == PSCI_E_ALREADY_ON); if (ret == PSCI_E_SUCCESS) { /* * Populate the test entry point for this core. * This is the address where the core will jump to once the framework * has finished initialising it. */ test_entrypoint[core_pos] = (test_function_t) entrypoint; cpus_status_map[core_pos].state = TFTF_AFFINITY_STATE_ON_PENDING; spin_unlock(&cpus_status_map[core_pos].lock); } else { spin_unlock(&cpus_status_map[core_pos].lock); ERROR("Failed to boot CPU 0x%llx (%d)\n", (unsigned long long)target_cpu, ret); } return ret; } int32_t tftf_try_cpu_on(u_register_t target_cpu, uintptr_t entrypoint, u_register_t context_id) { int32_t ret; unsigned int core_pos = platform_get_core_pos(target_cpu); ret = tftf_psci_cpu_on(target_cpu, (uintptr_t) tftf_hotplug_entry, context_id); if (ret == PSCI_E_SUCCESS) { spin_lock(&cpus_status_map[core_pos].lock); assert(cpus_status_map[core_pos].state == TFTF_AFFINITY_STATE_OFF); cpus_status_map[core_pos].state = TFTF_AFFINITY_STATE_ON_PENDING; spin_unlock(&cpus_status_map[core_pos].lock); /* * Populate the test entry point for this core. * This is the address where the core will jump to once the * framework has finished initialising it. */ test_entrypoint[core_pos] = (test_function_t) entrypoint; } return ret; } /* * Prepare the core to power off. Any driver which needs to perform specific * tasks before powering off a CPU, e.g. migrating interrupts to another * core, can implement a function and call it from here. */ static void tftf_prepare_cpu_off(void) { /* * Do the bare minimal to turn off this CPU i.e. turn off interrupts * and disable the GIC CPU interface */ disable_irq(); arm_gic_disable_interrupts_local(); } /* * Revert the changes made during tftf_prepare_cpu_off() */ static void tftf_revert_cpu_off(void) { arm_gic_enable_interrupts_local(); enable_irq(); } int32_t tftf_cpu_off(void) { int32_t ret; tftf_prepare_cpu_off(); tftf_set_cpu_offline(); INFO("Powering off\n"); /* Flush console before the last CPU is powered off. */ if (tftf_get_ref_cnt() == 0) console_flush(); /* Power off the CPU */ ret = tftf_psci_cpu_off(); ERROR("Failed to power off (%d)\n", ret); /* * PSCI CPU_OFF call does not return when successful. * Otherwise, it should return the PSCI error code 'DENIED'. */ assert(ret == PSCI_E_DENIED); /* * The CPU failed to power down since we returned from * tftf_psci_cpu_off(). So we need to adjust the framework's view of * the core by marking it back online. */ tftf_set_cpu_online(); tftf_revert_cpu_off(); return ret; } /* * C entry point for a CPU that has just been powered up. */ void __dead2 tftf_warm_boot_main(void) { /* Initialise the CPU */ tftf_arch_setup(); arm_gic_setup_local(); /* Enable the SGI used by the timer management framework */ tftf_irq_enable(IRQ_WAKE_SGI, GIC_HIGHEST_NS_PRIORITY); enable_irq(); INFO("Booting\n"); tftf_set_cpu_online(); /* Enter the test session */ run_tests(); /* Should never reach this point */ bug_unreachable(); }