summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Collard <louiscollard@chromium.org>2018-07-06 16:38:38 +0800
committerchrome-bot <chrome-bot@chromium.org>2018-09-05 16:11:01 -0700
commit98045b7fa94ad11803af540677b2cde5a337be80 (patch)
tree5f4e14722e93d57e501f64559afadae2de1f3e49
parent87b6fed80dbb08e548105c6f2019b1d57c598ea7 (diff)
downloadchrome-ec-98045b7fa94ad11803af540677b2cde5a337be80.tar.gz
cr50: Add commands to get/set serial number bits.
Allocates 16 bytes of INFO1 space, in the 'board' section, and after the current Board ID data, to store the serial number data for use by zero-touch enrollment. Adds a console command to read / set this data. Adds TPM vendor commands to set initial sn data, and update it during RMA. CQ-DEPEND=CL:*657450 BUG=b:111195266 TEST=tested locally on soraka BRANCH=none Change-Id: I752aefad9654742b7719156202f29d635d2306df Signed-off-by: Louis Collard <louiscollard@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1127574 Reviewed-by: Andrey Pronin <apronin@chromium.org>
-rw-r--r--board/cr50/board.h1
-rw-r--r--board/cr50/wp.c8
-rw-r--r--board/cr50/wp.h7
-rw-r--r--chip/g/board_id.c14
-rw-r--r--chip/g/board_id.h20
-rw-r--r--chip/g/board_space.h78
-rw-r--r--chip/g/build.mk1
-rw-r--r--chip/g/signed_header.h1
-rw-r--r--chip/g/sn_bits.c269
-rw-r--r--chip/g/sn_bits.h21
-rw-r--r--include/config.h6
-rw-r--r--include/tpm_vendor_cmds.h21
12 files changed, 416 insertions, 31 deletions
diff --git a/board/cr50/board.h b/board/cr50/board.h
index 9e8da8acb1..8d5e7b17ea 100644
--- a/board/cr50/board.h
+++ b/board/cr50/board.h
@@ -355,6 +355,7 @@ enum nvmem_users {
#define CONFIG_TPM_I2CS
#define CONFIG_BOARD_ID_SUPPORT
+#define CONFIG_SN_BITS_SUPPORT
#define CONFIG_EXTENDED_VERSION_INFO
#define I2C_PORT_MASTER 0
diff --git a/board/cr50/wp.c b/board/cr50/wp.c
index 83b666c0fc..f4359aa170 100644
--- a/board/cr50/wp.c
+++ b/board/cr50/wp.c
@@ -57,7 +57,7 @@ static void set_wp_state(int asserted)
*
* @return 0 if WP deasserted, 1 if WP asserted
*/
-static int get_wp_state(void)
+int wp_is_asserted(void)
{
/* Signal is active low, so invert */
return !GREG32(RBOX, EC_WP_L);
@@ -72,7 +72,7 @@ static void check_wp_battery_presence(void)
return;
/* Otherwise, mirror battery */
- if (bp != get_wp_state()) {
+ if (bp != wp_is_asserted()) {
CPRINTS("WP %d", bp);
set_wp_state(bp);
}
@@ -123,7 +123,7 @@ static enum vendor_cmd_rc vc_set_wp(enum vendor_cmd_cc code,
/* Get current wp settings */
if (GREG32(PMU, LONG_LIFE_SCRATCH1) & BOARD_FORCING_WP)
response |= WPV_FORCE;
- if (get_wp_state())
+ if (wp_is_asserted())
response |= WPV_ENABLE;
/* Get atboot wp settings */
if (ccd_get_flag(CCD_FLAG_OVERRIDE_WP_AT_BOOT)) {
@@ -166,7 +166,7 @@ static int command_wp(int argc, char **argv)
forced = GREG32(PMU, LONG_LIFE_SCRATCH1) & BOARD_FORCING_WP;
ccprintf("Flash WP: %s%s\n", forced ? "forced " : "",
- get_wp_state() ? "enabled" : "disabled");
+ wp_is_asserted() ? "enabled" : "disabled");
ccprintf(" at boot: ");
if (ccd_get_flag(CCD_FLAG_OVERRIDE_WP_AT_BOOT))
diff --git a/board/cr50/wp.h b/board/cr50/wp.h
index 35c02ae215..501067233f 100644
--- a/board/cr50/wp.h
+++ b/board/cr50/wp.h
@@ -16,6 +16,13 @@
void init_wp_state(void);
/**
+ * Get the current write protect state.
+ *
+ * @return 0 if WP deasserted, 1 if WP asserted
+ */
+int wp_is_asserted(void);
+
+/**
* Read the FWMP value from TPM NVMEM and set the console restriction
* appropriately.
*/
diff --git a/chip/g/board_id.c b/chip/g/board_id.c
index 569540eb62..1c74184103 100644
--- a/chip/g/board_id.c
+++ b/chip/g/board_id.c
@@ -70,8 +70,8 @@ int read_board_id(struct board_id *id)
id_p = (uint32_t *)id;
/* Make sure INFO1 board ID space is readable */
- if (flash_info_read_enable(INFO_BOARD_SPACE_OFFSET,
- INFO_BOARD_SPACE_PROTECT_SIZE) !=
+ if (flash_info_read_enable(INFO_BOARD_ID_OFFSET,
+ INFO_BOARD_ID_PROTECT_SIZE) !=
EC_SUCCESS) {
CPRINTS("%s: failed to enable read access to info", __func__);
return EC_ERROR_ACCESS_DENIED;
@@ -81,9 +81,7 @@ int read_board_id(struct board_id *id)
int rv;
rv = flash_physical_info_read_word
- (INFO_BOARD_SPACE_OFFSET +
- offsetof(struct info1_board_space, bid) + i,
- id_p);
+ (INFO_BOARD_ID_OFFSET + i, id_p);
if (rv != EC_SUCCESS) {
CPRINTF("%s: failed to read word %d, error %d\n",
__func__, i, rv);
@@ -154,15 +152,15 @@ static int write_board_id(const struct board_id *id)
}
/* Enable write access */
- if (flash_info_write_enable(INFO_BOARD_SPACE_OFFSET,
- INFO_BOARD_SPACE_PROTECT_SIZE) !=
+ if (flash_info_write_enable(INFO_BOARD_ID_OFFSET,
+ INFO_BOARD_ID_PROTECT_SIZE) !=
EC_SUCCESS) {
CPRINTS("%s: failed to enable write access", __func__);
return EC_ERROR_ACCESS_DENIED;
}
/* Write Board ID */
- rv = flash_info_physical_write(INFO_BOARD_SPACE_OFFSET +
+ rv = flash_info_physical_write(INFO_BOARD_ID_OFFSET +
offsetof(struct info1_board_space, bid),
sizeof(*id), (const char *)id);
if (rv != EC_SUCCESS)
diff --git a/chip/g/board_id.h b/chip/g/board_id.h
index dda2302c14..5fc8af46ba 100644
--- a/chip/g/board_id.h
+++ b/chip/g/board_id.h
@@ -7,25 +7,11 @@
#ifndef __EC_CHIP_G_BOARD_ID_H
#define __EC_CHIP_G_BOARD_ID_H
+#include "board_space.h"
#include "common.h"
#include "signed_header.h"
#include "util.h"
-/* Structure holding Board ID */
-struct board_id {
- uint32_t type; /* Board type */
- uint32_t type_inv; /* Board type (inverted) */
- uint32_t flags; /* Flags */
-};
-
-/* Info1 Board space contents. */
-struct info1_board_space {
- struct board_id bid;
-};
-
-#define INFO_BOARD_ID_SIZE sizeof(struct board_id)
-#define INFO_BOARD_SPACE_PROTECT_SIZE 16
-
/**
* Check the current header vs. the supplied Board ID
*
@@ -61,8 +47,4 @@ const struct SignedHeader *get_current_image_header(void);
*/
uint32_t board_id_mismatch(const struct SignedHeader *h);
-BUILD_ASSERT((offsetof(struct info1_board_space, bid) & 3) == 0);
-BUILD_ASSERT((INFO_BOARD_ID_SIZE & 3) == 0);
-BUILD_ASSERT(sizeof(struct info1_board_space) <= INFO_BOARD_SPACE_PROTECT_SIZE);
-
#endif /* ! __EC_CHIP_G_BOARD_ID_H */
diff --git a/chip/g/board_space.h b/chip/g/board_space.h
new file mode 100644
index 0000000000..90b6c02287
--- /dev/null
+++ b/chip/g/board_space.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __EC_CHIP_G_BOARD_SPACE_H
+#define __EC_CHIP_G_BOARD_SPACE_H
+
+#include "compile_time_macros.h"
+#include "flash_info.h"
+#include "stdint.h"
+
+/*
+ * Structures for data stored in the board space of INFO1.
+ */
+
+/* Structure holding Board ID */
+struct board_id {
+ uint32_t type; /* Board type */
+ uint32_t type_inv; /* Board type (inverted) */
+ uint32_t flags; /* Flags */
+};
+
+/* Structure holding serial number data */
+struct sn_data {
+ uint8_t version;
+ uint8_t reserved[2];
+ uint8_t rma_status;
+ uint32_t sn_hash[3];
+};
+
+/* Current sn_data format version */
+#define SN_DATA_VERSION 0x0f
+/* Size of header elements (everything apart from sn_hash) */
+#define SN_HEADER_SIZE offsetof(struct sn_data, sn_hash)
+/* Number of bits reserved for RMA counter */
+#define RMA_COUNT_BITS 7
+/* Value used to indicate device has been RMA'd */
+#define RMA_INDICATOR ((uint8_t) ~(1 << RMA_COUNT_BITS))
+
+/* Info1 Board space contents. */
+struct info1_board_space {
+ struct board_id bid;
+ /* Pad so that board_id occupies it's full 'protect' size */
+ uint8_t bid_padding[4];
+ struct sn_data sn;
+};
+
+#define INFO_BOARD_ID_SIZE sizeof(struct board_id)
+#define INFO_BOARD_ID_OFFSET (INFO_BOARD_SPACE_OFFSET + \
+ offsetof(struct info1_board_space, \
+ bid))
+
+#define INFO_SN_DATA_SIZE sizeof(struct sn_data)
+#define INFO_SN_DATA_OFFSET (INFO_BOARD_SPACE_OFFSET + \
+ offsetof(struct info1_board_space, \
+ sn))
+
+/*
+ * Write protection for the INFO1 space allows windows with sizes that are
+ * powers of 2 to be protected. Given the different write restrictions on
+ * the different spaces listed above, we keep them in separate windows.
+ * This implies that each space must occupy a space that has a size which
+ * is a power of two.
+ */
+#define INFO_BOARD_ID_PROTECT_SIZE 16
+#define INFO_SN_DATA_PROTECT_SIZE 16
+
+BUILD_ASSERT((INFO_BOARD_ID_SIZE & 3) == 0);
+BUILD_ASSERT((INFO_BOARD_ID_OFFSET & 3) == 0);
+BUILD_ASSERT(INFO_BOARD_ID_SIZE <= INFO_BOARD_ID_PROTECT_SIZE);
+
+BUILD_ASSERT((INFO_SN_DATA_SIZE & 3) == 0);
+BUILD_ASSERT((INFO_SN_DATA_OFFSET & 3) == 0);
+BUILD_ASSERT(INFO_SN_DATA_SIZE <= INFO_SN_DATA_PROTECT_SIZE);
+
+#endif /* ! __EC_CHIP_G_BOARD_SPACE_H */
diff --git a/chip/g/build.mk b/chip/g/build.mk
index 8c8a27d822..77f8ea4a00 100644
--- a/chip/g/build.mk
+++ b/chip/g/build.mk
@@ -20,6 +20,7 @@ endif
# Required chip modules
chip-y = clock.o gpio.o hwtimer.o pre_init.o system.o
chip-$(CONFIG_BOARD_ID_SUPPORT) += board_id.o
+chip-$(CONFIG_SN_BITS_SUPPORT) += sn_bits.o
ifeq ($(CONFIG_POLLING_UART),y)
chip-y += polling_uart.o
else
diff --git a/chip/g/signed_header.h b/chip/g/signed_header.h
index c59909b958..3ee4085a14 100644
--- a/chip/g/signed_header.h
+++ b/chip/g/signed_header.h
@@ -6,6 +6,7 @@
#define __CROS_EC_SIGNED_HEADER_H
#include "compile_time_macros.h"
+#include "stdint.h"
#define FUSE_PADDING 0x55555555 /* baked in hw! */
#define FUSE_IGNORE 0xa3badaac /* baked in rom! */
diff --git a/chip/g/sn_bits.c b/chip/g/sn_bits.c
new file mode 100644
index 0000000000..2e12db832f
--- /dev/null
+++ b/chip/g/sn_bits.c
@@ -0,0 +1,269 @@
+/* Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "board_space.h"
+#include "console.h"
+#include "extension.h"
+#include "flash_info.h"
+#include "util.h"
+#include "wp.h"
+
+#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args)
+#define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ## args)
+
+int read_sn_data(struct sn_data *sn)
+{
+ uint32_t *id_p;
+ int i;
+
+ /*
+ * SN Bits structure size is guaranteed to be divisible by 4, and it
+ * is guaranteed to be aligned at 4 bytes.
+ */
+
+ id_p = (uint32_t *)sn;
+
+ /* Make sure INFO1 sn bits space is readable */
+ if (flash_info_read_enable(INFO_SN_DATA_OFFSET,
+ INFO_SN_DATA_PROTECT_SIZE) !=
+ EC_SUCCESS) {
+ CPRINTS("%s: failed to enable read access to info", __func__);
+ return EC_ERROR_ACCESS_DENIED;
+ }
+
+ for (i = 0; i < sizeof(*sn); i += sizeof(uint32_t)) {
+ int rv;
+
+ rv = flash_physical_info_read_word
+ (INFO_SN_DATA_OFFSET + i, id_p);
+ if (rv != EC_SUCCESS) {
+ CPRINTF("%s: failed to read word %d, error %d\n",
+ __func__, i, rv);
+ return rv;
+ }
+ id_p++;
+ }
+ return EC_SUCCESS;
+}
+
+static int write_sn_data(struct sn_data *sn_data, int header_only)
+{
+ int rv = EC_SUCCESS;
+
+ /* Enable write access */
+ if (flash_info_write_enable(INFO_SN_DATA_OFFSET,
+ INFO_SN_DATA_PROTECT_SIZE) !=
+ EC_SUCCESS) {
+ CPRINTS("%s: failed to enable write access", __func__);
+ return EC_ERROR_ACCESS_DENIED;
+ }
+
+ /* Write sn bits */
+ rv = flash_info_physical_write(INFO_SN_DATA_OFFSET,
+ header_only ?
+ SN_HEADER_SIZE : sizeof(*sn_data),
+ (const char *)sn_data);
+ if (rv != EC_SUCCESS)
+ CPRINTS("%s: write failed", __func__);
+
+ /* Disable write access */
+ flash_info_write_disable();
+
+ return rv;
+}
+/**
+ * Initialize SN data space in flash INFO1, and write sn hash. This can only
+ * be called once per device; subsequent calls on a device that has already
+ * had the sn hash written will fail.
+ *
+ * @param id Pointer to a SN structure to copy into INFO1
+ *
+ * @return EC_SUCCESS or an error code in cases of various failures to read or
+ * if the space has been already initialized.
+ */
+static int write_sn_hash(const uint32_t sn_hash[3])
+{
+ int rv = EC_ERROR_PARAM_COUNT;
+ int i;
+ struct sn_data sn_data;
+
+ rv = read_sn_data(&sn_data);
+ if (rv != EC_SUCCESS)
+ return rv;
+
+ /* Check the sn data space is currently uninitialized */
+ for (i = 0; i < (sizeof(sn_data) / sizeof(uint32_t)); i++)
+ if (((uint32_t *) &sn_data)[i] != 0xffffffff)
+ return EC_ERROR_INVALID_CONFIG;
+
+ sn_data.version = SN_DATA_VERSION;
+ memcpy(sn_data.sn_hash, sn_hash, sizeof(sn_data.sn_hash));
+
+ rv = write_sn_data(&sn_data, 0);
+
+ return rv;
+}
+
+static int increment_rma_count(uint8_t inc)
+{
+ int rv = EC_ERROR_PARAM_COUNT;
+ struct sn_data sn_data;
+
+ rv = read_sn_data(&sn_data);
+ if (rv != EC_SUCCESS)
+ return rv;
+
+ /* Make sure we know how to update this data */
+ if (sn_data.version != SN_DATA_VERSION)
+ return EC_ERROR_INVALID_CONFIG;
+
+ /* Don't allow incrementing more than the number of bits */
+ if (inc > RMA_COUNT_BITS)
+ return EC_ERROR_INVAL;
+
+ /*
+ * The RMA status is initially set to 0xff. We set bit 7
+ * to 0 to indicate the device has been RMA'd at least once,
+ * and use the remaining bits as a count of how many times
+ * the device has been RMA'd. The number of 0s represents
+ * the number of RMAs. As there are only 7 bits available
+ * for the count, a value of 0x00 means the device has
+ * been RMA'd at least 7 times (but we do not know how many).
+ *
+ * We allow incrementing by 0 or n (rather than 0 or 1) so
+ * that a device in any state can be put into the RMA'd with
+ * unknown count (0x00) state with a single call to this
+ * function.
+ */
+ sn_data.rma_status <<= inc;
+ sn_data.rma_status &= RMA_INDICATOR;
+
+ rv = write_sn_data(&sn_data, 1);
+
+ return rv;
+}
+
+static enum vendor_cmd_rc vc_sn_set_hash(enum vendor_cmd_cc code,
+ void *buf,
+ size_t input_size,
+ size_t *response_size)
+{
+ uint32_t sn_hash[3];
+ uint8_t *pbuf = buf;
+
+ *response_size = 1;
+
+ if (input_size != sizeof(sn_hash)) {
+ *pbuf = VENDOR_RC_BOGUS_ARGS;
+ return VENDOR_RC_BOGUS_ARGS;
+ }
+
+ memcpy(&sn_hash, pbuf, sizeof(sn_hash));
+
+ /* We care about the LSB only. */
+ *pbuf = (uint8_t) write_sn_hash(sn_hash);
+
+ return *pbuf;
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_SN_SET_HASH, vc_sn_set_hash);
+
+static enum vendor_cmd_rc vc_sn_inc_rma(enum vendor_cmd_cc code,
+ void *buf,
+ size_t input_size,
+ size_t *response_size)
+{
+ uint8_t *pbuf = buf;
+
+ if (wp_is_asserted())
+ return EC_ERROR_ACCESS_DENIED;
+
+ *response_size = 1;
+
+ if (input_size != sizeof(*pbuf)) {
+ *pbuf = VENDOR_RC_BOGUS_ARGS;
+ return VENDOR_RC_BOGUS_ARGS;
+ }
+
+ /* We care about the LSB only. */
+ *pbuf = (uint8_t) increment_rma_count(*pbuf);
+
+ return *pbuf;
+}
+DECLARE_VENDOR_COMMAND(VENDOR_CC_SN_INC_RMA, vc_sn_inc_rma);
+
+static int command_sn(int argc, char **argv)
+{
+ int rv = EC_ERROR_PARAM_COUNT;
+ struct sn_data sn;
+
+ switch (argc) {
+#ifdef CR50_DEV
+ case 4:
+ {
+ char *e;
+
+ sn.sn_hash[0] = strtoi(argv[1], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM1;
+
+ sn.sn_hash[1] = strtoi(argv[2], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM2;
+
+ sn.sn_hash[2] = strtoi(argv[3], &e, 0);
+ if (*e)
+ return EC_ERROR_PARAM3;
+
+ rv = write_sn_hash(sn.sn_hash);
+ if (rv != EC_SUCCESS)
+ return rv;
+
+ goto print_sn_data;
+ }
+ case 3:
+ {
+ int count;
+ char *e;
+
+ if (strcasecmp(argv[1], "rmainc") != 0)
+ return EC_ERROR_PARAM1;
+
+ count = strtoi(argv[2], &e, 0);
+ if (*e || count > 7)
+ return EC_ERROR_PARAM2;
+
+ rv = increment_rma_count(count);
+ if (rv != EC_SUCCESS)
+ return rv;
+ }
+ /* fall through */
+print_sn_data:
+#endif
+ case 1:
+ rv = read_sn_data(&sn);
+ if (rv == EC_SUCCESS)
+ CPRINTF("Version: %02x\n"
+ "RMA: %02x\n"
+ "SN: %08x %08x %08x\n",
+ sn.version, sn.rma_status,
+ sn.sn_hash[0], sn.sn_hash[1], sn.sn_hash[2]);
+
+ break;
+ default:
+ rv = EC_ERROR_PARAM_COUNT;
+ }
+
+ return rv;
+}
+DECLARE_SAFE_CONSOLE_COMMAND(sn,
+ command_sn, ""
+#ifdef CR50_DEV
+ "[(sn0 sn1 sn2) | (rmainc n)]"
+#endif
+ , "Get"
+#ifdef CR50_DEV
+ "/Set"
+#endif
+ " Serial Number Data");
diff --git a/chip/g/sn_bits.h b/chip/g/sn_bits.h
new file mode 100644
index 0000000000..547ac1b938
--- /dev/null
+++ b/chip/g/sn_bits.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2018 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __EC_CHIP_G_SN_BITS_H
+#define __EC_CHIP_G_SN_BITS_H
+
+#include "board_space.h"
+
+/**
+ * Reads the SN data from the flash INFO1 space.
+ *
+ * @param id Pointer to a sn_data structure to fill
+ *
+ * @return EC_SUCCESS or an error code in case of failure.
+ */
+int read_sn_data(struct sn_data *sn);
+
+#endif /* ! __EC_CHIP_G_SN_BITS_H */
diff --git a/include/config.h b/include/config.h
index 0aedd97799..6d8cf67d6e 100644
--- a/include/config.h
+++ b/include/config.h
@@ -3613,6 +3613,12 @@
#undef CONFIG_BOARD_ID_SUPPORT
/*
+ * Define this if serial number support is required. For g chip based boards
+ * it allows a verifiable serial number to be stored / certified.
+ */
+#undef CONFIG_SN_BITS_SUPPORT
+
+/*
* Define this to enable Cros Board Info support. I2C_EEPROM_PORT and
* I2C_EEPROM_ADDR must be defined as well.
*/
diff --git a/include/tpm_vendor_cmds.h b/include/tpm_vendor_cmds.h
index 16fa071a6b..a3b260cf05 100644
--- a/include/tpm_vendor_cmds.h
+++ b/include/tpm_vendor_cmds.h
@@ -80,6 +80,27 @@ enum vendor_cmd_cc {
* it will response with the current tpm_mode value in uint8_t format.
*/
VENDOR_CC_TPM_MODE = 40,
+ /*
+ * Initializes INFO1 SN data space, and sets SN hash. Takes three
+ * int32 as parameters, which are written as the SN hash.
+ */
+ VENDOR_CC_SN_SET_HASH = 41,
+ /*
+ * Increments the RMA count in the INFO1 SN data space. The space must
+ * have been previously initialized with the _SET_HASH command above for
+ * this to succeed. Takes one byte as parameter, which indicates the
+ * number to increment the RMA count by; this is typically 1 or 0.
+ *
+ * Incrementing the RMA count by 0 will set the RMA indicator, but not
+ * incremement the count. This is useful to mark that a device has been
+ * RMA'd, but that we were not able to log the new serial number.
+ *
+ * Incrementing the count by the maximum RMA count (currently 7) will
+ * always set the RMA count to the maximum value, regardless of the
+ * previous value. This can be used with any device, regardless of
+ * current state, to mark it as RMA'd but with an unknown RMA count.
+ */
+ VENDOR_CC_SN_INC_RMA = 42,
LAST_VENDOR_COMMAND = 65535,
};