arm: support reboot / poweroff for qemu virt arm

Signed-off-by: Shanmin Zhang <zhangshanmin@xiaomi.com>
This commit is contained in:
Shanmin Zhang
2023-08-14 11:55:38 +08:00
committed by Xiang Xiao
parent 9f10ddc9b0
commit b9fb9bea1d
11 changed files with 503 additions and 0 deletions
+16
View File
@@ -645,11 +645,17 @@ config ARCH_CHIP_TLSR82
config ARCH_CHIP_QEMU_ARM
bool "QEMU virt platform (ARMv7a)"
select ARCH_HAVE_PSCI
select ARCH_HAVE_POWEROFF
select ARCH_HAVE_RESET
---help---
QEMU virt platform (ARMv7a)
config ARCH_CHIP_GOLDFISH_ARM
bool "GOLDFISH virt platform (ARMv7a)"
select ARCH_HAVE_PSCI
select ARCH_HAVE_POWEROFF
select ARCH_HAVE_RESET
---help---
GOLDFISH virt platform (ARMv7a)
@@ -1064,6 +1070,16 @@ config ARM_THUMB
bool "Thumb Mode"
default n
config ARCH_HAVE_PSCI
bool "ARM PCSI (Power State Coordination Interface) Support"
default n
---help---
This Power State Coordination Interface (PSCI) defines
a standard interface for power management. the PCSI need
to implement handling firmware at EL2 or EL3. The option
maybe not applicable for arm core without PCSI firmware
interface implement
config ARM_HAVE_WFE_SEV
bool
default n
+51
View File
@@ -126,6 +126,31 @@
* Public Types
****************************************************************************/
/* Result from SMC/HVC call
* a0-a7 result values from registers 0 to 7
*/
struct arm_smccc_res
{
unsigned long a0;
unsigned long a1;
unsigned long a2;
unsigned long a3;
unsigned long a4;
unsigned long a5;
unsigned long a6;
unsigned long a7;
};
typedef struct arm_smccc_res arm_smccc_res_t;
enum arm_smccc_conduit
{
SMCCC_CONDUIT_NONE,
SMCCC_CONDUIT_SMC,
SMCCC_CONDUIT_HVC,
};
/****************************************************************************
* Inline functions
****************************************************************************/
@@ -316,6 +341,32 @@ static inline long smh_call(unsigned int nbr, void *parm)
* Public Function Prototypes
****************************************************************************/
/* Make HVC calls
*
* param a0 function identifier
* param a1-a7 parameters registers
* param res results
*/
void arm_smccc_hvc(unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3,
unsigned long a4, unsigned long a5,
unsigned long a6, unsigned long a7,
struct arm_smccc_res *res);
/* Make SMC calls
*
* param a0 function identifier
* param a1-a7 parameters registers
* param res results
*/
void arm_smccc_smc(unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3,
unsigned long a4, unsigned long a5,
unsigned long a6, unsigned long a7,
struct arm_smccc_res *res);
#ifdef __cplusplus
#define EXTERN extern "C"
extern "C"
+5
View File
@@ -55,6 +55,11 @@ if(NOT CONFIG_ARCH_IDLE_CUSTOM)
list(APPEND SRCS arm_idle.c)
endif()
if(CONFIG_ARCH_HAVE_PSCI)
list(APPEND SRCS arm_smccc.S)
list(APPEND SRCS arm_cpu_psci.c)
endif()
if(CONFIG_BUILD_PROTECTED OR CONFIG_BUILD_KERNEL)
list(APPEND SRCS arm_task_start.c arm_pthread_start.c arm_signal_dispatch.c)
+5
View File
@@ -41,6 +41,11 @@ ifneq ($(CONFIG_ARCH_IDLE_CUSTOM),y)
CMN_CSRCS += arm_idle.c
endif
ifeq ($(CONFIG_ARCH_HAVE_PSCI),y)
CMN_ASRCS += arm_smccc.S
CMN_CSRCS += arm_cpu_psci.c
endif
ifeq ($(CONFIG_BUILD_PROTECTED)$(CONFIG_BUILD_KERNEL),y)
CMN_CSRCS += arm_task_start.c arm_pthread_start.c
CMN_CSRCS += arm_signal_dispatch.c
+232
View File
@@ -0,0 +1,232 @@
/***************************************************************************
* arch/arm/src/common/arm_cpu_psci.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
***************************************************************************/
/***************************************************************************
* Included Files
***************************************************************************/
#include <nuttx/config.h>
#include <debug.h>
#include <assert.h>
#include <nuttx/arch.h>
#include <arch/irq.h>
#include <arch/chip/chip.h>
#include <nuttx/spinlock.h>
#include "arch/syscall.h"
#include "arm_cpu_psci.h"
/***************************************************************************
* Private Data
***************************************************************************/
static struct psci_interface psci_data;
/***************************************************************************
* Private Functions
***************************************************************************/
static int psci_to_dev_err(int ret)
{
switch (ret)
{
case PSCI_RET_SUCCESS:
{
return 0;
}
case PSCI_RET_NOT_SUPPORTED:
{
return -ENOTSUP;
}
case PSCI_RET_INVALID_PARAMS:
case PSCI_RET_INVALID_ADDRESS:
{
return -EINVAL;
}
case PSCI_RET_DENIED:
{
return -EPERM;
}
}
return -EINVAL;
}
static unsigned long __invoke_psci_fn_hvc(unsigned long function_id,
unsigned long arg0,
unsigned long arg1,
unsigned long arg2)
{
struct arm_smccc_res res;
arm_smccc_hvc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res);
return res.a0;
}
static unsigned long __invoke_psci_fn_smc(unsigned long function_id,
unsigned long arg0,
unsigned long arg1,
unsigned long arg2)
{
struct arm_smccc_res res;
arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res);
return res.a0;
}
static uint32_t psci_get_version(void)
{
return psci_data.invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0);
}
static int set_conduit_method(const char *method)
{
if (!strcmp("hvc", method))
{
psci_data.conduit = SMCCC_CONDUIT_HVC;
psci_data.invoke_psci_fn = __invoke_psci_fn_hvc;
}
else if (!strcmp("smc", method))
{
psci_data.conduit = SMCCC_CONDUIT_SMC;
psci_data.invoke_psci_fn = __invoke_psci_fn_smc;
}
else
{
serr("Invalid conduit method");
return -EINVAL;
}
return 0;
}
static int psci_detect(void)
{
uint32_t ver = psci_get_version();
sinfo("Detected PSCI v%ld.%ld\n",
PSCI_VERSION_MAJOR(ver), PSCI_VERSION_MINOR(ver));
if (PSCI_VERSION_MAJOR(ver) == 0 && PSCI_VERSION_MINOR(ver) < 2)
{
serr("PSCI unsupported version");
return -ENOTSUP;
}
psci_data.version = ver;
return 0;
}
/***************************************************************************
* Public Functions
***************************************************************************/
uint32_t psci_version(void)
{
return psci_data.version;
}
int psci_cpu_off(void)
{
int ret;
if (psci_data.conduit == SMCCC_CONDUIT_NONE)
{
return -EINVAL;
}
ret = psci_data.invoke_psci_fn(PSCI_0_2_FN_CPU_OFF, 0, 0, 0);
return psci_to_dev_err(ret);
}
int psci_cpu_reset(void)
{
int ret;
if (psci_data.conduit == SMCCC_CONDUIT_NONE)
{
return -EINVAL;
}
ret = psci_data.invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
return psci_to_dev_err(ret);
}
int psci_cpu_on(unsigned long cpuid, uintptr_t entry_point)
{
int ret;
if (psci_data.conduit == SMCCC_CONDUIT_NONE)
{
return -EINVAL;
}
ret = psci_data.invoke_psci_fn(PSCI_FN_NATIVE(0_2, CPU_ON),
cpuid, (unsigned long)entry_point, 0);
return psci_to_dev_err(ret);
}
int psci_sys_reset(void)
{
int ret;
if (psci_data.conduit == SMCCC_CONDUIT_NONE)
{
return -EINVAL;
}
ret = psci_data.invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
return psci_to_dev_err(ret);
}
int psci_sys_poweroff(void)
{
int ret;
if (psci_data.conduit == SMCCC_CONDUIT_NONE)
{
return -EINVAL;
}
ret = psci_data.invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
return psci_to_dev_err(ret);
}
int arm_psci_init(const char * method)
{
psci_data.conduit = SMCCC_CONDUIT_NONE;
if (set_conduit_method(method))
{
return -ENOTSUP;
}
return psci_detect();
}
+107
View File
@@ -0,0 +1,107 @@
/****************************************************************************
* arch/arm/src/common/arm_cpu_psci.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef __ARCH_ARM_SRC_COMMON_ARM_CPU_PSCI_H
#define __ARCH_ARM_SRC_COMMON_ARM_CPU_PSCI_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <arch/irq.h>
#include <arch/chip/chip.h>
#include <arch/syscall.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN_##name
/* PSCI v0.2 interface */
#define PSCI_0_2_FN_BASE 0x84000000
#define PSCI_0_2_FN(n) (PSCI_0_2_FN_BASE + (n))
#define PSCI_0_2_64BIT 0x40000000
#define PSCI_0_2_FN64_BASE \
(PSCI_0_2_FN_BASE + PSCI_0_2_64BIT)
#define PSCI_0_2_FN64(n) (PSCI_0_2_FN64_BASE + (n))
#define PSCI_0_2_FN_PSCI_VERSION PSCI_0_2_FN(0)
#define PSCI_0_2_FN_CPU_SUSPEND PSCI_0_2_FN(1)
#define PSCI_0_2_FN_CPU_OFF PSCI_0_2_FN(2)
#define PSCI_0_2_FN_CPU_ON PSCI_0_2_FN(3)
#define PSCI_0_2_FN_AFFINITY_INFO PSCI_0_2_FN(4)
#define PSCI_0_2_FN_MIGRATE PSCI_0_2_FN(5)
#define PSCI_0_2_FN_MIGRATE_INFO_TYPE PSCI_0_2_FN(6)
#define PSCI_0_2_FN_MIGRATE_INFO_UP_CPU PSCI_0_2_FN(7)
#define PSCI_0_2_FN_SYSTEM_OFF PSCI_0_2_FN(8)
#define PSCI_0_2_FN_SYSTEM_RESET PSCI_0_2_FN(9)
#define PSCI_0_2_FN64_CPU_SUSPEND PSCI_0_2_FN64(1)
#define PSCI_0_2_FN64_CPU_ON PSCI_0_2_FN64(3)
#define PSCI_0_2_FN64_AFFINITY_INFO PSCI_0_2_FN64(4)
#define PSCI_0_2_FN64_MIGRATE PSCI_0_2_FN64(5)
#define PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU PSCI_0_2_FN64(7)
/* PSCI return values (inclusive of all PSCI versions) */
#define PSCI_RET_SUCCESS 0
#define PSCI_RET_NOT_SUPPORTED -1
#define PSCI_RET_INVALID_PARAMS -2
#define PSCI_RET_DENIED -3
#define PSCI_RET_ALREADY_ON -4
#define PSCI_RET_ON_PENDING -5
#define PSCI_RET_INTERNAL_FAILURE -6
#define PSCI_RET_NOT_PRESENT -7
#define PSCI_RET_DISABLED -8
#define PSCI_RET_INVALID_ADDRESS -9
/* PSCI version decoding (independent of PSCI version) */
#define PSCI_VERSION_MAJOR_SHIFT 16
#define PSCI_VERSION_MINOR_MASK \
((1U << PSCI_VERSION_MAJOR_SHIFT) - 1)
#define PSCI_VERSION_MAJOR_MASK ~PSCI_VERSION_MINOR_MASK
#define PSCI_VERSION_MAJOR(ver) \
(((ver) & PSCI_VERSION_MAJOR_MASK) >> PSCI_VERSION_MAJOR_SHIFT)
#define PSCI_VERSION_MINOR(ver) \
((ver) & PSCI_VERSION_MINOR_MASK)
typedef unsigned long (*psci_fn)(unsigned long, unsigned long, unsigned long,
unsigned long);
struct psci_interface
{
enum arm_smccc_conduit conduit;
psci_fn invoke_psci_fn;
uint32_t version;
};
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
uint32_t psci_version(void);
int psci_cpu_off(void);
int psci_cpu_reset(void);
int psci_cpu_on(unsigned long cpuid, uintptr_t entry_point);
int psci_sys_poweroff(void);
int psci_sys_reset(void);
#endif /* __ARCH_ARM_SRC_COMMON_ARM_CPU_PSCI_H */
+2
View File
@@ -323,6 +323,8 @@ void modifyreg32(unsigned int addr, uint32_t clearbits, uint32_t setbits);
void arm_boot(void);
int arm_psci_init(const char *method);
/* Context switching */
uint32_t *arm_decodeirq(uint32_t *regs);
+76
View File
@@ -0,0 +1,76 @@
/****************************************************************************
* arch/arm/src/common/arm_smccc.S
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
/****************************************************************************
* Public Symbols
****************************************************************************/
.file "arm_smccc.S"
/****************************************************************************
* Assembly Macros
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Public Functions
****************************************************************************/
/* The SMC instruction is used to generate a synchronous exception that is
* handled by Secure Monitor code running in EL3.
*/
.text
.syntax unified
.global arm_smccc_smc
.type arm_smccc_smc, function
arm_smccc_smc:
mov r12, sp
push {r4-r7}
ldm r12, {r4-r7}
smc #0
pop {r4-r7}
ldr r12, [sp, #(4 * 4)]
stm r12, {r0-r3}
bx lr
/* The HVC instruction is used to generate a synchronous exception that is
* handled by a hypervisor running in EL2.
*/
.global arm_smccc_hvc
.type arm_smccc_hvc, function
arm_smccc_hvc:
mov r12, sp
push {r4-r7}
ldm r12, {r4-r7}
hvc #0
pop {r4-r7}
ldr r12, [sp, #(4 * 4)]
stm r12, {r0-r3}
bx lr
+1
View File
@@ -22,3 +22,4 @@ include armv7-a/Make.defs
# goldfish-specific C source files
CHIP_CSRCS = goldfish_boot.c goldfish_serial.c goldfish_irq.c goldfish_timer.c goldfish_memorymap.c
+4
View File
@@ -55,6 +55,10 @@ void arm_boot(void)
fdt_register((FAR const char *)0x40000000);
#endif
#if defined(CONFIG_ARCH_HAVE_PSCI)
arm_psci_init("smc");
#endif
#ifdef USE_EARLYSERIALINIT
/* Perform early serial initialization if we are going to use the serial
* driver.
+4
View File
@@ -51,6 +51,10 @@ void arm_boot(void)
qemu_setupmappings();
#if defined(CONFIG_ARCH_HAVE_PSCI)
arm_psci_init("hvc");
#endif
#ifdef CONFIG_DEVICE_TREE
fdt_register((FAR const char *)0x40000000);
#endif