Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
adam.huang
Arm Trusted Firmware
Commits
b47f941d
Unverified
Commit
b47f941d
authored
Apr 11, 2018
by
Dimitris Papastamos
Committed by
GitHub
Apr 11, 2018
Browse files
Merge pull request #1342 from Summer-ARM/sq/support-tzmp1
support tzmp1
parents
a2344983
60a23fd8
Changes
9
Hide whitespace changes
Inline
Side-by-side
docs/user-guide.rst
View file @
b47f941d
...
...
@@ -789,6 +789,12 @@ Arm FVP platform specific build options
HW_CONFIG blob instead of the DTS file. This option is useful to override
the default HW_CONFIG selected by the build system.
ARM JUNO platform specific build options
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- ``JUNO_TZMP1`` : Boolean option to configure Juno to be used for TrustZone
Media Protection (TZ-MP1). Default value of this flag is 0.
Debugging options
~~~~~~~~~~~~~~~~~
...
...
include/plat/arm/common/plat_arm.h
View file @
b47f941d
...
...
@@ -11,6 +11,7 @@
#include <cassert.h>
#include <cpu_data.h>
#include <stdint.h>
#include <tzc_common.h>
#include <utils_def.h>
/*******************************************************************************
...
...
@@ -21,6 +22,43 @@ struct meminfo;
struct
image_info
;
struct
bl_params
;
typedef
struct
arm_tzc_regions_info
{
unsigned
long
long
base
;
unsigned
long
long
end
;
tzc_region_attributes_t
sec_attr
;
unsigned
int
nsaid_permissions
;
}
arm_tzc_regions_info_t
;
/*******************************************************************************
* Default mapping definition of the TrustZone Controller for ARM standard
* platforms.
* Configure:
* - Region 0 with no access;
* - Region 1 with secure access only;
* - the remaining DRAM regions access from the given Non-Secure masters.
******************************************************************************/
#if ENABLE_SPM
#define ARM_TZC_REGIONS_DEF \
{ARM_AP_TZC_DRAM1_BASE, ARM_EL3_TZC_DRAM1_END, \
TZC_REGION_S_RDWR, 0}, \
{ARM_NS_DRAM1_BASE, ARM_NS_DRAM1_END, ARM_TZC_NS_DRAM_S_ACCESS, \
PLAT_ARM_TZC_NS_DEV_ACCESS}, \
{ARM_DRAM2_BASE, ARM_DRAM2_END, ARM_TZC_NS_DRAM_S_ACCESS, \
PLAT_ARM_TZC_NS_DEV_ACCESS}, \
{ARM_SP_IMAGE_NS_BUF_BASE, (ARM_SP_IMAGE_NS_BUF_BASE + \
ARM_SP_IMAGE_NS_BUF_SIZE) - 1, TZC_REGION_S_NONE, \
PLAT_ARM_TZC_NS_DEV_ACCESS}
#else
#define ARM_TZC_REGIONS_DEF \
{ARM_AP_TZC_DRAM1_BASE, ARM_EL3_TZC_DRAM1_END, \
TZC_REGION_S_RDWR, 0}, \
{ARM_NS_DRAM1_BASE, ARM_NS_DRAM1_END, ARM_TZC_NS_DRAM_S_ACCESS, \
PLAT_ARM_TZC_NS_DEV_ACCESS}, \
{ARM_DRAM2_BASE, ARM_DRAM2_END, ARM_TZC_NS_DRAM_S_ACCESS, \
PLAT_ARM_TZC_NS_DEV_ACCESS}
#endif
#define ARM_CASSERT_MMAP \
CASSERT((ARRAY_SIZE(plat_arm_mmap) + ARM_BL_REGIONS) \
<= MAX_MMAP_REGIONS, \
...
...
@@ -110,9 +148,10 @@ void arm_setup_page_tables(uintptr_t total_base,
void
arm_io_setup
(
void
);
/* Security utility functions */
void
arm_tzc400_setup
(
void
);
void
arm_tzc400_setup
(
const
arm_tzc_regions_info_t
*
tzc_regions
);
struct
tzc_dmc500_driver_data
;
void
arm_tzc_dmc500_setup
(
struct
tzc_dmc500_driver_data
*
plat_driver_data
);
void
arm_tzc_dmc500_setup
(
struct
tzc_dmc500_driver_data
*
plat_driver_data
,
const
arm_tzc_regions_info_t
*
tzc_regions
);
/* Systimer utility function */
void
arm_configure_sys_timer
(
void
);
...
...
plat/arm/board/fvp/fvp_security.c
View file @
b47f941d
/*
* Copyright (c) 2014-201
5
, ARM Limited and Contributors. All rights reserved.
* Copyright (c) 2014-201
8
, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
...
...
@@ -22,5 +22,5 @@ void plat_arm_security_setup(void)
*/
if
(
get_arm_config
()
->
flags
&
ARM_CONFIG_HAS_TZC
)
arm_tzc400_setup
();
arm_tzc400_setup
(
NULL
);
}
plat/arm/board/juno/include/platform_def.h
View file @
b47f941d
/*
* Copyright (c) 2014-201
7
, ARM Limited and Contributors. All rights reserved.
* Copyright (c) 2014-201
8
, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
...
...
plat/arm/board/juno/juno_security.c
View file @
b47f941d
/*
* Copyright (c) 2014-201
5
, ARM Limited and Contributors. All rights reserved.
* Copyright (c) 2014-201
8
, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <debug.h>
#include <mmio.h>
#include <nic_400.h>
#include <plat_arm.h>
#include <soc_css.h>
#include "juno_def.h"
#include "juno_tzmp1_def.h"
#ifdef JUNO_TZMP1
/*
* Protect buffer for VPU/GPU/DPU memory usage with hardware protection
* enabled. Propose 224MB video output, 96 MB video input and 32MB video
* private.
*
* Ind Memory Range Caption S_ATTR NS_ATTR
* 1 0x080000000 - 0x0E7FFFFFF ARM_NS_DRAM1 NONE RDWR | MEDIA_RW
* 2 0x0E8000000 - 0x0F5FFFFFF JUNO_MEDIA_TZC_PROT_DRAM1 NONE MEDIA_RW | AP_WR
* 3 0x0F6000000 - 0x0FBFFFFFF JUNO_VPU_TZC_PROT_DRAM1 RDWR VPU_PROT_RW
* 4 0x0FC000000 - 0x0FDFFFFFF JUNO_VPU_TZC_PRIV_DRAM1 RDWR VPU_PRIV_RW
* 5 0x0FE000000 - 0x0FEFFFFFF JUNO_AP_TZC_SHARE_DRAM1 NONE RDWR | MEDIA_RW
* 6 0x0FF000000 - 0x0FFFFFFFF ARM_AP_TZC_DRAM1 RDWR NONE
* 7 0x880000000 - 0x9FFFFFFFF ARM_DRAM2 NONE RDWR | MEDIA_RW
*
* Memory regions are neighbored to save limited TZC regions. Calculation
* started from ARM_TZC_SHARE_DRAM1 since it is known and fixed for both
* protected-enabled and protected-disabled settings.
*
* Video private buffer aheads of ARM_TZC_SHARE_DRAM1
*/
static
const
arm_tzc_regions_info_t
juno_tzmp1_tzc_regions
[]
=
{
{
ARM_AP_TZC_DRAM1_BASE
,
ARM_AP_TZC_DRAM1_END
,
TZC_REGION_S_RDWR
,
0
},
{
JUNO_NS_DRAM1_PT1_BASE
,
JUNO_NS_DRAM1_PT1_END
,
TZC_REGION_S_NONE
,
JUNO_MEDIA_TZC_NS_DEV_ACCESS
},
{
JUNO_MEDIA_TZC_PROT_DRAM1_BASE
,
JUNO_MEDIA_TZC_PROT_DRAM1_END
,
TZC_REGION_S_NONE
,
JUNO_MEDIA_TZC_PROT_ACCESS
},
{
JUNO_VPU_TZC_PROT_DRAM1_BASE
,
JUNO_VPU_TZC_PROT_DRAM1_END
,
TZC_REGION_S_RDWR
,
JUNO_VPU_TZC_PROT_ACCESS
},
{
JUNO_VPU_TZC_PRIV_DRAM1_BASE
,
JUNO_VPU_TZC_PRIV_DRAM1_END
,
TZC_REGION_S_RDWR
,
JUNO_VPU_TZC_PRIV_ACCESS
},
{
JUNO_AP_TZC_SHARE_DRAM1_BASE
,
JUNO_AP_TZC_SHARE_DRAM1_END
,
TZC_REGION_S_NONE
,
JUNO_MEDIA_TZC_NS_DEV_ACCESS
},
{
ARM_DRAM2_BASE
,
ARM_DRAM2_END
,
TZC_REGION_S_NONE
,
JUNO_MEDIA_TZC_NS_DEV_ACCESS
},
{},
};
/*******************************************************************************
* Program dp650 to configure NSAID value for protected mode.
******************************************************************************/
static
void
init_dp650
(
void
)
{
mmio_write_32
(
DP650_BASE
+
DP650_PROT_NSAID_OFFSET
,
DP650_PROT_NSAID_CONFIG
);
}
/*******************************************************************************
* Program v550 to configure NSAID value for protected mode.
******************************************************************************/
static
void
init_v550
(
void
)
{
/*
* bits[31:28] is for PRIVATE,
* bits[27:24] is for OUTBUF,
* bits[23:20] is for PROTECTED.
*/
mmio_write_32
(
V550_BASE
+
V550_PROTCTRL_OFFSET
,
V550_PROTCTRL_CONFIG
);
}
#endif
/* JUNO_TZMP1 */
/*******************************************************************************
* Set up the MMU-401 SSD tables. The power-on configuration has all stream IDs
...
...
@@ -59,11 +124,23 @@ void plat_arm_security_setup(void)
/* Initialize debug configuration */
init_debug_cfg
();
/* Initialize the TrustZone Controller */
arm_tzc400_setup
();
#ifdef JUNO_TZMP1
arm_tzc400_setup
(
juno_tzmp1_tzc_regions
);
INFO
(
"TZC protected shared memory base address for TZMP usecase: %p
\n
"
,
(
void
*
)
JUNO_AP_TZC_SHARE_DRAM1_BASE
);
INFO
(
"TZC protected shared memory end address for TZMP usecase: %p
\n
"
,
(
void
*
)
JUNO_AP_TZC_SHARE_DRAM1_END
);
#else
arm_tzc400_setup
(
NULL
);
#endif
/* Do ARM CSS internal NIC setup */
css_init_nic400
();
/* Do ARM CSS SoC security setup */
soc_css_security_setup
();
/* Initialize the SMMU SSD tables */
init_mmu401
();
#ifdef JUNO_TZMP1
init_dp650
();
init_v550
();
#endif
}
plat/arm/board/juno/juno_tzmp1_def.h
0 → 100644
View file @
b47f941d
/*
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __JUNO_TZMP1_DEF_H__
#define __JUNO_TZMP1_DEF_H__
#include <plat_arm.h>
/*
* Public memory regions for both protected and non-protected mode
*
* OPTEE shared memory 0xFEE00000 - 0xFEFFFFFF
*/
#define JUNO_AP_TZC_SHARE_DRAM1_SIZE ULL(0x02000000)
#define JUNO_AP_TZC_SHARE_DRAM1_BASE (ARM_AP_TZC_DRAM1_BASE - \
JUNO_AP_TZC_SHARE_DRAM1_SIZE)
#define JUNO_AP_TZC_SHARE_DRAM1_END (ARM_AP_TZC_DRAM1_BASE - 1)
/* ARM_MEDIA_FEATURES for MEDIA GPU Protect Mode Test */
#define JUNO_TZC400_NSAID_FPGA_MEDIA_SECURE 8
/* GPU/DPU protected, VPU outbuf */
#define JUNO_TZC400_NSAID_FPGA_VIDEO_PROTECTED 7
/* VPU protected */
#define JUNO_TZC400_NSAID_FPGA_VIDEO_PRIVATE 10
/* VPU private (firmware) */
#define JUNO_VPU_TZC_PRIV_DRAM1_SIZE ULL(0x02000000)
#define JUNO_VPU_TZC_PRIV_DRAM1_BASE (JUNO_AP_TZC_SHARE_DRAM1_BASE - \
JUNO_VPU_TZC_PRIV_DRAM1_SIZE)
#define JUNO_VPU_TZC_PRIV_DRAM1_END (JUNO_AP_TZC_SHARE_DRAM1_BASE - 1)
/* Video input protected buffer follows upper item */
#define JUNO_VPU_TZC_PROT_DRAM1_SIZE ULL(0x06000000)
#define JUNO_VPU_TZC_PROT_DRAM1_BASE (JUNO_VPU_TZC_PRIV_DRAM1_BASE - \
JUNO_VPU_TZC_PROT_DRAM1_SIZE)
#define JUNO_VPU_TZC_PROT_DRAM1_END (JUNO_VPU_TZC_PRIV_DRAM1_BASE - 1)
/* Video, graphics and display shares same NSAID and same protected buffer */
#define JUNO_MEDIA_TZC_PROT_DRAM1_SIZE ULL(0x0e000000)
#define JUNO_MEDIA_TZC_PROT_DRAM1_BASE (JUNO_VPU_TZC_PROT_DRAM1_BASE - \
JUNO_MEDIA_TZC_PROT_DRAM1_SIZE)
#define JUNO_MEDIA_TZC_PROT_DRAM1_END (JUNO_VPU_TZC_PROT_DRAM1_BASE - 1)
/* Rest of DRAM1 are Non-Secure public buffer */
#define JUNO_NS_DRAM1_PT1_BASE ARM_DRAM1_BASE
#define JUNO_NS_DRAM1_PT1_END (JUNO_MEDIA_TZC_PROT_DRAM1_BASE - 1)
#define JUNO_NS_DRAM1_PT1_SIZE (JUNO_NS_DRAM1_PT1_END - \
JUNO_NS_DRAM1_PT1_BASE + 1)
/* TZC filter flags */
#define JUNO_MEDIA_TZC_NS_DEV_ACCESS (PLAT_ARM_TZC_NS_DEV_ACCESS | \
TZC_REGION_ACCESS_RD(JUNO_TZC400_NSAID_FPGA_MEDIA_SECURE))
/* VPU / GPU /DPU protected access */
#define JUNO_MEDIA_TZC_PROT_ACCESS \
(TZC_REGION_ACCESS_RDWR(JUNO_TZC400_NSAID_FPGA_MEDIA_SECURE) | \
TZC_REGION_ACCESS_WR(TZC400_NSAID_AP))
#define JUNO_VPU_TZC_PROT_ACCESS \
(TZC_REGION_ACCESS_RDWR(JUNO_TZC400_NSAID_FPGA_VIDEO_PROTECTED))
#define JUNO_VPU_TZC_PRIV_ACCESS \
(TZC_REGION_ACCESS_RDWR(JUNO_TZC400_NSAID_FPGA_VIDEO_PRIVATE))
/*******************************************************************************
* Mali-DP650 related constants
******************************************************************************/
/* Base address of DP650 */
#define DP650_BASE 0x6f200000
/* offset to PROT_NSAID register */
#define DP650_PROT_NSAID_OFFSET 0x10004
/* config to PROT_NSAID register */
#define DP650_PROT_NSAID_CONFIG 0x08008888
/*******************************************************************************
* Mali-V550 related constants
******************************************************************************/
/* Base address of V550 */
#define V550_BASE 0x6f030000
/* offset to PROTCTRL register */
#define V550_PROTCTRL_OFFSET 0x0040
/* config to PROTCTRL register */
#define V550_PROTCTRL_CONFIG 0xa8700000
#endif
/* __JUNO_TZMP1_DEF_H__ */
plat/arm/board/juno/platform.mk
View file @
b47f941d
#
# Copyright (c) 2013-201
7
, ARM Limited and Contributors. All rights reserved.
# Copyright (c) 2013-201
8
, ARM Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
...
...
@@ -31,6 +31,13 @@ JUNO_AARCH32_EL3_RUNTIME := 0
$(eval
$(call
assert_boolean,JUNO_AARCH32_EL3_RUNTIME))
$(eval
$(call
add_define,JUNO_AARCH32_EL3_RUNTIME))
# Flag to enable support for TZMP1 on JUNO
JUNO_TZMP1
:=
0
$(eval
$(call
assert_boolean,JUNO_TZMP1))
ifeq
(${JUNO_TZMP1}, 1)
$(eval
$(call
add_define,JUNO_TZMP1))
endif
ifeq
(${JUNO_AARCH32_EL3_RUNTIME}, 1)
# Include BL32 in FIP
NEED_BL32
:=
yes
...
...
plat/arm/common/arm_tzc400.c
View file @
b47f941d
...
...
@@ -18,16 +18,20 @@
/*******************************************************************************
* Initialize the TrustZone Controller for ARM standard platforms.
* Configure:
* - Region 0 with no access;
* - Region 1 with secure access only;
* - the remaining DRAM regions access from the given Non-Secure masters.
*
* When booting an EL3 payload, this is simplified: we configure region 0 with
* secure access only and do not enable any other region.
******************************************************************************/
void
arm_tzc400_setup
(
void
)
void
arm_tzc400_setup
(
const
arm_tzc_regions_info_t
*
tzc_regions
)
{
#ifndef EL3_PAYLOAD_BASE
int
region_index
=
1
;
const
arm_tzc_regions_info_t
*
p
;
const
arm_tzc_regions_info_t
init_tzc_regions
[]
=
{
ARM_TZC_REGIONS_DEF
,
{
0
}
};
#endif
INFO
(
"Configuring TrustZone Controller
\n
"
);
tzc400_init
(
PLAT_ARM_TZC_BASE
);
...
...
@@ -36,42 +40,22 @@ void arm_tzc400_setup(void)
tzc400_disable_filters
();
#ifndef EL3_PAYLOAD_BASE
if
(
tzc_regions
==
NULL
)
p
=
init_tzc_regions
;
else
p
=
tzc_regions
;
/* Region 0 set to no access by default */
tzc400_configure_region0
(
TZC_REGION_S_NONE
,
0
);
/* Region 1 set to cover Secure part of DRAM */
tzc400_configure_region
(
PLAT_ARM_TZC_FILTERS
,
1
,
ARM_AP_TZC_DRAM1_BASE
,
ARM_EL3_TZC_DRAM1_END
,
TZC_REGION_S_RDWR
,
0
);
/* Region 2 set to cover Non-Secure access to 1st DRAM address range.
* Apply the same configuration to given filters in the TZC. */
tzc400_configure_region
(
PLAT_ARM_TZC_FILTERS
,
2
,
ARM_NS_DRAM1_BASE
,
ARM_NS_DRAM1_END
,
ARM_TZC_NS_DRAM_S_ACCESS
,
PLAT_ARM_TZC_NS_DEV_ACCESS
);
/* Region 3 set to cover Non-Secure access to 2nd DRAM address range */
tzc400_configure_region
(
PLAT_ARM_TZC_FILTERS
,
3
,
ARM_DRAM2_BASE
,
ARM_DRAM2_END
,
ARM_TZC_NS_DRAM_S_ACCESS
,
PLAT_ARM_TZC_NS_DEV_ACCESS
);
#if ENABLE_SPM
/*
* Region 4 set to cover Non-Secure access to the communication buffer
* shared with the Secure world.
*/
tzc400_configure_region
(
PLAT_ARM_TZC_FILTERS
,
4
,
ARM_SP_IMAGE_NS_BUF_BASE
,
(
ARM_SP_IMAGE_NS_BUF_BASE
+
ARM_SP_IMAGE_NS_BUF_SIZE
)
-
1
,
TZC_REGION_S_NONE
,
PLAT_ARM_TZC_NS_DEV_ACCESS
);
#endif
/* Rest Regions set according to tzc_regions array */
for
(;
p
->
base
!=
0ULL
;
p
++
)
{
tzc400_configure_region
(
PLAT_ARM_TZC_FILTERS
,
region_index
,
p
->
base
,
p
->
end
,
p
->
sec_attr
,
p
->
nsaid_permissions
);
region_index
++
;
}
INFO
(
"Total %d regions set.
\n
"
,
region_index
);
#else
/* if defined(EL3_PAYLOAD_BASE) */
...
...
@@ -92,5 +76,5 @@ void arm_tzc400_setup(void)
void
plat_arm_security_setup
(
void
)
{
arm_tzc400_setup
();
arm_tzc400_setup
(
NULL
);
}
plat/arm/common/arm_tzc_dmc500.c
View file @
b47f941d
/*
* Copyright (c) 2016, ARM Limited and Contributors. All rights reserved.
* Copyright (c) 2016
-2018
, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
...
...
@@ -12,15 +12,21 @@
/*******************************************************************************
* Initialize the DMC500-TrustZone Controller for ARM standard platforms.
* Configure both the interfaces on Region 0 with no access, Region 1 with
* secure access only, and the remaining DRAM regions access from the
* given Non-Secure masters.
*
* When booting an EL3 payload, this is simplified: we configure region 0 with
* secure access only and do not enable any other region.
******************************************************************************/
void
arm_tzc_dmc500_setup
(
tzc_dmc500_driver_data_t
*
plat_driver_data
)
void
arm_tzc_dmc500_setup
(
tzc_dmc500_driver_data_t
*
plat_driver_data
,
const
arm_tzc_regions_info_t
*
tzc_regions
)
{
#ifndef EL3_PAYLOAD_BASE
int
region_index
=
1
;
const
arm_tzc_regions_info_t
*
p
;
const
arm_tzc_regions_info_t
init_tzc_regions
[]
=
{
ARM_TZC_REGIONS_DEF
,
{
0
}
};
#endif
assert
(
plat_driver_data
);
INFO
(
"Configuring DMC-500 TZ Settings
\n
"
);
...
...
@@ -28,28 +34,23 @@ void arm_tzc_dmc500_setup(tzc_dmc500_driver_data_t *plat_driver_data)
tzc_dmc500_driver_init
(
plat_driver_data
);
#ifndef EL3_PAYLOAD_BASE
if
(
tzc_regions
==
NULL
)
p
=
init_tzc_regions
;
else
p
=
tzc_regions
;
/* Region 0 set to no access by default */
tzc_dmc500_configure_region0
(
TZC_REGION_S_NONE
,
0
);
/* Region 1 set to cover Secure part of DRAM */
tzc_dmc500_configure_region
(
1
,
ARM_AP_TZC_DRAM1_BASE
,
ARM_EL3_TZC_DRAM1_END
,
TZC_REGION_S_RDWR
,
0
);
/* Rest Regions set according to tzc_regions array */
for
(;
p
->
base
!=
0ULL
;
p
++
)
{
tzc_dmc500_configure_region
(
region_index
,
p
->
base
,
p
->
end
,
p
->
sec_attr
,
p
->
nsaid_permissions
);
region_index
++
;
}
/* Region 2 set to cover Non-Secure access to 1st DRAM address range.*/
tzc_dmc500_configure_region
(
2
,
ARM_NS_DRAM1_BASE
,
ARM_NS_DRAM1_END
,
ARM_TZC_NS_DRAM_S_ACCESS
,
PLAT_ARM_TZC_NS_DEV_ACCESS
);
INFO
(
"Total %d regions set.
\n
"
,
region_index
);
/* Region 3 set to cover Non-Secure access to 2nd DRAM address range */
tzc_dmc500_configure_region
(
3
,
ARM_DRAM2_BASE
,
ARM_DRAM2_END
,
ARM_TZC_NS_DRAM_S_ACCESS
,
PLAT_ARM_TZC_NS_DEV_ACCESS
);
#else
/* Allow secure access only to DRAM for EL3 payloads */
tzc_dmc500_configure_region0
(
TZC_REGION_S_RDWR
,
0
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment