MLK-10958 imx: mx6ul support Bus Encryption Engine
authorYe Li <ye.li@nxp.com>
Fri, 23 Mar 2018 08:14:13 +0000 (01:14 -0700)
committerYe Li <ye.li@nxp.com>
Wed, 28 Apr 2021 20:49:33 +0000 (13:49 -0700)
This patch is to support Bus Encryption Engine(BEE) for i.MX 6UL.
Supported feature:
1. SNVS key and soft key
2. CTR and ECB mode
3. Specify address region to bee.

Two commands are included:
bee init [key] [mode] [start] [end] - BEE block initial
    "Example: bee init 1 1 0x80000000 0x80010000\n"
bee test [region]
    "Example: bee test 1\n"

Mapping:
[0x10000000 - (0x10000000 + size - 1)] : [start - (start + size - 1)]
[0x30000000 - (0x30000000 + IRAM_SIZE - 1)] : [IRAM_BASE_ADDR -
(IRAM_BASE_ADDR + IRAM_SIZE - 1)]

Whatever start is, start - (start + size -1) will be fixed mapping to
0x10000000 - (0x10000000 + size - 1)

Since default AES region's protected size is SZ_512M, so
on mx6ul evk board, you can not simply run 'bee init', it will
overlap with uboot execution environment, you can use
'bee init 0 0 0x80000000 0x81000000'.

If want to use bee, Need to define CONFIG_CMD_BEE in board configuration
header file, since CONFIG_CMD_BEE default is not enabled.

This patch also checks fuse bit 25 of bank 0 word 4 before initialize bee.
The bit: 0 means bee enabled, 1 means bee disabled.

Signed-off-by: Peng Fan <Peng.Fan@freescale.com>
Signed-off-by: Ye Li <ye.li@nxp.com>
(cherry picked from commit 9d592121bebbdb9ded0009a2703c7cab01edfa70)
(cherry picked from commit cd03ba8327d46178900a63310d5ef3edd8034350)
(cherry picked from commit 6c8ed4a65e73747f38d2e16e8a2b8cb0c7cce4af)

arch/arm/include/asm/arch-mx6/mx6_bee.h [new file with mode: 0644]
arch/arm/mach-imx/mx6/Kconfig
arch/arm/mach-imx/mx6/Makefile
arch/arm/mach-imx/mx6/bee.c [new file with mode: 0644]

diff --git a/arch/arm/include/asm/arch-mx6/mx6_bee.h b/arch/arm/include/asm/arch-mx6/mx6_bee.h
new file mode 100644 (file)
index 0000000..eb51dfe
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 Freescale Semiconductor, Inc.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+
+#define GPR0           0x0
+#define GPR1           0x4
+#define GPR2           0x8
+#define GPR3           0xC
+#define GPR4           0x10
+#define GPR5           0x14
+#define GPR6           0x18
+#define GPR7           0x1C
+#define GPR8           0x20
+#define GPR9           0x24
+#define GPR10          0x28
+#define GPR11          0x2C
+
+#define GPR0_CTRL_CLK_EN_LOCK  (1 << 31)
+#define GPR0_CTRL_CLK_EN       (1 << 15)
+#define GPR0_CTRL_SFTRST_N_LOCK        (1 << 30)
+#define GPR0_CTRL_SFTRST       (0 << 14)
+#define GPR0_CTRL_SFTRST_N     (1 << 14)
+#define GPR0_CTRL_AES_MODE_LOCK        (1 << 29)
+#define GPR0_CTRL_AES_MODE_ECB (0 << 13)
+#define GPR0_CTRL_AES_MODE_CTR (1 << 13)
+#define GPR0_SEC_LEVEL_LOCK    (3 << 24)
+#define GPR0_SEC_LEVEL         (3 << 8)
+#define GPR0_AES_KEY_SEL_LOCK  (1 << 20)
+#define GPR0_AES_KEY_SEL_SNVS  (0 << 4)
+#define GPR0_AES_KEY_SEL_SOFT  (1 << 4)
+#define GPR0_BEE_ENABLE_LOCK   (1 << 16)
+#define GPR0_BEE_ENABLE                (1 << 0)
+
+/*
+ * SECURITY LEVEL
+ *        Non-Secure User |  Non-Secure Spvr | Secure User | Secure Spvr
+ * Level
+ * (0)00      RD + WR           RD + WR          RD + WR       RD + WR
+ * (1)01      None              RD + WR          RD + WR       RD + WR
+ * (2)10      None              None             RD + WR       RD + WR
+ * (3)11      None              None             None          RD + WR
+ */
+#define GPR0_SEC_LEVEL_0       (0 << 8)
+#define GPR0_SEC_LEVEL_1       (1 << 8)
+#define GPR0_SEC_LEVEL_2       (2 << 8)
+#define GPR0_SEC_LEVEL_3       (3 << 8)
index af91d33..06100ff 100644 (file)
@@ -115,6 +115,12 @@ config LDO_BYPASS_CHECK
          "fsl,ldo-bypass" property. When the property is set, board relevant 
          PMIC settings are called to adjust for LDO bypass. 
 
+config CMD_BEE
+       bool "Enable commands for Bus Encryption Engine(BEE)"
+       depends on MX6UL
+       help
+         Set "Y" to enable the bee commands 
+
 choice
        prompt "MX6 board select"
        optional
index 7ea8f91..a6e4539 100644 (file)
@@ -4,6 +4,8 @@
 # Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 #
 # (C) Copyright 2011 Freescale Semiconductor, Inc.
+# Copyright 2018 NXP
+#
 
 obj-y  := soc.o clock.o
 obj-$(CONFIG_IMX_MODULE_FUSE) += module_fuse.o
@@ -11,3 +13,6 @@ obj-$(CONFIG_SPL_BUILD)            += ddr.o
 obj-$(CONFIG_MP)             += mp.o
 obj-$(CONFIG_MX6UL_LITESOM)  += litesom.o
 obj-$(CONFIG_MX6UL_OPOS6UL)  += opos6ul.o
+ifdef CONFIG_MX6UL
+obj-$(CONFIG_CMD_BEE)        += bee.o
+endif
diff --git a/arch/arm/mach-imx/mx6/bee.c b/arch/arm/mach-imx/mx6/bee.c
new file mode 100644 (file)
index 0000000..bb88661
--- /dev/null
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2015-2016 Freescale Semiconductor, Inc.
+ * Copyright 2018 NXP
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <asm/io.h>
+#include <asm/arch/mx6_bee.h>
+#include <linux/errno.h>
+#include <asm/system.h>
+#include <common.h>
+#include <command.h>
+#include <fuse.h>
+#include <asm/arch/sys_proto.h>
+#include <cpu_func.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#if (defined(CONFIG_SYS_DCACHE_OFF) || defined(CONFIG_SYS_ICACHE_OFF))
+#error "Bee needs Cache Open"
+#endif
+
+struct bee_parameters {
+       int key_method;
+       int mode;
+       u32 start1;
+       u32 size1;
+       u32 start2;
+       u32 size2;
+};
+
+#define SOFT_KEY 0
+#define SNVS_KEY 1
+
+#define ECB_MODE 0
+#define CTR_MODE 1
+
+#define AES_REGION0_ADDR       0x10000000
+#define AES_REGION1_ADDR       0x30000000
+
+static struct bee_parameters para;
+static int bee_inited;
+
+union key_soft {
+       u8 s_key[16];
+       u32 b_key[4];
+};
+
+union key_soft key_bad;
+
+/* software version */
+u8 hw_get_random_byte(void)
+{
+       static u32 lcg_state;
+       static u32 nb_soft = 9876543;
+#define MAX_SOFT_RNG 1024
+       static const u32 a = 1664525;
+       static const u32 c = 1013904223;
+       nb_soft = (nb_soft + 1) % MAX_SOFT_RNG;
+       lcg_state = (a * lcg_state + c);
+       return (u8) (lcg_state >> 24);
+}
+
+/*
+ * Lock bee GPR0 bits
+ * Only reset can release these bits.
+ */
+static int bee_lock(void)
+{
+       int val;
+
+       val = readl(BEE_BASE_ADDR + GPR0);
+       val |= (GPR0_CTRL_CLK_EN_LOCK | GPR0_CTRL_SFTRST_N_LOCK |
+               GPR0_CTRL_AES_MODE_LOCK | GPR0_SEC_LEVEL_LOCK |
+               GPR0_AES_KEY_SEL_LOCK | GPR0_BEE_ENABLE_LOCK);
+       writel(val, BEE_BASE_ADDR + GPR0);
+
+       return 0;
+}
+
+/* Only check bee enable lock is enough */
+static int bee_locked(void)
+{
+       int val;
+
+       val = readl(BEE_BASE_ADDR + GPR0);
+
+       return val & GPR0_BEE_ENABLE_LOCK ? 1 : 0;
+}
+
+int bee_init(struct bee_parameters *p)
+{
+       int i;
+       union key_soft *key = &key_bad;
+       u32 value;
+
+       if (bee_locked()) {
+               printf("BEE already enabled and locked.\n");
+               return CMD_RET_FAILURE;
+       }
+
+       /* CLKGATE, SFTRST */
+       writel(GPR0_CTRL_CLK_EN | GPR0_CTRL_SFTRST_N, BEE_BASE_ADDR + GPR0);
+       /* OFFSET_ADDR0 */
+       writel(p->start1 >> 16, BEE_BASE_ADDR + GPR1);
+       /*
+        * OFFSET_ADDR1
+        * Default protect IRAM region, if what you want to protect
+        * bigger that 512M which is the max size that one AES region
+        * can protect, we need AES region 1 to cover.
+        */
+       writel(p->start2 >> 16, BEE_BASE_ADDR + GPR2);
+
+       if (p->key_method == SOFT_KEY) {
+               for (i = 0; i < 16; i++)
+                       key->s_key[i] = hw_get_random_byte();
+               /* AES 128 key from software */
+               /* aes0_key0_w0 */
+               writel(key->b_key[0], BEE_BASE_ADDR + GPR3);
+               /* aes0_key0_w1 */
+               writel(key->b_key[1], BEE_BASE_ADDR + GPR4);
+               /* aes0_key0_w2 */
+               writel(key->b_key[2], BEE_BASE_ADDR + GPR5);
+               /* aes0_key0_w3 */
+               writel(key->b_key[3], BEE_BASE_ADDR + GPR6);
+       }
+
+       if (p->mode == ECB_MODE) {
+               value = GPR0_CTRL_CLK_EN | GPR0_CTRL_SFTRST_N |
+                       GPR0_SEC_LEVEL_3 | GPR0_AES_KEY_SEL_SNVS |
+                       GPR0_BEE_ENABLE | GPR0_CTRL_AES_MODE_ECB;
+               if (p->key_method == SOFT_KEY)
+                       value = GPR0_CTRL_CLK_EN | GPR0_CTRL_SFTRST_N |
+                               GPR0_SEC_LEVEL_3 | GPR0_AES_KEY_SEL_SOFT |
+                               GPR0_BEE_ENABLE | GPR0_CTRL_AES_MODE_ECB;
+               writel(value, BEE_BASE_ADDR + GPR0);
+       } else {
+               for (i = 0; i < 16; i++)
+                       key->s_key[i] = hw_get_random_byte();
+               /* aes_key1_w0 */
+               writel(key->b_key[0], BEE_BASE_ADDR + GPR8);
+               /* aes_key1_w1 */
+               writel(key->b_key[1], BEE_BASE_ADDR + GPR9);
+               /* aes_key1_w2 */
+               writel(key->b_key[2], BEE_BASE_ADDR + GPR10);
+               /* aes_key1_w3 */
+               writel(key->b_key[3], BEE_BASE_ADDR + GPR11);
+
+               value = GPR0_CTRL_CLK_EN | GPR0_CTRL_SFTRST_N |
+                       GPR0_SEC_LEVEL_3 | GPR0_AES_KEY_SEL_SNVS |
+                       GPR0_BEE_ENABLE | GPR0_CTRL_AES_MODE_CTR;
+               if (p->key_method == SOFT_KEY)
+                       value = GPR0_CTRL_CLK_EN | GPR0_CTRL_SFTRST_N |
+                               GPR0_SEC_LEVEL_3 | GPR0_AES_KEY_SEL_SOFT |
+                               GPR0_BEE_ENABLE | GPR0_CTRL_AES_MODE_CTR;
+               writel(value, BEE_BASE_ADDR + GPR0);
+       }
+
+       bee_lock();
+
+       printf("BEE is settings as: %s mode, %s %d key\n",
+              (p->mode == ECB_MODE) ? "ECB" : "CTR",
+              (p->key_method == SOFT_KEY) ? "SOFT" : "SNVS HW",
+              (p->mode == ECB_MODE) ? 128 : 256);
+
+       return CMD_RET_SUCCESS;
+}
+
+int bee_test(struct bee_parameters *p, int region)
+{
+       u32 result = 0, range, address;
+       int i, val;
+       /*
+        * Test instruction running in AES Region:
+        * int test(void)
+        * {
+        *      return 0x55aa55aa;
+        * }
+        * Assemble:
+        * 0xe59f0000: ldr r0, [pc]
+        * 0xe12fff1e: bx lr
+        * 0x55aa55aa: 0x55aa55aa
+        */
+       u32 inst[3] = {0xe59f0000, 0xe12fff1e, 0x55aa55aa};
+
+       /* Cache enabled? */
+       if ((get_cr() & (CR_I | CR_C)) != (CR_I | CR_C)) {
+               printf("Enable dcache and icache first!\n");
+               return CMD_RET_FAILURE;
+       }
+
+       printf("Test Region %d\nBegin Data test: Writing... ", region);
+
+       range = (region == 0) ? p->size1 : p->size2;
+       address = (region == 0) ? AES_REGION0_ADDR : AES_REGION1_ADDR;
+       for (i = 0; i < range; i = i + 4)
+               writel(i, address + i);
+
+       printf("Finshed Write!\n");
+
+       flush_dcache_range(address, address + range);
+
+       printf("Reading... ");
+       for (i = 0; i < range; i = i + 4) {
+               val = readl(address + i);
+               if (val != i)
+                       result++;
+       }
+       printf("Finshed Read!\n");
+
+       if (result > 0)
+               printf("BEE Data Test check Failed!\n");
+       else
+               printf("BEE Data Test Check Passed!\n");
+
+       for (i = 0; i < ARRAY_SIZE(inst); i++)
+               writel(inst[i], address + (i * 4));
+
+       flush_dcache_range(address, address + sizeof(inst));
+
+       val = ((int (*)(void))address)();
+
+       printf("\nBee Instruction test, Program:\n"
+              "int test(void)\n"
+              "{\n"
+              "      return 0x55aa55aa;\n"
+              "}\n"
+              "Assemble:\n"
+              "0xe59f0000: ldr r0, [pc]\n"
+              "0xe12fff1e: bx lr\n"
+              "0x55aa55aa: 0x55aa55aa\n"
+              "Runnint at 0x%x\n", address);
+       if (val == 0x55aa55aa)
+               printf("Bee Instruction Test Passed!\n");
+       else
+               printf("Bee Instruction Test Failed!\n");
+
+       return CMD_RET_SUCCESS;
+}
+
+static int region_valid(u32 start, u32 size)
+{
+       if ((start < PHYS_SDRAM) || (start >= (start + size - 1)) ||
+           (start >= (PHYS_SDRAM + PHYS_SDRAM_SIZE - 1))) {
+               printf("Invalid start 0x%x, size 0x%x\n", start, size);
+               return -EINVAL;
+       }
+
+       if (size > SZ_512M) {
+               printf("The region size exceeds SZ_512M\n");
+               return -EINVAL;
+       }
+
+       if ((start & 0xFFFF) && (size & 0xFFFF)) {
+               printf("start or size not 64KB aligned!\n");
+               return -EINVAL;
+       }
+
+       /* 128K for U-Boot Stack */
+       if ((start + size - 1) >= (gd->start_addr_sp - SZ_128K)) {
+               printf("Overlap with uboot execution environment!\n"
+                      "Decrease size or start\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int do_bee_init(cmd_tbl_t *cmdtp, int flag, int argc,
+                      char * const argv[])
+{
+       u32 start, size, val;
+       int ret;
+       struct bee_parameters *p = &para;
+
+#if defined(CONFIG_SYS_ARM_CACHE_WRITETHROUGH)
+       enum dcache_option option = DCACHE_WRITETHROUGH;
+#else
+       enum dcache_option option = DCACHE_WRITEBACK;
+#endif
+
+       if (argc > 5)
+               return CMD_RET_USAGE;
+
+       if (fuse_read(0, 4, &val)) {
+               puts("Can not get fuse bank 0, word 4\n");
+       } else {
+               if (val & (1 << 25)) {
+                       puts("BEE disabed in fuse!\n");
+                       return CMD_RET_FAILURE;
+               }
+       }
+
+       /* Cache enabled? */
+       if ((get_cr() & (CR_I | CR_C)) != (CR_I | CR_C)) {
+               /*
+                * Here we need icache and dcache both enabled, because
+                * we may take the protected region for instruction and
+                * data usage. And icache and dcache both enabled are
+                * better for performance.
+                */
+               printf("Please enable dcache and icache first!\n");
+               return CMD_RET_FAILURE;
+       }
+
+       p->key_method = SOFT_KEY;
+       p->mode = ECB_MODE;
+       p->start1 = PHYS_SDRAM;
+       p->size1 = SZ_512M;
+       p->start2 = IRAM_BASE_ADDR;
+       p->size2 = IRAM_SIZE;
+
+       if (argc == 2) {
+               p->key_method = (int)simple_strtoul(argv[1], NULL, 16);
+               p->mode = ECB_MODE;
+               p->start1 = PHYS_SDRAM;
+               p->size1 = SZ_512M;
+       } else if (argc == 3) {
+               p->key_method = (int)simple_strtoul(argv[1], NULL, 16);
+               p->mode = (int)simple_strtoul(argv[2], NULL, 10);
+               p->start1 = PHYS_SDRAM;
+               p->size1 = SZ_512M;
+       } else if ((argc == 4) || (argc == 5)) {
+               p->key_method = (int)simple_strtoul(argv[1], NULL, 16);
+               p->mode = (int)simple_strtoul(argv[2], NULL, 10);
+               start = (u32)simple_strtoul(argv[3], NULL, 16);
+               /* Default size that AES Region0 can protected */
+               size = SZ_512M;
+               if (argc == 5)
+                       size = (u32)simple_strtoul(argv[4], NULL, 16);
+               p->start1 = start;
+               p->size1 = size;
+       }
+
+       if ((p->key_method != SOFT_KEY) && (p->key_method != SNVS_KEY))
+               return CMD_RET_USAGE;
+
+       if ((p->mode != ECB_MODE) && (p->mode != CTR_MODE))
+               return CMD_RET_USAGE;
+
+       /*
+        * No need to check region valid for IRAM, since it is fixed.
+        * Only check DRAM region here.
+        */
+       if (region_valid(p->start1, p->size1))
+               return CMD_RET_FAILURE;
+
+       ret = bee_init(p);
+       if (ret)
+               return CMD_RET_FAILURE;
+
+       /*
+        * Set DCACHE OFF to AES REGION0 and AES REGION1 first
+        * to avoid possible unexcepted cache settings.
+        */
+       mmu_set_region_dcache_behaviour(AES_REGION0_ADDR, SZ_1G, DCACHE_OFF);
+
+       mmu_set_region_dcache_behaviour(AES_REGION0_ADDR, p->size1, option);
+
+       mmu_set_region_dcache_behaviour(AES_REGION1_ADDR, p->size2, option);
+
+       printf("Access Region 0x%x - 0x%x to protect 0x%x - 0x%x\n"
+              "Do not directly access 0x%x - 0x%x\n"
+              "Access Region 0x%x - 0x%x to protect 0x%x - 0x%x\n"
+              "Do not directly access 0x%x - 0x%x\n",
+              AES_REGION0_ADDR, AES_REGION0_ADDR + p->size1 - 1,
+              p->start1, p->start1 + p->size1 - 1,
+              p->start1, p->start1 + p->size1 - 1,
+              AES_REGION1_ADDR, AES_REGION1_ADDR + p->size2 - 1,
+              p->start2, p->start2 + p->size2 - 1,
+              p->start2, p->start2 + p->size2 - 1);
+
+       bee_inited = 1;
+
+       return CMD_RET_SUCCESS;
+}
+
+static int do_bee_test(cmd_tbl_t *cmdtp, int flag, int argc,
+                      char * const argv[])
+{
+       int ret;
+       int region;
+
+       if (bee_inited == 0) {
+               printf("Bee not initialized, run bee init first!\n");
+               return CMD_RET_FAILURE;
+       }
+       if (argc > 2)
+               return CMD_RET_USAGE;
+
+       region = 0;
+       if (argc == 2)
+               region = (int)simple_strtoul(argv[1], NULL, 16);
+       /* Only two regions are supported, 0 and 1 */
+       if (region >= 2)
+               return CMD_RET_USAGE;
+
+       ret = bee_test(&para, region);
+       if (ret)
+               return CMD_RET_FAILURE;
+
+       return CMD_RET_SUCCESS;
+}
+
+static cmd_tbl_t cmd_bmp_sub[] = {
+       U_BOOT_CMD_MKENT(init, 5, 0, do_bee_init, "", ""),
+       U_BOOT_CMD_MKENT(test, 2, 0, do_bee_test, "", ""),
+};
+
+static int do_bee_ops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+       cmd_tbl_t *c;
+
+       c = find_cmd_tbl(argv[1], &cmd_bmp_sub[0], ARRAY_SIZE(cmd_bmp_sub));
+
+       /* Drop off the 'bee' command argument */
+       argc--;
+       argv++;
+
+       if (c)
+               return  c->cmd(cmdtp, flag, argc, argv);
+       else
+               return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(
+       bee, CONFIG_SYS_MAXARGS, 1, do_bee_ops,
+       "BEE function test",
+       "init [key] [mode] [start] [size]       - BEE block initial\n"
+       "  key: 0 | 1, 0 means software key, 1 means SNVS random key\n"
+       "  mode: 0 | 1, 0 means ECB mode, 1 means CTR mode\n"
+       "  start: start address that you want to protect\n"
+       "  size: The size of the area that you want to protect\n"
+       "  start and end(start + size) addr both should be 64KB aligned.\n"
+       "\n"
+       " After initialization, the mapping:\n"
+       " 1. [0x10000000 - (0x10000000 + size - 1)] <--->\n"
+       "    [start - (start + size - 1)]\n"
+       "    Here [start - (start + size -1)] is fixed mapping to\n"
+       "    [0x10000000 - (0x10000000 + size - 1)], whatever start is.\n"
+       " 2. [0x30000000 - (0x30000000 + IRAM_SIZE - 1)] <--->\n"
+       "    [IRAM_BASE_ADDR - (IRAM_BASE_ADDR + IRAM_SIZE - 1)]\n"
+       "\n"
+       " Note: Here we only use AES region 0 to protect the DRAM\n"
+       "       area that you specified, max size SZ_512M.\n"
+       "       AES region 1 is used to protect IRAM area.\n"
+       " Example:\n"
+       " 1. bee init 1 1 0xa0000000 0x10000\n"
+       "    Access 0x10000000 - 0x10010000 to protect 0xa0000000 - 0xa0010000\n"
+       " 2. bee init 1 1 0x80000000 0x20000\n"
+       "    Access 0x10000000 - 0x10020000 to protect 0x80000000 - 0x80020000\n"
+       "\n"
+       " Default configuration if only `bee init` without any args:\n"
+       " 1. software key\n"
+       " 2. ECB mode\n"
+       " 3. Address protected:\n"
+       "   Remapped Region0: PHYS_SDRAM - PHYS_SDRAM + SZ_512M\n"
+       "   Remapped Region1: IRAM_BASE_ADDR - IRAM_BASE_ADDR + IRAM_SIZE\n"
+       " 4. Default Mapping for 6UL:\n"
+       "   [0x10000000 - 0x2FFFFFFF] <-> [0x80000000 - 0x9FFFFFFF]\n"
+       "   [0x30000000 - 0x3001FFFF] <-> [0x00900000 - 0x0091FFFF]\n"
+       "\n"
+       "bee test [region]                      - BEE function test\n"
+       "  region: 0 | 1, 0 means region0, 1 means regions1\n"
+);
+