int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id,

                   uint32_t target_el, bool target_aa64)

{

    CPUState *target_cpu_state;

    ARMCPU *target_cpu;



    DPRINTF("cpu %" PRId64 " (EL %d, %s) @ 0x%" PRIx64 " with R0 = 0x%" PRIx64

            "\n", cpuid, target_el, target_aa64 ? "aarch64" : "aarch32", entry,

            context_id);



    /* requested EL level need to be in the 1 to 3 range */

    assert((target_el > 0) && (target_el < 4));



    if (target_aa64 && (entry & 3)) {

        /*

         * if we are booting in AArch64 mode then "entry" needs to be 4 bytes

         * aligned.

         */

        return QEMU_ARM_POWERCTL_INVALID_PARAM;

    }



    /* Retrieve the cpu we are powering up */

    target_cpu_state = arm_get_cpu_by_id(cpuid);

    if (!target_cpu_state) {

        /* The cpu was not found */

        return QEMU_ARM_POWERCTL_INVALID_PARAM;

    }



    target_cpu = ARM_CPU(target_cpu_state);

    if (!target_cpu->powered_off) {

        qemu_log_mask(LOG_GUEST_ERROR,

                      "[ARM]%s: CPU %" PRId64 " is already on\n",

                      __func__, cpuid);

        return QEMU_ARM_POWERCTL_ALREADY_ON;

    }



    /*

     * The newly brought CPU is requested to enter the exception level

     * "target_el" and be in the requested mode (AArch64 or AArch32).

     */



    if (((target_el == 3) && !arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) ||

        ((target_el == 2) && !arm_feature(&target_cpu->env, ARM_FEATURE_EL2))) {

        /*

         * The CPU does not support requested level

         */

        return QEMU_ARM_POWERCTL_INVALID_PARAM;

    }



    if (!target_aa64 && arm_feature(&target_cpu->env, ARM_FEATURE_AARCH64)) {

        /*

         * For now we don't support booting an AArch64 CPU in AArch32 mode

         * TODO: We should add this support later

         */

        qemu_log_mask(LOG_UNIMP,

                      "[ARM]%s: Starting AArch64 CPU %" PRId64

                      " in AArch32 mode is not supported yet\n",

                      __func__, cpuid);

        return QEMU_ARM_POWERCTL_INVALID_PARAM;

    }



    /* Initialize the cpu we are turning on */

    cpu_reset(target_cpu_state);

    target_cpu->powered_off = false;

    target_cpu_state->halted = 0;



    if (target_aa64) {

        if ((target_el < 3) && arm_feature(&target_cpu->env, ARM_FEATURE_EL3)) {

            /*

             * As target mode is AArch64, we need to set lower

             * exception level (the requested level 2) to AArch64

             */

            target_cpu->env.cp15.scr_el3 |= SCR_RW;

        }



        if ((target_el < 2) && arm_feature(&target_cpu->env, ARM_FEATURE_EL2)) {

            /*

             * As target mode is AArch64, we need to set lower

             * exception level (the requested level 1) to AArch64

             */

            target_cpu->env.cp15.hcr_el2 |= HCR_RW;

        }



        target_cpu->env.pstate = aarch64_pstate_mode(target_el, true);

    } else {

        /* We are requested to boot in AArch32 mode */

        static uint32_t mode_for_el[] = { 0,

                                          ARM_CPU_MODE_SVC,

                                          ARM_CPU_MODE_HYP,

                                          ARM_CPU_MODE_SVC };



        cpsr_write(&target_cpu->env, mode_for_el[target_el], CPSR_M,

                   CPSRWriteRaw);

    }



    if (target_el == 3) {

        /* Processor is in secure mode */

        target_cpu->env.cp15.scr_el3 &= ~SCR_NS;

    } else {

        /* Processor is not in secure mode */

        target_cpu->env.cp15.scr_el3 |= SCR_NS;

    }



    /* We check if the started CPU is now at the correct level */

    assert(target_el == arm_current_el(&target_cpu->env));



    if (target_aa64) {

        target_cpu->env.xregs[0] = context_id;

        target_cpu->env.thumb = false;

    } else {

        target_cpu->env.regs[0] = context_id;

        target_cpu->env.thumb = entry & 1;

        entry &= 0xfffffffe;

    }



    /* Start the new CPU at the requested address */

    cpu_set_pc(target_cpu_state, entry);



    qemu_cpu_kick(target_cpu_state);



    /* We are good to go */

    return QEMU_ARM_POWERCTL_RET_SUCCESS;

}
