/* Copyright 2023 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "common.h" #include "board_id.h" #include "endian.h" #include "extension.h" #include "flash_info.h" #include "system.h" #include "util.h" #define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args) #define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ## args) static int factory_config_is_blank(uint64_t fc) { return fc == 0; } /** * Read the INFO1 factory config value into fc. * * @return EC_SUCCESS or an error code in cases of various failures to read the * flash space. */ int read_factory_config(uint64_t *fc) { uint32_t *fc_p; int i; /* * The factory config offset is guaranteed to be divisible by 4, and it * is guaranteed to be aligned at 8 bytes. */ fc_p = (uint32_t *)fc; for (i = 0; i < sizeof(*fc); i += sizeof(uint32_t)) { int rv; rv = flash_physical_info_read_word (INFO_FACTORY_CFG_OFFSET + i, fc_p); if (rv != EC_SUCCESS) { CPRINTF("%s: failed to read word %d, error %d\n", __func__, i, rv); return rv; } fc_p++; } /* The config is stored inverted. Invert the value. */ *fc = ~(*fc); return EC_SUCCESS; } void print_factory_config(void) { uint64_t fc; int rv; rv = read_factory_config(&fc); ccprintf("fc = "); if (rv) ccprintf("invalid (%d)\n", rv); else ccprintf("0x%016llx\n", fc); ccprintf("\n"); } /** * Write the factory config into the flash INFO1 space. * * @param fc Pointer to the factory config 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_factory_config(uint64_t *new_fc) { uint64_t fc; uint32_t rv; #ifndef CR50_DEV struct board_id id; /* Fail if Board ID Type is already programmed */ if (read_board_id(&id) || !board_id_type_is_blank(&id)) return EC_ERROR_ACCESS_DENIED; #endif rv = read_factory_config(&fc); if (rv != EC_SUCCESS) { CPRINTS("%s: error reading cfg", __func__); return rv; } if (*new_fc == fc) { CPRINTS("%s: ok.", __func__); return EC_SUCCESS; } if (!factory_config_is_blank(fc)) { CPRINTS("%s: factory cfg already programmed", __func__); return EC_ERROR_ACCESS_DENIED; } CPRINTS("Set FC - 0x%llx ", *new_fc); /* The config is stored inverted. */ *new_fc = ~(*new_fc); flash_info_write_enable(); /* Write Board ID */ rv = flash_info_physical_write(INFO_FACTORY_CFG_OFFSET, sizeof(*new_fc), (const char *)new_fc); if (rv != EC_SUCCESS) CPRINTS("%s: write failed", __func__); flash_info_write_disable(); return rv; } static enum vendor_cmd_rc vc_set_factory_config(enum vendor_cmd_cc code, void *buf, size_t input_size, size_t *response_size) { uint64_t fc; uint8_t *pbuf = buf; int rv; *response_size = 0; if (input_size != INFO_FACTORY_CFG_SIZE) return VENDOR_RC_BOGUS_ARGS; memcpy(&fc, pbuf, INFO_FACTORY_CFG_SIZE); /* Convert to line representation. */ fc = be64toh(fc); rv = write_factory_config(&fc); if (rv == EC_ERROR_ACCESS_DENIED) return VENDOR_RC_NOT_ALLOWED; else if (rv) return VENDOR_RC_INTERNAL_ERROR; return VENDOR_RC_SUCCESS; } DECLARE_VENDOR_COMMAND(VENDOR_CC_SET_FACTORY_CONFIG, vc_set_factory_config); static enum vendor_cmd_rc vc_get_factory_config(enum vendor_cmd_cc code, void *buf, size_t input_size, size_t *response_size) { uint64_t fc; *response_size = 0; if (read_factory_config(&fc)) return VENDOR_RC_READ_FLASH_FAIL; fc = htobe64(fc); memcpy(buf, &fc, sizeof(fc)); *response_size = sizeof(fc); return VENDOR_RC_SUCCESS; } DECLARE_VENDOR_COMMAND(VENDOR_CC_GET_FACTORY_CONFIG, vc_get_factory_config);