aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile8
-rw-r--r--include/lib/xlat_tables/xlat_tables_v2.h12
-rw-r--r--include/lib/xlat_tables/xlat_tables_v2_helpers.h62
-rw-r--r--include/plat/arm/common/plat_arm.h5
-rw-r--r--lib/aarch64/cache_helpers.S6
-rw-r--r--lib/xlat_tables_v2/ro_xlat_tables.mk37
-rw-r--r--lib/xlat_tables_v2/xlat_tables.mk6
-rw-r--r--lib/xlat_tables_v2/xlat_tables_context.c78
-rw-r--r--make_helpers/defaults.mk7
-rw-r--r--plat/arm/board/fvp/platform.mk13
-rw-r--r--plat/arm/board/juno/platform.mk8
-rw-r--r--plat/arm/common/arm_bl31_setup.c5
-rw-r--r--plat/arm/common/arm_common.c20
-rw-r--r--plat/arm/common/sp_min/arm_sp_min_setup.c4
-rw-r--r--plat/arm/common/tsp/arm_tsp_setup.c6
15 files changed, 264 insertions, 13 deletions
diff --git a/Makefile b/Makefile
index 11b0753c4..39e8a0046 100644
--- a/Makefile
+++ b/Makefile
@@ -621,6 +621,12 @@ ifeq ($(MEASURED_BOOT),1)
endif
endif
+ifeq (${ARM_XLAT_TABLES_LIB_V1}, 1)
+ ifeq (${ALLOW_RO_XLAT_TABLES}, 1)
+ $(error "ALLOW_RO_XLAT_TABLES requires translation tables library v2")
+ endif
+endif
+
################################################################################
# Process platform overrideable behaviour
################################################################################
@@ -748,6 +754,7 @@ endif
# Build options checks
################################################################################
+$(eval $(call assert_boolean,ALLOW_RO_XLAT_TABLES))
$(eval $(call assert_boolean,COLD_BOOT_SINGLE_CPU))
$(eval $(call assert_boolean,CREATE_KEYS))
$(eval $(call assert_boolean,CTX_INCLUDE_AARCH32_REGS))
@@ -815,6 +822,7 @@ endif
# platform to overwrite the default options
################################################################################
+$(eval $(call add_define,ALLOW_RO_XLAT_TABLES))
$(eval $(call add_define,ARM_ARCH_MAJOR))
$(eval $(call add_define,ARM_ARCH_MINOR))
$(eval $(call add_define,COLD_BOOT_SINGLE_CPU))
diff --git a/include/lib/xlat_tables/xlat_tables_v2.h b/include/lib/xlat_tables/xlat_tables_v2.h
index 0e099987e..a80fab073 100644
--- a/include/lib/xlat_tables/xlat_tables_v2.h
+++ b/include/lib/xlat_tables/xlat_tables_v2.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -345,6 +345,16 @@ int xlat_change_mem_attributes_ctx(const xlat_ctx_t *ctx, uintptr_t base_va,
size_t size, uint32_t attr);
int xlat_change_mem_attributes(uintptr_t base_va, size_t size, uint32_t attr);
+#if PLAT_RO_XLAT_TABLES
+/*
+ * Change the memory attributes of the memory region encompassing the higher
+ * level translation tables to secure read-only data.
+ *
+ * Return 0 on success, a negative error code on error.
+ */
+int xlat_make_tables_readonly(void);
+#endif
+
/*
* Query the memory attributes of a memory page in a set of translation tables.
*
diff --git a/include/lib/xlat_tables/xlat_tables_v2_helpers.h b/include/lib/xlat_tables/xlat_tables_v2_helpers.h
index b17b71a87..c88fa4dd5 100644
--- a/include/lib/xlat_tables/xlat_tables_v2_helpers.h
+++ b/include/lib/xlat_tables/xlat_tables_v2_helpers.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -70,6 +70,9 @@ struct xlat_ctx {
*/
uint64_t (*tables)[XLAT_TABLE_ENTRIES];
int tables_num;
+#if PLAT_RO_XLAT_TABLES
+ bool readonly_tables;
+#endif
/*
* Keep track of how many regions are mapped in each table. The base
* table can't be unmapped so it isn't needed to keep track of it.
@@ -122,6 +125,14 @@ struct xlat_ctx {
/* do nothing */
#endif /* PLAT_XLAT_TABLES_DYNAMIC */
+#if PLAT_RO_XLAT_TABLES
+#define XLAT_CTX_INIT_TABLE_ATTR() \
+ .readonly_tables = false,
+#else
+#define XLAT_CTX_INIT_TABLE_ATTR()
+ /* do nothing */
+#endif
+
#define REGISTER_XLAT_CONTEXT_FULL_SPEC(_ctx_name, _mmap_count, \
_xlat_tables_count, _virt_addr_space_size, \
_phy_addr_space_size, _xlat_regime, _section_name)\
@@ -142,22 +153,63 @@ struct xlat_ctx {
XLAT_ALLOC_DYNMAP_STRUCT(_ctx_name, _xlat_tables_count) \
\
static xlat_ctx_t _ctx_name##_xlat_ctx = { \
- .va_max_address = (_virt_addr_space_size) - 1UL, \
.pa_max_address = (_phy_addr_space_size) - 1ULL, \
+ .va_max_address = (_virt_addr_space_size) - 1UL, \
.mmap = _ctx_name##_mmap, \
.mmap_num = (_mmap_count), \
- .base_level = GET_XLAT_TABLE_LEVEL_BASE(_virt_addr_space_size),\
+ .tables = _ctx_name##_xlat_tables, \
+ .tables_num = _xlat_tables_count, \
+ XLAT_CTX_INIT_TABLE_ATTR() \
+ XLAT_REGISTER_DYNMAP_STRUCT(_ctx_name) \
+ .next_table = 0, \
.base_table = _ctx_name##_base_xlat_table, \
.base_table_entries = \
GET_NUM_BASE_LEVEL_ENTRIES(_virt_addr_space_size),\
+ .max_pa = 0U, \
+ .max_va = 0U, \
+ .base_level = GET_XLAT_TABLE_LEVEL_BASE(_virt_addr_space_size),\
+ .initialized = false, \
+ .xlat_regime = (_xlat_regime) \
+ }
+
+#define REGISTER_XLAT_CONTEXT_RO_BASE_TABLE(_ctx_name, _mmap_count, \
+ _xlat_tables_count, _virt_addr_space_size, \
+ _phy_addr_space_size, _xlat_regime, _section_name)\
+ CASSERT(CHECK_PHY_ADDR_SPACE_SIZE(_phy_addr_space_size), \
+ assert_invalid_physical_addr_space_sizefor_##_ctx_name);\
+ \
+ static mmap_region_t _ctx_name##_mmap[_mmap_count + 1]; \
+ \
+ static uint64_t _ctx_name##_xlat_tables[_xlat_tables_count] \
+ [XLAT_TABLE_ENTRIES] \
+ __aligned(XLAT_TABLE_SIZE) __section(_section_name); \
+ \
+ static uint64_t _ctx_name##_base_xlat_table \
+ [GET_NUM_BASE_LEVEL_ENTRIES(_virt_addr_space_size)] \
+ __aligned(GET_NUM_BASE_LEVEL_ENTRIES(_virt_addr_space_size)\
+ * sizeof(uint64_t)) \
+ __section(".rodata"); \
+ \
+ XLAT_ALLOC_DYNMAP_STRUCT(_ctx_name, _xlat_tables_count) \
+ \
+ static xlat_ctx_t _ctx_name##_xlat_ctx = { \
+ .pa_max_address = (_phy_addr_space_size) - 1ULL, \
+ .va_max_address = (_virt_addr_space_size) - 1UL, \
+ .mmap = _ctx_name##_mmap, \
+ .mmap_num = (_mmap_count), \
.tables = _ctx_name##_xlat_tables, \
.tables_num = _xlat_tables_count, \
+ XLAT_CTX_INIT_TABLE_ATTR() \
XLAT_REGISTER_DYNMAP_STRUCT(_ctx_name) \
- .xlat_regime = (_xlat_regime), \
+ .next_table = 0, \
+ .base_table = _ctx_name##_base_xlat_table, \
+ .base_table_entries = \
+ GET_NUM_BASE_LEVEL_ENTRIES(_virt_addr_space_size),\
.max_pa = 0U, \
.max_va = 0U, \
- .next_table = 0, \
+ .base_level = GET_XLAT_TABLE_LEVEL_BASE(_virt_addr_space_size),\
.initialized = false, \
+ .xlat_regime = (_xlat_regime) \
}
#endif /*__ASSEMBLER__*/
diff --git a/include/plat/arm/common/plat_arm.h b/include/plat/arm/common/plat_arm.h
index 025a64fa2..862e73af1 100644
--- a/include/plat/arm/common/plat_arm.h
+++ b/include/plat/arm/common/plat_arm.h
@@ -237,6 +237,11 @@ int arm_get_mbedtls_heap(void **heap_addr, size_t *heap_size);
void arm_free_init_memory(void);
/*
+ * Make the higher level translation tables read-only
+ */
+void arm_xlat_make_tables_readonly(void);
+
+/*
* Mandatory functions required in ARM standard platforms
*/
unsigned int plat_arm_get_cluster_core_count(u_register_t mpidr);
diff --git a/lib/aarch64/cache_helpers.S b/lib/aarch64/cache_helpers.S
index 9ef8ca79b..de9c8e4f0 100644
--- a/lib/aarch64/cache_helpers.S
+++ b/lib/aarch64/cache_helpers.S
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013-2019, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -30,7 +30,7 @@ loop_\op:
dc \op, x0
add x0, x0, x2
cmp x0, x1
- b.lo loop_\op
+ b.lo loop_\op
dsb sy
exit_loop_\op:
ret
@@ -140,7 +140,7 @@ loop3_\_op:
level_done:
add x10, x10, #2 // increment cache number
cmp x3, x10
- b.hi loop1
+ b.hi loop1
msr csselr_el1, xzr // select cache level 0 in csselr
dsb sy // barrier to complete final cache operation
isb
diff --git a/lib/xlat_tables_v2/ro_xlat_tables.mk b/lib/xlat_tables_v2/ro_xlat_tables.mk
new file mode 100644
index 000000000..7991e1afd
--- /dev/null
+++ b/lib/xlat_tables_v2/ro_xlat_tables.mk
@@ -0,0 +1,37 @@
+#
+# Copyright (c) 2020, ARM Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+ifeq (${USE_DEBUGFS}, 1)
+ $(error "Debugfs requires functionality from the dynamic translation \
+ library and is incompatible with ALLOW_RO_XLAT_TABLES.")
+endif
+
+ifeq (${ARCH},aarch32)
+ ifeq (${RESET_TO_SP_MIN},1)
+ $(error "RESET_TO_SP_MIN requires functionality from the dynamic \
+ translation library and is incompatible with \
+ ALLOW_RO_XLAT_TABLES.")
+ endif
+else # if AArch64
+ ifeq (${PLAT},tegra)
+ $(error "Tegra requires functionality from the dynamic translation \
+ library and is incompatible with ALLOW_RO_XLAT_TABLES.")
+ endif
+ ifeq (${RESET_TO_BL31},1)
+ $(error "RESET_TO_BL31 requires functionality from the dynamic \
+ translation library and is incompatible with \
+ ALLOW_RO_XLAT_TABLES.")
+ endif
+ ifeq (${SPD},trusty)
+ $(error "Trusty requires functionality from the dynamic translation \
+ library and is incompatible with ALLOW_RO_XLAT_TABLES.")
+ endif
+ ifeq (${SPM_MM},1)
+ $(error "SPM_MM requires functionality to change memory region \
+ attributes, which is not possible once the translation tables \
+ have been made read-only.")
+ endif
+endif
diff --git a/lib/xlat_tables_v2/xlat_tables.mk b/lib/xlat_tables_v2/xlat_tables.mk
index c946315bf..bcc3e68d8 100644
--- a/lib/xlat_tables_v2/xlat_tables.mk
+++ b/lib/xlat_tables_v2/xlat_tables.mk
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
+# Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
@@ -13,3 +13,7 @@ XLAT_TABLES_LIB_SRCS := $(addprefix lib/xlat_tables_v2/, \
XLAT_TABLES_LIB_V2 := 1
$(eval $(call add_define,XLAT_TABLES_LIB_V2))
+
+ifeq (${ALLOW_RO_XLAT_TABLES}, 1)
+ include lib/xlat_tables_v2/ro_xlat_tables.mk
+endif
diff --git a/lib/xlat_tables_v2/xlat_tables_context.c b/lib/xlat_tables_v2/xlat_tables_context.c
index f4b64b33f..adca57875 100644
--- a/lib/xlat_tables_v2/xlat_tables_context.c
+++ b/lib/xlat_tables_v2/xlat_tables_context.c
@@ -1,9 +1,10 @@
/*
- * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
+#include <arch_helpers.h>
#include <assert.h>
#include <platform_def.h>
@@ -24,8 +25,14 @@ uint64_t mmu_cfg_params[MMU_CFG_PARAM_MAX];
* Allocate and initialise the default translation context for the BL image
* currently executing.
*/
+#if PLAT_RO_XLAT_TABLES
+REGISTER_XLAT_CONTEXT_RO_BASE_TABLE(tf, MAX_MMAP_REGIONS, MAX_XLAT_TABLES,
+ PLAT_VIRT_ADDR_SPACE_SIZE, PLAT_PHY_ADDR_SPACE_SIZE,
+ EL_REGIME_INVALID, "xlat_table");
+#else
REGISTER_XLAT_CONTEXT(tf, MAX_MMAP_REGIONS, MAX_XLAT_TABLES,
PLAT_VIRT_ADDR_SPACE_SIZE, PLAT_PHY_ADDR_SPACE_SIZE);
+#endif
void mmap_add_region(unsigned long long base_pa, uintptr_t base_va, size_t size,
unsigned int attr)
@@ -119,6 +126,75 @@ int xlat_change_mem_attributes(uintptr_t base_va, size_t size, uint32_t attr)
return xlat_change_mem_attributes_ctx(&tf_xlat_ctx, base_va, size, attr);
}
+#if PLAT_RO_XLAT_TABLES
+/* Change the memory attributes of the descriptors which resolve the address
+ * range that belongs to the translation tables themselves, which are by default
+ * mapped as part of read-write data in the BL image's memory.
+ *
+ * Since the translation tables map themselves via these level 3 (page)
+ * descriptors, any change applied to them with the MMU on would introduce a
+ * chicken and egg problem because of the break-before-make sequence.
+ * Eventually, it would reach the descriptor that resolves the very table it
+ * belongs to and the invalidation (break step) would cause the subsequent write
+ * (make step) to it to generate an MMU fault. Therefore, the MMU is disabled
+ * before making the change.
+ *
+ * No assumption is made about what data this function needs, therefore all the
+ * caches are flushed in order to ensure coherency. A future optimization would
+ * be to only flush the required data to main memory.
+ */
+int xlat_make_tables_readonly(void)
+{
+ assert(tf_xlat_ctx.initialized == true);
+#ifdef __aarch64__
+ if (tf_xlat_ctx.xlat_regime == EL1_EL0_REGIME) {
+ disable_mmu_el1();
+ } else if (tf_xlat_ctx.xlat_regime == EL3_REGIME) {
+ disable_mmu_el3();
+ } else {
+ assert(tf_xlat_ctx.xlat_regime == EL2_REGIME);
+ return -1;
+ }
+
+ /* Flush all caches. */
+ dcsw_op_all(DCCISW);
+#else /* !__aarch64__ */
+ assert(tf_xlat_ctx.xlat_regime == EL1_EL0_REGIME);
+ /* On AArch32, we flush the caches before disabling the MMU. The reason
+ * for this is that the dcsw_op_all AArch32 function pushes some
+ * registers onto the stack under the assumption that it is writing to
+ * cache, which is not true with the MMU off. This would result in the
+ * stack becoming corrupted and a wrong/junk value for the LR being
+ * restored at the end of the routine.
+ */
+ dcsw_op_all(DC_OP_CISW);
+ disable_mmu_secure();
+#endif
+
+ int rc = xlat_change_mem_attributes_ctx(&tf_xlat_ctx,
+ (uintptr_t)tf_xlat_ctx.tables,
+ tf_xlat_ctx.tables_num * XLAT_TABLE_SIZE,
+ MT_RO_DATA | MT_SECURE);
+
+#ifdef __aarch64__
+ if (tf_xlat_ctx.xlat_regime == EL1_EL0_REGIME) {
+ enable_mmu_el1(0U);
+ } else {
+ assert(tf_xlat_ctx.xlat_regime == EL3_REGIME);
+ enable_mmu_el3(0U);
+ }
+#else /* !__aarch64__ */
+ enable_mmu_svc_mon(0U);
+#endif
+
+ if (rc == 0) {
+ tf_xlat_ctx.readonly_tables = true;
+ }
+
+ return rc;
+}
+#endif /* PLAT_RO_XLAT_TABLES */
+
/*
* If dynamic allocation of new regions is disabled then by the time we call the
* function enabling the MMU, we'll have registered all the memory regions to
diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk
index e8e990d45..60958a1d1 100644
--- a/make_helpers/defaults.mk
+++ b/make_helpers/defaults.mk
@@ -207,6 +207,13 @@ USE_FCONF_BASED_IO := 0
# Build option to choose whether Trusted Firmware uses library at ROM
USE_ROMLIB := 0
+# Build option to choose whether the xlat tables of BL images can be read-only.
+# Note that this only serves as a higher level option to PLAT_RO_XLAT_TABLES,
+# which is the per BL-image option that actually enables the read-only tables
+# API. The reason for having this additional option is to have a common high
+# level makefile where we can check for incompatible features/build options.
+ALLOW_RO_XLAT_TABLES := 0
+
# Chain of trust.
COT := tbbr
diff --git a/plat/arm/board/fvp/platform.mk b/plat/arm/board/fvp/platform.mk
index 4176968f8..05c11ce52 100644
--- a/plat/arm/board/fvp/platform.mk
+++ b/plat/arm/board/fvp/platform.mk
@@ -292,7 +292,7 @@ ifeq (${ARCH},aarch32)
ifeq (${RESET_TO_SP_MIN},1)
BL32_CFLAGS += -DPLAT_XLAT_TABLES_DYNAMIC=1
endif
-else # if AArch64
+else # AArch64
ifeq (${RESET_TO_BL31},1)
BL31_CFLAGS += -DPLAT_XLAT_TABLES_DYNAMIC=1
endif
@@ -301,6 +301,17 @@ else # if AArch64
endif
endif
+ifeq (${ALLOW_RO_XLAT_TABLES}, 1)
+ ifeq (${ARCH},aarch32)
+ BL32_CFLAGS += -DPLAT_RO_XLAT_TABLES=1
+ else # AArch64
+ BL31_CFLAGS += -DPLAT_RO_XLAT_TABLES=1
+ ifeq (${SPD},tspd)
+ BL32_CFLAGS += -DPLAT_RO_XLAT_TABLES=1
+ endif
+ endif
+endif
+
ifeq (${USE_DEBUGFS},1)
BL31_CFLAGS += -DPLAT_XLAT_TABLES_DYNAMIC=1
endif
diff --git a/plat/arm/board/juno/platform.mk b/plat/arm/board/juno/platform.mk
index 27650d266..f07c1b163 100644
--- a/plat/arm/board/juno/platform.mk
+++ b/plat/arm/board/juno/platform.mk
@@ -155,6 +155,14 @@ else
endif
endif
+ifeq (${ALLOW_RO_XLAT_TABLES}, 1)
+ ifeq (${JUNO_AARCH32_EL3_RUNTIME}, 1)
+ BL32_CFLAGS += -DPLAT_RO_XLAT_TABLES=1
+ else
+ BL31_CFLAGS += -DPLAT_RO_XLAT_TABLES=1
+ endif
+endif
+
# Add the FDT_SOURCES and options for Dynamic Config
FDT_SOURCES += plat/arm/board/juno/fdts/${PLAT}_fw_config.dts
TB_FW_CONFIG := ${BUILD_PLAT}/fdts/${PLAT}_fw_config.dtb
diff --git a/plat/arm/common/arm_bl31_setup.c b/plat/arm/common/arm_bl31_setup.c
index c135d7f2d..85535c11a 100644
--- a/plat/arm/common/arm_bl31_setup.c
+++ b/plat/arm/common/arm_bl31_setup.c
@@ -256,9 +256,14 @@ void arm_bl31_plat_runtime_setup(void)
/* Initialize the runtime console */
arm_console_runtime_init();
+
#if RECLAIM_INIT_CODE
arm_free_init_memory();
#endif
+
+#if PLAT_RO_XLAT_TABLES
+ arm_xlat_make_tables_readonly();
+#endif
}
#if RECLAIM_INIT_CODE
diff --git a/plat/arm/common/arm_common.c b/plat/arm/common/arm_common.c
index d1e9620de..d1eee08d1 100644
--- a/plat/arm/common/arm_common.c
+++ b/plat/arm/common/arm_common.c
@@ -25,6 +25,26 @@
* conflicts with the definition in plat/common. */
#pragma weak plat_get_syscnt_freq2
+/*******************************************************************************
+ * Changes the memory attributes for the region of mapped memory where the BL
+ * image's translation tables are located such that the tables will have
+ * read-only permissions.
+ ******************************************************************************/
+#if PLAT_RO_XLAT_TABLES
+void arm_xlat_make_tables_readonly(void)
+{
+ int rc = xlat_make_tables_readonly();
+
+ if (rc != 0) {
+ ERROR("Failed to make translation tables read-only at EL%u.\n",
+ get_current_el());
+ panic();
+ }
+
+ INFO("Translation tables are now read-only at EL%u.\n",
+ get_current_el());
+}
+#endif
void arm_setup_romlib(void)
{
diff --git a/plat/arm/common/sp_min/arm_sp_min_setup.c b/plat/arm/common/sp_min/arm_sp_min_setup.c
index 0cc746b10..cbbdfa21b 100644
--- a/plat/arm/common/sp_min/arm_sp_min_setup.c
+++ b/plat/arm/common/sp_min/arm_sp_min_setup.c
@@ -167,6 +167,10 @@ void arm_sp_min_plat_runtime_setup(void)
{
/* Initialize the runtime console */
arm_console_runtime_init();
+
+#if PLAT_RO_XLAT_TABLES
+ arm_xlat_make_tables_readonly();
+#endif
}
/*******************************************************************************
diff --git a/plat/arm/common/tsp/arm_tsp_setup.c b/plat/arm/common/tsp/arm_tsp_setup.c
index aefdf89c7..5dd964de2 100644
--- a/plat/arm/common/tsp/arm_tsp_setup.c
+++ b/plat/arm/common/tsp/arm_tsp_setup.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@@ -79,4 +79,8 @@ void tsp_plat_arch_setup(void)
setup_page_tables(bl_regions, plat_arm_get_mmap());
enable_mmu_el1(0);
+
+#if PLAT_RO_XLAT_TABLES
+ arm_xlat_make_tables_readonly();
+#endif
}