提交 00179319 编写于 作者: T Tom Rini

Revert most of the series for adding vexpress_aemv8r support

Per a request from Andre Przywara and agreed with by Peter Hoyes, the
vexpress aemv8r support wasn't quite ready to be merged, but the
discussion had moved off list.  We should keep the first patch in the
series for now, but revert the rest.  This reverts the following
commits:

e0bd6f31 doc: Add documentation for the Arm vexpress board configs
30e5a449 arm: Use armv8_switch_to_el1 env to switch to EL1
b53bbca6 vexpress64: Add BASER_FVP vexpress board variant
2f5b7b74 armv8: Add ARMv8 MPU configuration logic
37a757e2 armv8: Ensure EL1&0 VMSA is enabled
Signed-off-by: NTom Rini <trini@konsulko.com>
上级 b35be5ed
......@@ -1185,13 +1185,6 @@ config TARGET_VEXPRESS64_BASE_FVP
select PL01X_SERIAL
select SEMIHOSTING
config TARGET_VEXPRESS64_BASER_FVP
bool "Support Versatile Express ARMv8r64 FVP BASE model"
select ARM64
select DM
select DM_SERIAL
select PL01X_SERIAL
config TARGET_VEXPRESS64_JUNO
bool "Support Versatile Express Juno Development Platform"
select ARM64
......
......@@ -15,7 +15,6 @@
#include <asm/global_data.h>
#include <asm/system.h>
#include <asm/armv8/mmu.h>
#include <asm/armv8/mpu.h>
DECLARE_GLOBAL_DATA_PTR;
......@@ -366,86 +365,6 @@ __weak u64 get_page_table_size(void)
return size;
}
static void mpu_clear_regions(void)
{
int i;
for (i = 0; mpu_mem_map[i].end || mpu_mem_map[i].attrs; i++) {
setup_el2_mpu_region(i, 0, 0);
}
}
static struct mpu_region default_mpu_mem_map[] = {{0,}};
__weak struct mpu_region *mpu_mem_map = default_mpu_mem_map;
static void mpu_setup(void)
{
int i;
if (current_el() != 2) {
panic("MPU configuration is only supported at EL2");
}
set_sctlr(get_sctlr() & ~(CR_M | CR_WXN));
asm volatile("msr MAIR_EL2, %0" : : "r" MEMORY_ATTRIBUTES);
for (i = 0; mpu_mem_map[i].end || mpu_mem_map[i].attrs; i++) {
setup_el2_mpu_region(i,
PRBAR_ADDRESS(mpu_mem_map[i].start)
| PRBAR_OUTER_SH | PRBAR_AP_RW_ANY,
PRLAR_ADDRESS(mpu_mem_map[i].end)
| mpu_mem_map[i].attrs | PRLAR_EN_BIT
);
}
set_sctlr(get_sctlr() | CR_M);
}
static bool el_has_mmu(void)
{
uint64_t id_aa64mmfr0;
asm volatile("mrs %0, id_aa64mmfr0_el1"
: "=r" (id_aa64mmfr0) : : "cc");
uint64_t msa = id_aa64mmfr0 & ID_AA64MMFR0_EL1_MSA_MASK;
uint64_t msa_frac = id_aa64mmfr0 & ID_AA64MMFR0_EL1_MSA_FRAC_MASK;
switch (msa) {
case ID_AA64MMFR0_EL1_MSA_VMSA:
/*
* VMSA supported in all translation regimes.
* No support for PMSA.
*/
return true;
case ID_AA64MMFR0_EL1_MSA_USE_FRAC:
/* See MSA_frac for the supported MSAs. */
switch (msa_frac) {
case ID_AA64MMFR0_EL1_MSA_FRAC_NO_PMSA:
/*
* PMSA not supported in any translation
* regime.
*/
return true;
case ID_AA64MMFR0_EL1_MSA_FRAC_VMSA:
/*
* PMSA supported in all translation
* regimes. No support for VMSA.
*/
case ID_AA64MMFR0_EL1_MSA_FRAC_PMSA:
/*
* PMSA supported in all translation
* regimes.
*/
return false;
default:
panic("Unsupported id_aa64mmfr0_el1 " \
"MSA_frac value");
}
default:
panic("Unsupported id_aa64mmfr0_el1 MSA value");
}
}
void setup_pgtables(void)
{
int i;
......@@ -560,13 +479,8 @@ void dcache_enable(void)
/* The data cache is not active unless the mmu is enabled */
if (!(get_sctlr() & CR_M)) {
invalidate_dcache_all();
if (el_has_mmu()) {
__asm_invalidate_tlb_all();
mmu_setup();
} else {
mpu_setup();
}
__asm_invalidate_tlb_all();
mmu_setup();
}
set_sctlr(get_sctlr() | CR_C);
......@@ -585,11 +499,7 @@ void dcache_disable(void)
set_sctlr(sctlr & ~(CR_C|CR_M));
flush_dcache_all();
if (el_has_mmu())
__asm_invalidate_tlb_all();
else
mpu_clear_regions();
__asm_invalidate_tlb_all();
}
int dcache_status(void)
......
/*
* SPDX-License-Identifier: GPL-2.0+
*
* (C) Copyright 2021 Arm Limited
*/
#ifndef _ASM_ARMV8_MPU_H_
#define _ASM_ARMV8_MPU_H_
#include <asm/armv8/mmu.h>
#include <asm/barriers.h>
#include <linux/stringify.h>
#define PRSELR_EL2 S3_4_c6_c2_1
#define PRBAR_EL2 S3_4_c6_c8_0
#define PRLAR_EL2 S3_4_c6_c8_1
#define MPUIR_EL2 S3_4_c0_c0_4
#define PRBAR_ADDRESS(addr) ((addr) & ~(0x3fULL))
/* Access permissions */
#define PRBAR_AP(val) (((val) & 0x3) << 2)
#define PRBAR_AP_RW_HYP PRBAR_AP(0x0)
#define PRBAR_AP_RW_ANY PRBAR_AP(0x1)
#define PRBAR_AP_RO_HYP PRBAR_AP(0x2)
#define PRBAR_AP_RO_ANY PRBAR_AP(0x3)
/* Shareability */
#define PRBAR_SH(val) (((val) & 0x3) << 4)
#define PRBAR_NON_SH PRBAR_SH(0x0)
#define PRBAR_OUTER_SH PRBAR_SH(0x2)
#define PRBAR_INNER_SH PRBAR_SH(0x3)
/* Memory attribute (MAIR idx) */
#define PRLAR_ATTRIDX(val) (((val) & 0x7) << 1)
#define PRLAR_EN_BIT (0x1)
#define PRLAR_ADDRESS(addr) ((addr) & ~(0x3fULL))
#ifndef __ASSEMBLY__
static inline void setup_el2_mpu_region(uint8_t region, uint64_t base, uint64_t limit)
{
asm volatile("msr " __stringify(PRSELR_EL2) ", %0" : : "r" (region));
isb();
asm volatile("msr " __stringify(PRBAR_EL2) ", %0" : : "r" (base));
asm volatile("msr " __stringify(PRLAR_EL2) ", %0" : : "r" (limit));
dsb();
isb();
}
#endif
struct mpu_region {
u64 start;
u64 end;
u64 attrs;
};
extern struct mpu_region *mpu_mem_map;
#endif /* _ASM_ARMV8_MPU_H_ */
......@@ -316,23 +316,6 @@ lr .req x30
csel \tmp, \tmp2, \tmp, eq
msr hcr_el2, \tmp
/*
* Detect whether the system has a configurable memory system
* architecture at EL1&0
*/
mrs \tmp, id_aa64mmfr0_el1
lsr \tmp, \tmp, #48
and \tmp, \tmp, #((ID_AA64MMFR0_EL1_MSA_MASK | \
ID_AA64MMFR0_EL1_MSA_FRAC_MASK) >> 48)
cmp \tmp, #((ID_AA64MMFR0_EL1_MSA_USE_FRAC | \
ID_AA64MMFR0_EL1_MSA_FRAC_VMSA) >> 48)
bne 2f
/* Ensure the EL1&0 VMSA is enabled */
mov \tmp, #(VTCR_EL2_MSA)
msr vtcr_el2, \tmp
2:
/* Return to the EL1_SP1 mode from EL2 */
ldr \tmp, =(SPSR_EL_DEBUG_MASK | SPSR_EL_SERR_MASK |\
SPSR_EL_IRQ_MASK | SPSR_EL_FIQ_MASK |\
......
......@@ -83,30 +83,6 @@
#define HCR_EL2_RW_AARCH32 (0 << 31) /* Lower levels are AArch32 */
#define HCR_EL2_HCD_DIS (1 << 29) /* Hypervisor Call disabled */
/*
* VTCR_EL2 bits definitions
*/
#define VTCR_EL2_MSA (1 << 31) /* EL1&0 memory architecture */
/*
* ID_AA64MMFR0_EL1 bits definitions
*/
#define ID_AA64MMFR0_EL1_MSA_FRAC_MASK (0xFUL << 52) /* Memory system
architecture
frac */
#define ID_AA64MMFR0_EL1_MSA_FRAC_VMSA (0x2UL << 52) /* EL1&0 supports
VMSA */
#define ID_AA64MMFR0_EL1_MSA_FRAC_PMSA (0x1UL << 52) /* EL1&0 only
supports PMSA*/
#define ID_AA64MMFR0_EL1_MSA_FRAC_NO_PMSA (0x0UL << 52) /* No PMSA
support */
#define ID_AA64MMFR0_EL1_MSA_MASK (0xFUL << 48) /* Memory system
architecture */
#define ID_AA64MMFR0_EL1_MSA_USE_FRAC (0xFUL << 48) /* Use MSA_FRAC */
#define ID_AA64MMFR0_EL1_MSA_VMSA (0x0UL << 48) /* Memory system
architecture
is VMSA */
/*
* ID_AA64ISAR1_EL1 bits definitions
*/
......
......@@ -317,6 +317,7 @@ __weak void update_os_arch_secondary_cores(uint8_t os_arch)
{
}
#ifdef CONFIG_ARMV8_SWITCH_TO_EL1
static void switch_to_el1(void)
{
if ((IH_ARCH_DEFAULT == IH_ARCH_ARM64) &&
......@@ -331,6 +332,7 @@ static void switch_to_el1(void)
ES_TO_AARCH64);
}
#endif
#endif
/* Subcommand: GO */
static void boot_jump_linux(bootm_headers_t *images, int flag)
......@@ -357,33 +359,21 @@ static void boot_jump_linux(bootm_headers_t *images, int flag)
update_os_arch_secondary_cores(images->os.arch);
#ifdef CONFIG_ARMV8_MULTIENTRY
int armv8_switch_to_el1 = -1;
#else
int armv8_switch_to_el1 = env_get_yesno("armv8_switch_to_el1");
#endif
#ifdef CONFIG_ARMV8_SWITCH_TO_EL1
if (armv8_switch_to_el1 == -1) {
armv8_switch_to_el1 = 1;
}
#endif
if (armv8_switch_to_el1 == 1) {
armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0,
(u64)switch_to_el1, ES_TO_AARCH64);
#else
if ((IH_ARCH_DEFAULT == IH_ARCH_ARM64) &&
(images->os.arch == IH_ARCH_ARM))
armv8_switch_to_el2(0, (u64)gd->bd->bi_arch_number,
(u64)images->ft_addr, 0,
(u64)images->ep,
ES_TO_AARCH32);
else
armv8_switch_to_el2((u64)images->ft_addr, 0, 0, 0,
(u64)switch_to_el1, ES_TO_AARCH64);
} else {
if ((IH_ARCH_DEFAULT == IH_ARCH_ARM64) &&
(images->os.arch == IH_ARCH_ARM))
armv8_switch_to_el2(0,
(u64)gd->bd->bi_arch_number,
(u64)images->ft_addr, 0,
(u64)images->ep,
ES_TO_AARCH32);
else
armv8_switch_to_el2((u64)images->ft_addr,
0, 0, 0,
images->ep,
ES_TO_AARCH64);
}
images->ep,
ES_TO_AARCH64);
#endif
}
#else
unsigned long machid = gd->bd->bi_arch_number;
......
if TARGET_VEXPRESS64_BASE_FVP || TARGET_VEXPRESS64_JUNO || \
TARGET_VEXPRESS64_BASER_FVP
if TARGET_VEXPRESS64_BASE_FVP || TARGET_VEXPRESS64_JUNO
config SYS_BOARD
default "vexpress64"
......@@ -8,7 +7,7 @@ config SYS_VENDOR
default "armltd"
config SYS_CONFIG_NAME
default "vexpress_aemv8"
default "vexpress_aemv8a"
config JUNO_DTB_PART
string "NOR flash partition holding DTB"
......
......@@ -14,8 +14,3 @@ JUNO DEVELOPMENT PLATFORM BOARD
M: Linus Walleij <linus.walleij@linaro.org>
S: Maintained
F: configs/vexpress_aemv8a_juno_defconfig
VEXPRESS64 BASER_FVP
M: Peter Hoyes <Peter.Hoyes@arm.com>
S: Maintained
F: configs/vexpress_aemv8r_defconfig
......@@ -18,7 +18,6 @@
#include <dm/platform_data/serial_pl01x.h>
#include "pcie.h"
#include <asm/armv8/mmu.h>
#include <asm/armv8/mpu.h>
DECLARE_GLOBAL_DATA_PTR;
......@@ -55,27 +54,6 @@ static struct mm_region vexpress64_mem_map[] = {
struct mm_region *mem_map = vexpress64_mem_map;
static struct mpu_region vexpress64_aemv8r_mem_map[] = {
{
.start = 0x0UL,
.end = 0x7fffffffUL,
.attrs = PRLAR_ATTRIDX(MT_NORMAL)
}, {
.start = 0x80000000UL,
.end = 0xffffffffUL,
.attrs = PRLAR_ATTRIDX(MT_DEVICE_NGNRNE)
}, {
.start = 0x100000000UL,
.end = 0xffffffffffUL,
.attrs = PRLAR_ATTRIDX(MT_NORMAL)
}, {
/* List terminator */
0,
}
};
struct mpu_region *mpu_mem_map = vexpress64_aemv8r_mem_map;
/* This function gets replaced by platforms supporting PCIe.
* The replacement function, eg. on Juno, initialises the PCIe bus.
*/
......
CONFIG_ARM=y
CONFIG_TARGET_VEXPRESS64_BASER_FVP=y
CONFIG_SYS_TEXT_BASE=0x00001000
CONFIG_SYS_MALLOC_F_LEN=0x2000
CONFIG_NR_DRAM_BANKS=2
CONFIG_SYS_MEMTEST_START=0x80000000
CONFIG_SYS_MEMTEST_END=0xff000000
CONFIG_ENV_SIZE=0x40000
CONFIG_ENV_SECT_SIZE=0x40000
CONFIG_IDENT_STRING=" vexpress_aemv8r64"
CONFIG_DISTRO_DEFAULTS=y
CONFIG_BOOTDELAY=3
CONFIG_USE_BOOTARGS=y
CONFIG_BOOTARGS="console=ttyAMA0 earlycon=pl011,0x9c090000 rootfstype=ext4 root=/dev/vda1 rw rootwait"
CONFIG_USE_BOOTCOMMAND=y
CONFIG_BOOTCOMMAND="fdt addr ${fdt_addr}; fdt resize; booti $kernel_addr - $fdt_addr"
# CONFIG_DISPLAY_CPUINFO is not set
CONFIG_SYS_PROMPT="VExpress64# "
CONFIG_OF_LIBFDT=y
......@@ -25,7 +25,7 @@ or turning on CONFIG_BASE_FVP for the more full featured model.
Rather than create a new armv8 board similar to armltd/vexpress64, add
semihosting calls to the existing one, enabled with CONFIG_SEMIHOSTING
and CONFIG_BASE_FVP both set. Also reuse the existing board config file
vexpress_aemv8.h but differentiate the two models by the presence or
vexpress_aemv8a.h but differentiate the two models by the presence or
absence of CONFIG_BASE_FVP. This change is tested and works on both the
Foundation and Base fastmodel simulators.
......
......@@ -18,8 +18,7 @@ Notes
classical firmware (like initial hardware setup, CPU errata workarounds
or SMP bringup). U-Boot can be entered in EL2 when its main purpose is
that of a boot loader. It can drop to lower exception levels before
entering the OS. For ARMv8-R it is recommened to enter at S-EL2, as for this
architecture there is no S-EL3.
entering the OS.
2. U-Boot for arm64 is compiled with AArch64-gcc. AArch64-gcc
use rela relocation format, a tool(tools/relocate-rela) by Scott Wood
......
.. SPDX-License-Identifier: GPL-2.0+
Arm Ltd
=============
.. toctree::
:maxdepth: 2
vexpress64.rst
.. SPDX-License-Identifier: GPL-2.0+
Arm Versatile Express
=====================
The vexpress_* board configuration supports the following platforms:
* FVP_Base_RevC-2xAEMvA
* FVP_BaseR_AEMv8R
* Juno development board
Fixed Virtual Platforms
-----------------------
The Fixed Virtual Platforms (FVP) are complete simulations of an Arm system,
including processor, memory and peripherals. They are set out in a "programmer's
view", which gives a comprehensive model on which to build and test software.
The supported FVPs are available free of charge and can be downloaded from the
Arm developer site [1]_ (user registration might be required).
Supported features:
* GICv3
* Generic timer
* PL011 UART
* SMC91111 network interface
The default configuration assumes that U-Boot is boostrapped from the start of
the DRAM (address 0x80000000 for AEMvA; 0x00000000 for AEMv8R) using a suitable
bootloader. Alternatively, U-Boot can be launched directly by mapping the binary
to the same address (using the FVP's --data argument).
The FVPs can be debugged using Arm Development Studio [2]_.
FVP_BaseR
^^^^^^^^^
On Armv8r64 platforms (such as the FVP_BaseR), U-Boot runs at S-EL2, so
CONFIG_ARMV8_SWITCH_TO_EL1 is defined so that the next stage boots at S-EL1. If
S-EL2 is desired instead, the *armv8_switch_to_el1* environment variable is
available. This can be set to *n* to override the config flag and boot the next
stage at S-EL2 instead.
Juno
----
The Juno development board is an open, vendor-neutral Armv8-A development
platform that supports an out-of-the-box Linux software package. A range of
plug-in expansion options enables hardware and software applications to be
developed and debugged.
References
----------
.. [1] https://developer.arm.com/tools-and-software/simulation-models/fixed-virtual-platforms
.. [2] https://developer.arm.com/tools-and-software/embedded/arm-development-studio
......@@ -10,7 +10,6 @@ Board-specific doc
advantech/index
AndesTech/index
amlogic/index
armltd/index
atmel/index
congatec/index
coreboot/index
......
......@@ -10,36 +10,30 @@
#define CONFIG_REMAKE_ELF
/* Link Definitions */
#ifdef CONFIG_TARGET_VEXPRESS64_JUNO
#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x7fff0)
#else
#ifdef CONFIG_TARGET_VEXPRESS64_BASE_FVP
/* ATF loads u-boot here for BASE_FVP model */
#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x03f00000)
#elif CONFIG_TARGET_VEXPRESS64_JUNO
#define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x7fff0)
#endif
#define CONFIG_SYS_BOOTM_LEN (64 << 20) /* Increase max gunzip size */
/* CS register bases for the original memory map. */
#ifdef CONFIG_TARGET_VEXPRESS64_BASER_FVP
#define V2M_BASE 0x00000000
#define V2M_PA_BASE 0x80000000
#else
#define V2M_BASE 0x80000000
#define V2M_PA_BASE 0x00000000
#endif
#define V2M_PA_CS0 (V2M_PA_BASE + 0x00000000)
#define V2M_PA_CS1 (V2M_PA_BASE + 0x14000000)
#define V2M_PA_CS2 (V2M_PA_BASE + 0x18000000)
#define V2M_PA_CS3 (V2M_PA_BASE + 0x1c000000)
#define V2M_PA_CS4 (V2M_PA_BASE + 0x0c000000)
#define V2M_PA_CS5 (V2M_PA_BASE + 0x10000000)
#define V2M_PA_CS0 0x00000000
#define V2M_PA_CS1 0x14000000
#define V2M_PA_CS2 0x18000000
#define V2M_PA_CS3 0x1c000000
#define V2M_PA_CS4 0x0c000000
#define V2M_PA_CS5 0x10000000
#define V2M_PERIPH_OFFSET(x) (x << 16)
#define V2M_SYSREGS (V2M_PA_CS3 + V2M_PERIPH_OFFSET(1))
#define V2M_SYSCTL (V2M_PA_CS3 + V2M_PERIPH_OFFSET(2))
#define V2M_SERIAL_BUS_PCI (V2M_PA_CS3 + V2M_PERIPH_OFFSET(3))
#define V2M_BASE 0x80000000
/* Common peripherals relative to CS7. */
#define V2M_AACI (V2M_PA_CS3 + V2M_PERIPH_OFFSET(4))
#define V2M_MMCI (V2M_PA_CS3 + V2M_PERIPH_OFFSET(5))
......@@ -74,27 +68,27 @@
#define V2M_SYS_CFGSTAT (V2M_SYSREGS + 0x0a8)
/* Generic Timer Definitions */
#define COUNTER_FREQUENCY 100000000 /* 100MHz */
#define COUNTER_FREQUENCY 24000000 /* 24MHz */
/* Generic Interrupt Controller Definitions */
#ifdef CONFIG_GICV3
#define GICD_BASE (V2M_PA_BASE + 0x2f000000)
#define GICR_BASE (V2M_PA_BASE + 0x2f100000)
#define GICD_BASE (0x2f000000)
#define GICR_BASE (0x2f100000)
#else
#ifdef CONFIG_TARGET_VEXPRESS64_JUNO
#ifdef CONFIG_TARGET_VEXPRESS64_BASE_FVP
#define GICD_BASE (0x2f000000)
#define GICC_BASE (0x2c000000)
#elif CONFIG_TARGET_VEXPRESS64_JUNO
#define GICD_BASE (0x2C010000)
#define GICC_BASE (0x2C02f000)
#else
#define GICD_BASE (V2M_PA_BASE + 0x2f000000)
#define GICC_BASE (V2M_PA_BASE + 0x2c000000)
#endif
#endif /* !CONFIG_GICV3 */
#ifndef CONFIG_TARGET_VEXPRESS64_JUNO
/* The Vexpress64 simulators use SMSC91C111 */
#define CONFIG_SMC91111 1
#define CONFIG_SMC91111_BASE (V2M_PA_BASE + 0x01A000000)
#define CONFIG_SMC91111_BASE (0x01A000000)
#endif
/* PL011 Serial Configuration */
......@@ -119,7 +113,7 @@
#ifdef CONFIG_TARGET_VEXPRESS64_JUNO
#define PHYS_SDRAM_2 (0x880000000)
#define PHYS_SDRAM_2_SIZE 0x180000000
#elif CONFIG_NR_DRAM_BANKS == 2
#elif CONFIG_TARGET_VEXPRESS64_BASE_FVP && CONFIG_NR_DRAM_BANKS == 2
#define PHYS_SDRAM_2 (0x880000000)
#define PHYS_SDRAM_2_SIZE 0x80000000
#endif
......@@ -206,12 +200,6 @@
" booti $kernel_addr - $fdt_addr; " \
"fi"
#endif
#elif CONFIG_TARGET_VEXPRESS64_BASER_FVP
#define CONFIG_EXTRA_ENV_SETTINGS \
"kernel_addr=0x00800000\0" \
"fdt_addr=0x03000000\0" \
"boot_addr=0x0007f800\0"
#endif
/* Monitor Command Prompt */
......@@ -225,7 +213,7 @@
/* Store environment at top of flash in the same location as blank.img */
/* in the Juno firmware. */
#else
#define CONFIG_SYS_FLASH_BASE (V2M_PA_BASE + 0x0C000000)
#define CONFIG_SYS_FLASH_BASE 0x0C000000
/* 256 x 256KiB sectors */
#define CONFIG_SYS_MAX_FLASH_SECT 256
/* Store environment at top of flash */
......@@ -242,8 +230,4 @@
#define CONFIG_SYS_FLASH_EMPTY_INFO /* flinfo indicates empty blocks */
#define FLASH_MAX_SECTOR_SIZE 0x00040000
#ifdef CONFIG_TARGET_VEXPRESS64_BASER_FVP
#define CONFIG_ARMV8_SWITCH_TO_EL1
#endif
#endif /* __VEXPRESS_AEMV8A_H */
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册