Commit 408c3768 authored by danh-arm's avatar danh-arm
Browse files

Merge pull request #48 from danh-arm/dh/major-refactoring

dh/major refactoring
parents b495bdef 97043ac9
......@@ -59,14 +59,14 @@ else
BUILD_TYPE := release
endif
BL_COMMON_SOURCES := misc_helpers.S \
cache_helpers.S \
tlb_helpers.S \
xlat_helpers.c \
std.c \
bl_common.c \
platform_helpers.S \
io_storage.c
BL_COMMON_SOURCES := common/bl_common.c \
lib/aarch64/cache_helpers.S \
lib/aarch64/misc_helpers.S \
lib/aarch64/tlb_helpers.S \
lib/aarch64/xlat_helpers.c \
lib/stdlib/std.c \
lib/io_storage.c \
plat/common/aarch64/platform_helpers.S
ARCH ?= aarch64
......@@ -132,12 +132,17 @@ endif
.SUFFIXES:
INCLUDES += -Ilib/include/ \
-Idrivers/io \
-Iinclude/${ARCH}/ \
-Iinclude/ \
-Iarch/system/gic \
-Iservices/std_svc/psci \
INCLUDES += -Iinclude/bl1 \
-Iinclude/bl2 \
-Iinclude/bl31 \
-Iinclude/bl31/services \
-Iinclude/bl32 \
-Iinclude/bl32/payloads \
-Iinclude/common \
-Iinclude/drivers \
-Iinclude/drivers/arm \
-Iinclude/lib \
-Iinclude/lib/aarch64 \
-Iinclude/stdlib \
-Iinclude/stdlib/sys \
-Iplat/${PLAT} \
......@@ -156,13 +161,6 @@ LDFLAGS += --fatal-warnings -O1
LDFLAGS += --gc-sections
vpath %.ld.S bl1:bl2:bl31
vpath %.c bl1:bl2:bl31
vpath %.c bl1/${ARCH}:bl2/${ARCH}:bl31/${ARCH}
vpath %.S bl1/${ARCH}:bl2/${ARCH}:bl31/${ARCH}
vpath %.c lib/arch/${ARCH} # One of the missing paths needed for BL_COMMON_SOURCES
ifneq (${DEBUG}, 0)
#CFLAGS += -g -O0
CFLAGS += -g
......
......@@ -28,9 +28,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <arch.h>
#include <arch_helpers.h>
#include <platform.h>
#include <assert.h>
/*******************************************************************************
* Function that does the first bit of architectural setup that affects
......
......@@ -29,11 +29,9 @@
*/
#include <arch.h>
#include <asm_macros.S>
#include <bl_common.h>
#include <bl1.h>
#include <platform.h>
#include <runtime_svc.h>
#include <asm_macros.S>
.globl bl1_exceptions
......
......@@ -28,25 +28,10 @@
# POSSIBILITY OF SUCH DAMAGE.
#
vpath %.c plat/${PLAT} \
plat/${PLAT}/${ARCH} \
common \
lib \
arch/${ARCH} \
lib/arch/${ARCH} \
${PLAT_BL1_C_VPATH}
BL1_SOURCES += bl1/bl1_main.c \
bl1/aarch64/bl1_arch_setup.c \
bl1/aarch64/bl1_entrypoint.S \
bl1/aarch64/bl1_exceptions.S \
lib/aarch64/cpu_helpers.S
vpath %.S arch/${ARCH}/cpu \
plat/common/${ARCH} \
plat/${PLAT}/${ARCH} \
include \
lib/arch/${ARCH} \
${PLAT_BL1_S_VPATH}
BL1_SOURCES += bl1_arch_setup.c \
bl1_entrypoint.S \
bl1_exceptions.S \
bl1_main.c \
cpu_helpers.S
BL1_LINKERFILE := bl1.ld.S
BL1_LINKERFILE := bl1/bl1.ld.S
......@@ -28,15 +28,14 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <arch.h>
#include <arch_helpers.h>
#include <platform.h>
#include <semihosting.h>
#include <assert.h>
#include <bl_common.h>
#include <bl1.h>
void bl1_arch_next_el_setup(void);
#include <platform.h>
#include <stdio.h>
#include "bl1_private.h"
/*******************************************************************************
* Function to perform late architectural and platform specific initialization.
......@@ -52,8 +51,8 @@ void bl1_main(void)
#endif
unsigned long bl2_base;
unsigned int load_type = TOP_LOAD, spsr;
meminfo *bl1_tzram_layout;
meminfo *bl2_tzram_layout = 0x0;
meminfo_t *bl1_tzram_layout;
meminfo_t *bl2_tzram_layout = 0x0;
/*
* Ensure that MMU/Caches and coherency are turned on
......@@ -88,7 +87,7 @@ void bl1_main(void)
* to BL2. BL2 will read the memory layout before using its
* memory for other purposes.
*/
bl2_tzram_layout = (meminfo *) bl1_tzram_layout->free_base;
bl2_tzram_layout = (meminfo_t *) bl1_tzram_layout->free_base;
init_bl2_mem_layout(bl1_tzram_layout,
bl2_tzram_layout,
load_type,
......
......@@ -28,129 +28,13 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __GIC_V2_H__
#define __GIC_V2_H__
#ifndef __BL1_PRIVATE_H__
#define __BL1_PRIVATE_H__
#include <mmio.h>
/******************************************
* Function prototypes
*****************************************/
extern void bl1_arch_setup(void);
extern void bl1_arch_next_el_setup(void);
/*******************************************************************************
* GIC Distributor interface accessors for reading entire registers
******************************************************************************/
static inline unsigned int gicd_read_ctlr(unsigned int base)
{
return mmio_read_32(base + GICD_CTLR);
}
static inline unsigned int gicd_read_typer(unsigned int base)
{
return mmio_read_32(base + GICD_TYPER);
}
static inline unsigned int gicd_read_sgir(unsigned int base)
{
return mmio_read_32(base + GICD_SGIR);
}
/*******************************************************************************
* GIC Distributor interface accessors for writing entire registers
******************************************************************************/
static inline void gicd_write_ctlr(unsigned int base, unsigned int val)
{
mmio_write_32(base + GICD_CTLR, val);
}
static inline void gicd_write_sgir(unsigned int base, unsigned int val)
{
mmio_write_32(base + GICD_SGIR, val);
}
/*******************************************************************************
* GIC CPU interface accessors for reading entire registers
******************************************************************************/
static inline unsigned int gicc_read_ctlr(unsigned int base)
{
return mmio_read_32(base + GICC_CTLR);
}
static inline unsigned int gicc_read_pmr(unsigned int base)
{
return mmio_read_32(base + GICC_PMR);
}
static inline unsigned int gicc_read_BPR(unsigned int base)
{
return mmio_read_32(base + GICC_BPR);
}
static inline unsigned int gicc_read_IAR(unsigned int base)
{
return mmio_read_32(base + GICC_IAR);
}
static inline unsigned int gicc_read_EOIR(unsigned int base)
{
return mmio_read_32(base + GICC_EOIR);
}
static inline unsigned int gicc_read_hppir(unsigned int base)
{
return mmio_read_32(base + GICC_HPPIR);
}
static inline unsigned int gicc_read_dir(unsigned int base)
{
return mmio_read_32(base + GICC_DIR);
}
static inline unsigned int gicc_read_iidr(unsigned int base)
{
return mmio_read_32(base + GICC_IIDR);
}
/*******************************************************************************
* GIC CPU interface accessors for writing entire registers
******************************************************************************/
static inline void gicc_write_ctlr(unsigned int base, unsigned int val)
{
mmio_write_32(base + GICC_CTLR, val);
}
static inline void gicc_write_pmr(unsigned int base, unsigned int val)
{
mmio_write_32(base + GICC_PMR, val);
}
static inline void gicc_write_BPR(unsigned int base, unsigned int val)
{
mmio_write_32(base + GICC_BPR, val);
}
static inline void gicc_write_IAR(unsigned int base, unsigned int val)
{
mmio_write_32(base + GICC_IAR, val);
}
static inline void gicc_write_EOIR(unsigned int base, unsigned int val)
{
mmio_write_32(base + GICC_EOIR, val);
}
static inline void gicc_write_hppir(unsigned int base, unsigned int val)
{
mmio_write_32(base + GICC_HPPIR, val);
}
static inline void gicc_write_dir(unsigned int base, unsigned int val)
{
mmio_write_32(base + GICC_DIR, val);
}
#endif /* __GIC_V2_H__ */
#endif /* __BL1_PRIVATE_H__ */
......@@ -28,8 +28,8 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <arch.h>
#include <arch_helpers.h>
#include <platform.h>
/*******************************************************************************
* Place holder function to perform any S-EL1 specific architectural setup. At
......
......@@ -28,9 +28,9 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <bl_common.h>
#include <arch.h>
#include <asm_macros.S>
#include <bl_common.h>
.globl bl2_entrypoint
......
......@@ -28,25 +28,10 @@
# POSSIBILITY OF SUCH DAMAGE.
#
vpath %.c common \
lib \
plat/${PLAT} \
plat/${PLAT}/${ARCH} \
arch/${ARCH} \
${PLAT_BL2_C_VPATH}
BL2_SOURCES += bl2/bl2_main.c \
bl2/aarch64/bl2_entrypoint.S \
bl2/aarch64/bl2_arch_setup.c \
common/aarch64/early_exceptions.S \
lib/locks/exclusive/spinlock.S
vpath %.S lib/arch/${ARCH} \
include \
lib/sync/locks/exclusive \
common/${ARCH} \
${PLAT_BL2_S_VPATH}
BL2_SOURCES += bl2_entrypoint.S \
bl2_arch_setup.c \
bl2_main.c \
spinlock.S \
early_exceptions.S
BL2_LINKERFILE := bl2.ld.S
CFLAGS += $(DEFINES)
BL2_LINKERFILE := bl2/bl2.ld.S
......@@ -28,16 +28,15 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <arch.h>
#include <arch_helpers.h>
#include <console.h>
#include <platform.h>
#include <semihosting.h>
#include <assert.h>
#include <bl_common.h>
#include <bl2.h>
#include "debug.h"
#include <debug.h>
#include <platform.h>
#include <stdio.h>
#include "bl2_private.h"
/*******************************************************************************
* The only thing to do in BL2 is to load further images and pass control to
......@@ -47,8 +46,8 @@
******************************************************************************/
void bl2_main(void)
{
meminfo *bl2_tzram_layout;
bl31_args *bl2_to_bl31_args;
meminfo_t *bl2_tzram_layout;
bl31_args_t *bl2_to_bl31_args;
unsigned long bl31_base, bl32_base = 0, bl33_base, el_status;
unsigned int bl2_load, bl31_load, mode;
......
/*
* Copyright (c) 2013-2014, 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.
*/
#ifndef __BL2_PRIVATE_H__
#define __BL2_PRIVATE_H__
/******************************************
* Function prototypes
*****************************************/
extern void bl2_arch_setup(void);
#endif /* __BL2_PRIVATE_H__ */
......@@ -28,9 +28,12 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <arch.h>
#include <arch_helpers.h>
#include <platform.h>
#include <assert.h>
#include <bl_common.h>
#include <bl31.h>
#include <platform.h>
/*******************************************************************************
* This duplicates what the primary cpu did after a cold boot in BL1. The same
......
......@@ -28,11 +28,10 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <bl_common.h>
#include <platform.h>
#include <arch.h>
#include "cm_macros.S"
#include <asm_macros.S>
#include <bl_common.h>
#include <cm_macros.S>
.globl bl31_entrypoint
......
......@@ -28,8 +28,9 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <context.h>
#include <arch.h>
#include <asm_macros.S>
#include <context.h>
/* -----------------------------------------------------
* The following function strictly follows the AArch64
......
......@@ -29,11 +29,11 @@
*/
#include <arch.h>
#include <runtime_svc.h>
#include <platform.h>
#include <asm_macros.S>
#include <cm_macros.S>
#include <context.h>
#include "asm_macros.S"
#include "cm_macros.S"
#include <platform.h>
#include <runtime_svc.h>
.globl runtime_exceptions
.globl el3_exit
......
......@@ -28,46 +28,23 @@
# POSSIBILITY OF SUCH DAMAGE.
#
vpath %.c common \
lib \
arch/system/gic \
plat/${PLAT} \
arch/${ARCH} \
services/std_svc \
services/std_svc/psci \
lib/sync/locks/bakery \
plat/${PLAT}/${ARCH} \
${PLAT_BL31_C_VPATH}
BL31_SOURCES += bl31/bl31_main.c \
bl31/context_mgmt.c \
bl31/runtime_svc.c \
bl31/aarch64/bl31_arch_setup.c \
bl31/aarch64/bl31_entrypoint.S \
bl31/aarch64/context.S \
bl31/aarch64/runtime_exceptions.S \
common/aarch64/early_exceptions.S \
lib/locks/bakery/bakery_lock.c \
lib/locks/exclusive/spinlock.S \
services/std_svc/std_svc_setup.c \
services/std_svc/psci/psci_afflvl_off.c \
services/std_svc/psci/psci_afflvl_on.c \
services/std_svc/psci/psci_afflvl_suspend.c \
services/std_svc/psci/psci_common.c \
services/std_svc/psci/psci_entry.S \
services/std_svc/psci/psci_main.c \
services/std_svc/psci/psci_setup.c
vpath %.S lib/arch/${ARCH} \
services/std_svc \
services/std_svc/psci \
include \
plat/${PLAT}/${ARCH} \
lib/sync/locks/exclusive \
plat/common/${ARCH} \
arch/system/gic/${ARCH} \
common/${ARCH} \
${PLAT_BL31_S_VPATH}
BL31_SOURCES += bl31_arch_setup.c \
bl31_entrypoint.S \
runtime_exceptions.S \
bl31_main.c \
std_svc_setup.c \
psci_entry.S \
psci_setup.c \
psci_common.c \
psci_afflvl_on.c \
psci_main.c \
psci_afflvl_off.c \
psci_afflvl_suspend.c \
spinlock.S \
gic_v3_sysregs.S \
bakery_lock.c \
runtime_svc.c \
early_exceptions.S \
context_mgmt.c \
context.S
BL31_LINKERFILE := bl31.ld.S
BL31_LINKERFILE := bl31/bl31.ld.S
......@@ -28,17 +28,14 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <arch.h>
#include <arch_helpers.h>
#include <console.h>
#include <platform.h>
#include <semihosting.h>
#include <assert.h>
#include <bl_common.h>
#include <bl31.h>
#include <runtime_svc.h>
#include <context_mgmt.h>
#include <runtime_svc.h>
#include <stdio.h>
/*******************************************************************************
......@@ -47,7 +44,7 @@
* for SP execution. In cases where both SPD and SP are absent, or when SPD
* finds it impossible to execute SP, this pointer is left as NULL
******************************************************************************/
static int32_t (*bl32_init)(meminfo *);
static int32_t (*bl32_init)(meminfo_t *);
/*******************************************************************************
* Variable to indicate whether next image to execute after BL31 is BL33
......@@ -153,7 +150,7 @@ uint32_t bl31_get_next_image_type(void)
******************************************************************************/
void bl31_prepare_next_image_entry()
{
el_change_info *next_image_info;
el_change_info_t *next_image_info;
uint32_t scr, image_type;
/* Determine which image to execute next */
......@@ -190,7 +187,7 @@ void bl31_prepare_next_image_entry()
* This function initializes the pointer to BL32 init function. This is expected
* to be called by the SPD after it finishes all its initialization
******************************************************************************/
void bl31_register_bl32_init(int32_t (*func)(meminfo *))
void bl31_register_bl32_init(int32_t (*func)(meminfo_t *))
{
bl32_init = func;
}
......@@ -28,15 +28,13 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <arch_helpers.h>
#include <platform.h>
#include <assert.h>
#include <bl_common.h>
#include <runtime_svc.h>
#include <context.h>
#include <context_mgmt.h>
#include <platform.h>
#include <runtime_svc.h>
/*******************************************************************************
* Data structure which holds the pointers to non-secure and secure security
......@@ -45,9 +43,9 @@
******************************************************************************/
typedef struct {
void *ptr[2];
} __aligned (CACHE_WRITEBACK_GRANULE) context_info;
} __aligned (CACHE_WRITEBACK_GRANULE) context_info_t;
static context_info cm_context_info[PLATFORM_CORE_COUNT];
static context_info_t cm_context_info[PLATFORM_CORE_COUNT];
/*******************************************************************************
* Context management library initialisation routine. This library is used by
......@@ -104,7 +102,7 @@ void cm_set_context(uint64_t mpidr, void *context, uint32_t security_state)
******************************************************************************/
void cm_el3_sysregs_context_save(uint32_t security_state)
{
cpu_context *ctx;
cpu_context_t *ctx;
ctx = cm_get_context(read_mpidr(), security_state);
assert(ctx);
......@@ -114,7 +112,7 @@ void cm_el3_sysregs_context_save(uint32_t security_state)
void cm_el3_sysregs_context_restore(uint32_t security_state)
{
cpu_context *ctx;
cpu_context_t *ctx;
ctx = cm_get_context(read_mpidr(), security_state);
assert(ctx);
......@@ -124,7 +122,7 @@ void cm_el3_sysregs_context_restore(uint32_t security_state)
void cm_el1_sysregs_context_save(uint32_t security_state)
{
cpu_context *ctx;
cpu_context_t *ctx;
ctx = cm_get_context(read_mpidr(), security_state);
assert(ctx);
......@@ -134,7 +132,7 @@ void cm_el1_sysregs_context_save(uint32_t security_state)
void cm_el1_sysregs_context_restore(uint32_t security_state)
{
cpu_context *ctx;
cpu_context_t *ctx;
ctx = cm_get_context(read_mpidr(), security_state);
assert(ctx);
......@@ -151,8 +149,8 @@ void cm_el1_sysregs_context_restore(uint32_t security_state)
void cm_set_el3_eret_context(uint32_t security_state, uint64_t entrypoint,
uint32_t spsr, uint32_t scr)
{
cpu_context *ctx;
el3_state *state;
cpu_context_t *ctx;
el3_state_t *state;
ctx = cm_get_context(read_mpidr(), security_state);
assert(ctx);
......@@ -170,8 +168,8 @@ void cm_set_el3_eret_context(uint32_t security_state, uint64_t entrypoint,
******************************************************************************/
void cm_set_el3_elr(uint32_t security_state, uint64_t entrypoint)
{
cpu_context *ctx;
el3_state *state;
cpu_context_t *ctx;
el3_state_t *state;
ctx = cm_get_context(read_mpidr(), security_state);
assert(ctx);
......@@ -188,7 +186,7 @@ void cm_set_el3_elr(uint32_t security_state, uint64_t entrypoint)
******************************************************************************/
void cm_set_next_eret_context(uint32_t security_state)
{
cpu_context *ctx;
cpu_context_t *ctx;
#if DEBUG
uint64_t sp_mode;
#endif
......@@ -220,8 +218,8 @@ void cm_set_next_eret_context(uint32_t security_state)
******************************************************************************/
void cm_init_exception_stack(uint64_t mpidr, uint32_t security_state)
{
cpu_context *ctx;
el3_state *state;
cpu_context_t *ctx;
el3_state_t *state;
ctx = cm_get_context(mpidr, security_state);
assert(ctx);
......
......@@ -28,20 +28,10 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <debug.h>
#include <errno.h>
#include <assert.h>
#include <arch_helpers.h>
#include <console.h>
#include <platform.h>
#include <semihosting.h>
#include <bl_common.h>
#include <psci.h>
#include <runtime_svc.h>
#include <context.h>
#include <debug.h>
#include <context_mgmt.h>
#include <string.h>
/*******************************************************************************
* The 'rt_svc_descs' array holds the runtime service descriptors exported by
......@@ -55,12 +45,12 @@
#define RT_SVC_DESCS_START ((uint64_t) (&__RT_SVC_DESCS_START__))
#define RT_SVC_DESCS_END ((uint64_t) (&__RT_SVC_DESCS_END__))
uint8_t rt_svc_descs_indices[MAX_RT_SVCS];
static rt_svc_desc *rt_svc_descs;
static rt_svc_desc_t *rt_svc_descs;
/*******************************************************************************
* Simple routine to sanity check a runtime service descriptor before using it
******************************************************************************/
static int32_t validate_rt_svc_desc(rt_svc_desc *desc)
static int32_t validate_rt_svc_desc(rt_svc_desc_t *desc)
{
if (desc == NULL)
return -EINVAL;
......@@ -96,14 +86,14 @@ void runtime_svc_init()
/* If no runtime services are implemented then simply bail out */
rt_svc_descs_num = RT_SVC_DESCS_END - RT_SVC_DESCS_START;
rt_svc_descs_num /= sizeof(rt_svc_desc);
rt_svc_descs_num /= sizeof(rt_svc_desc_t);
if (rt_svc_descs_num == 0)
return;
/* Initialise internal variables to invalid state */
memset(rt_svc_descs_indices, -1, sizeof(rt_svc_descs_indices));
rt_svc_descs = (rt_svc_desc *) RT_SVC_DESCS_START;
rt_svc_descs = (rt_svc_desc_t *) RT_SVC_DESCS_START;
for (index = 0; index < rt_svc_descs_num; index++) {
/*
......@@ -148,7 +138,7 @@ error:
void fault_handler(void *handle)
{
gp_regs *gpregs_ctx = get_gpregs_ctx(handle);
gp_regs_t *gpregs_ctx = get_gpregs_ctx(handle);
ERROR("Unhandled synchronous fault. Register dump @ 0x%x \n",
gpregs_ctx);
panic();
......
......@@ -28,10 +28,9 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <bl_common.h>
#include <arch.h>
#include <tsp.h>
#include <asm_macros.S>
#include <tsp.h>
.globl tsp_entrypoint
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment