summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/lib/fw_config.md353
-rw-r--r--Documentation/lib/index.md2
-rw-r--r--src/Kconfig27
-rw-r--r--src/include/device/device.h4
-rw-r--r--src/include/fw_config.h53
-rw-r--r--src/lib/Makefile.inc5
-rw-r--r--src/lib/fw_config.c94
7 files changed, 537 insertions, 1 deletions
diff --git a/Documentation/lib/fw_config.md b/Documentation/lib/fw_config.md
new file mode 100644
index 0000000000..63a56dcd7b
--- /dev/null
+++ b/Documentation/lib/fw_config.md
@@ -0,0 +1,353 @@
+# Firmware Configuration Interface in coreboot
+
+## Motivation
+
+The firmware configuration interface in coreboot is designed to support a wide variety of
+configuration options in that are dictated by the hardware at runtime. This allows a single
+BIOS image to be used across a wide variety of devices which may have key differences but are
+otherwise similar enough to use the same coreboot build target.
+
+The initial implementation is designed to take advantage of a bitmask returned by the Embedded
+Controller on Google Chrome OS devices which allows the manufacturer to use the same firmware
+image across multiple devices by selecting various options at runtime. See the Chromium OS
+[Firmware Config][1] documentation for more information.
+
+This firmware configuration interface differs from the CMOS option interface in that this
+bitmask value is not intended as a user-configurable setting as the configuration values must
+match the actual hardware. In the case where a user was to swap their hardware this value
+would need to be updated or overridden.
+
+## Device Presence
+
+One common example of why a firmware configuration interface is important is determining if a
+device is present in the system. With some bus topologies and hardware mechanisms it is
+possible to probe and enumerate this at runtime:
+
+- PCI is a self-discoverable bus and is very easy to handle.
+- I2C devices can often be probed with a combination of bus and address.
+- The use of GPIOs with external strap to ground or different voltages can be used to detect
+presence of a device.
+
+However there are several cases where this is insufficient:
+
+- I2C peripherals that require different drivers but have the same bus address cannot be
+uniquely identified at runtime.
+- A mainboard may be designed with multiple daughter board combinations which contain devices
+and configurations that cannot be detected.
+- While presence detect GPIOs are a convenient way for a single device presence, they are
+unable to distinguish between different devices so it can require a large number of GPIOs to
+support relatively few options.
+
+This presence detection can impact different stages of boot:
+
+### ACPI
+
+Devices that are not present should not provide an ACPI device indicating that they are
+present or the operating system may not be able to handle it correctly.
+
+The ACPI devices are largely driven by chips defined in the mainboard `devicetree.cb` and
+the variant overridetree.cb. This means it is important to be able to specify when a device
+is present or not directly in `devicetree.cb` itself. Otherwise each mainboard needs custom
+code to parse the tree and disable unused devices.
+
+### GPIO
+
+GPIOs with multiple functions may need to be configured correctly depending on the attached
+device. Given the wide variety of GPIO configuration possibilities it is not feasible to
+specify all combinations directly in `devicetree.cb` and it is best left to code provided by
+the mainboard.
+
+### FSP UPD
+
+Enabling and disabling devices may require altering FSP UPD values that are provided to the
+various stages of FSP. These options are also not easy to specify multiple times for
+different configurations in `devicetree.cb` and can be provided by the mainboard as code.
+
+## Firmware Configuration Interface
+
+The firmware configuration interface can be enabled by selecting `CONFIG_FW_CONFIG` and also
+providing a source for the value by defining an additional Kconfig option defined below.
+
+If the firmware configuration interface is disabled via Kconfig then all probe attempts will
+return true.
+
+## Firmware Configuration Value
+
+The 32bit value used as the firmware configuration bitmask is meant to be determined at runtime
+but could also be defined at compile time if needed.
+
+There are two supported sources for providing this information to coreboot.
+
+### CBFS
+
+The value can be provided with a 32bit raw value in CBFS that is read by coreboot. The value
+can be set at build time but also adjusted in an existing image with `cbfstool`.
+
+To enable this select the `CONFIG_FW_CONFIG_CBFS` option in the build configuration and add a
+raw 32bit value to CBFS with the name of the current prefix at `CONFIG_FW_PREFIX/fw_config`.
+
+When `fw_config_probe_device()` or `fw_config_probe()` is called it will look for the specified
+file in CBFS use the value it contains when matching fields and options.
+
+### Embedded Controller
+
+Google Chrome OS devices support an Embedded Controller interface for reading and writing the
+firmware configuration value, along with other board-specific information. It is possible for
+coreboot to read this value at boot on systems that support this feature.
+
+This option is selected by default for the mainboards that use it with
+`CONFIG_FW_CONFIG_CHROME_EC_CBI` and it is not typically necessary to adjust the value. It is
+possible by enabling the CBFS source and coreboot will look in CBFS first for a valid value
+before asking the embedded controller.
+
+It is also possible to adjust the value in the embedded controller *(after disabling write
+protection)* with the `ectool` command in a Chrome OS environment.
+
+For more information on the firmware configuration field on Chrome OS devices see the Chromium
+documentation for [Firmware Config][1] and [Board Info][2].
+
+[1]: http://chromium.googlesource.com/chromiumos/docs/+/master/design_docs/firmware_config.md
+[2]: http://chromium.googlesource.com/chromiumos/docs/+/master/design_docs/cros_board_info.md
+
+## Firmware Configuration Table
+
+The firmware configuration table itself is defined in the mainboard `devicetree.cb` with
+special tokens for defining fields and options.
+
+The table itself is enclosed in a `fw_config` token and terminated with `end` and it contains
+a mix of field and option definitions.
+
+Each field is defined by providing the field name and the start and end bit marking the exact
+location in the bitmask. Field names must be at least three characters long in order to
+satisfy the sconfig parser requirements and they must be unique with non-overlapping masks.
+
+ field <name> <start-bit> <end-bit> [option...] end
+
+For single-bit fields only one number is needed:
+
+ field <name> <bit> [option...] end
+
+Each `field` definition starts a new block that can be composed of zero or more field options,
+and it is terminated with `end`.
+
+Inside the field block the options can be defined by providing the option name and the field
+value that this option represents when the bit offsets are used to apply a mask and shift.
+Option names must also be at least three characters for the sconfig parser.
+
+ option <name> <value>
+
+It is possible for there to be multiple `fw_config` blocks and for subsequent `field` blocks
+to add additional `option` definitions to the existing field. These subsequent definitions
+should not provide the field bitmask as it has already been defined earlier in the file and
+this is just matching an existing field by name.
+
+ field <name> [option...] end
+
+This allows a baseboard to define the major fields and options in `devicetree.cb` and a board
+variant to add specific options to fields in or define new fields in the unused bitmask in
+`overridetree.cb`.
+
+It is not possible to redefine a field mask or override the value of an existing option this
+way, only to add new options to a field or new fields to the table.
+
+### Firmware Configuration Table Example
+
+In this example a baseboard defines a simple boolean feature that is enabled or disabled
+depending on the value of bit 0, and a field at bits 1-2 that indicates which daughter board
+is attached.
+
+The baseboard itself defines one daughter board and the variant adds two more possibilities.
+This way each variant can support multiple possible daughter boards in addition to the one
+that was defined by the baseboard.
+
+#### devicetree.cb
+
+ fw_config
+ field FEATURE 0
+ option DISABLED 0
+ option ENABLED 1
+ end
+ field DAUGHTER_BOARD 1 2
+ option NONE 0
+ option REFERENCE_DB 1
+ end
+ end
+
+#### overridetree.cb
+
+ fw_config
+ field DAUGHTER_BOARD
+ option VARIANT_DB_ONE 2
+ option VARIANT_DB_TWO 3
+ end
+ end
+
+The result of this table defined in `devicetree.cb` is a list of constants that can be used
+to check if fields match the firmware configuration options determined at runtime with a
+simple check of the field mask and the option value.
+
+#### static.h
+
+```c
+/* field: FEATURE */
+#define FW_CONFIG_FIELD_FEATURE_NAME "FEATURE"
+#define FW_CONFIG_FIELD_FEATURE_MASK 0x00000001
+#define FW_CONFIG_FIELD_FEATURE_OPTION_DISABLED_NAME "DISABLED"
+#define FW_CONFIG_FIELD_FEATURE_OPTION_DISABLED_VALUE 0x00000000
+#define FW_CONFIG_FIELD_FEATURE_OPTION_ENABLED_NAME "ENABLED"
+#define FW_CONFIG_FIELD_FEATURE_OPTION_ENABLED_VALUE 0x00000001
+
+/* field: DAUGHTER_BOARD */
+#define FW_CONFIG_FIELD_DAUGHTER_BOARD_NAME "DAUGHTER_BOARD"
+#define FW_CONFIG_FIELD_DAUGHTER_BOARD_MASK 0x00000006
+#define FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_NONE_NAME "NONE"
+#define FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_NONE_VALUE 0x00000000
+#define FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_REFERENCE_DB_NAME "REFERENCE_DB"
+#define FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_REFERENCE_DB_VALUE 0x00000002
+#define FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_ONE_NAME "VARIANT_DB_ONE"
+#define FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_ONE_VALUE 0x00000004
+#define FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_TWO_NAME "VARIANT_DB_TWO"
+#define FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_TWO_VALUE 0x00000006
+```
+
+## Device Probing
+
+One use of the firmware configuration interface in devicetree is to allow device probing to be
+specified directly with the devices themselves. A new `probe` token is introduced to allow a
+device to be probed by field and option name. Multiple `probe` entries may be present for
+each device and any successful probe will consider the device to be present.
+
+### Probing Example
+
+Continuing with the previous example this device would be considered present if the field
+`DAUGHTER_BOARD` was set to either `VARIANT_DB_ONE` or `VARIANT_DB_TWO`:
+
+#### overridetree.cb
+
+ chip drivers/generic/example
+ device generic 0 on
+ probe DAUGHTER_BOARD VARIANT_DB_ONE
+ probe DAUGHTER_BOARD VARIANT_DB_TWO
+ end
+ end
+
+If the field were set to any other option, including `NONE` and `REFERENCE_DB` and any
+undefined value then the device would be disabled.
+
+### Probe Overrides
+
+When a device is declared with a probe in the baseboard `devicetree.cb` and the same device
+is also present in the `overridetree.cb` then the probing information from the baseboard
+is discarded and the override device must provide all necessary probing information.
+
+In this example a device is listed in the baseboard with `DAUGHTER_BOARD` field probing for
+`REFERENCE_DB` as a field option, It is also defined as an override device with the field
+probing for the `VARIANT_DB_ONE` option instead.
+
+In this case only the probe listed in the override is checked and a field option of
+`REFERENCE_DB` will not mark this device present. If both options are desired then the
+override device must list both. This allows an override device to remove a probe entry that
+was defined in the baseboard.
+
+#### devicetree.cb
+
+ chip drivers/generic/example
+ device generic 0 on
+ probe DAUGHTER_BOARD REFERENCE_DB
+ end
+ end
+
+#### overridetree.cb
+
+ chip drivers/generic/example
+ device generic 0 on
+ probe DAUGHTER_BOARD VARIANT_DB_ONE
+ end
+ end
+
+### Automatic Device Probing
+
+At boot time the firmware configuration interface will walk the device tree and apply any
+probe entries that were defined in `devicetree.cb`. This probing takes effect before the
+`BS_DEV_ENUMERATE` step during the boot state machine in ramstage.
+
+Devices that have a probe list but do do not find a match are disabled by setting
+`dev->enabled = 0` but the chip `enable_dev()` and device `enable()` handlers will still
+be executed to allow any device disable code to execute.
+
+The result of this probe definition is to provide an array of structures describing each
+field and option to check.
+
+#### fw_config.h
+
+```c
+/**
+ * struct fw_config - Firmware configuration field and option.
+ * @field_name: Name of the field that this option belongs to.
+ * @option_name: Name of the option within this field.
+ * @mask: Bitmask of the field.
+ * @value: Value of the option within the mask.
+ */
+struct fw_config {
+ const char *field_name;
+ const char *option_name;
+ uint32_t mask;
+ uint32_t value;
+};
+```
+
+#### static.c
+
+```c
+STORAGE struct fw_config __devN_probe_list[] = {
+ {
+ .field_name = FW_CONFIG_FIELD_DAUGHTER_BOARD_NAME,
+ .option_name = FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_ONE_NAME,
+ .mask = FW_CONFIG_FIELD_DAUGHTER_BOARD_MASK,
+ .value = FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_ONE_VALUE
+ },
+ {
+ .field_name = FW_CONFIG_FIELD_DAUGHTER_BOARD_NAME,
+ .option_name = FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_TWO_NAME,
+ .mask = FW_CONFIG_FIELD_DAUGHTER_BOARD_MASK,
+ .value = FW_CONFIG_FIELD_DAUGHTER_BOARD_OPTION_VARIANT_DB_TWO_VALUE
+ },
+ { }
+};
+```
+
+### Runtime Probing
+
+The device driver probing allows for seamless integration with the mainboard but it is only
+effective in ramstage and for specific devices declared in devicetree.cb. There are other
+situations where code may need to probe or check the value of a field in romstage or at other
+points in ramstage. For this reason it is also possible to use the firmware configuration
+interface directly.
+
+```c
+/**
+ * fw_config_probe() - Check if field and option matches.
+ * @match: Structure containing field and option to probe.
+ *
+ * Return %true if match is found, %false if match is not found.
+ */
+bool fw_config_probe(const struct fw_config *match);
+```
+
+The argument provided to this function can be created from a macro for easy use:
+
+ FW_CONFIG(field, option)
+
+This example has a mainboard check if a feature is disabled and set an FSP UPD before memory
+training. This example expects that the default value of this `register` is set to `true` in
+`devicetree.cb` and this code is disabling that feature before FSP is executed.
+
+```c
+#include <fw_config.h>
+
+void mainboard_memory_init_params(FSPM_UPD *mupd)
+{
+ if (fw_config_probe_one(FW_CONFIG(FEATURE, DISABLED))
+ mupd->ExampleFeature = false;
+}
+```
diff --git a/Documentation/lib/index.md b/Documentation/lib/index.md
index 99b8061325..d64b4e999e 100644
--- a/Documentation/lib/index.md
+++ b/Documentation/lib/index.md
@@ -3,7 +3,7 @@
This section contains documentation about coreboot internal technical
information and libraries.
-## Structure and layout
- [Flashmap and Flashmap Descriptor](flashmap.md)
- [ABI data consumption](abi-data-consumption.md)
- [Timestamps](timestamp.md)
+- [Firmware Configuration Interface](fw_config.md)
diff --git a/src/Kconfig b/src/Kconfig
index 2a2a144235..b96d51f526 100644
--- a/src/Kconfig
+++ b/src/Kconfig
@@ -323,6 +323,33 @@ config BOOTSPLASH_FILE
The path and filename of the file to use as graphical bootsplash
screen. The file format has to be jpg.
+config FW_CONFIG
+ bool "Firmware Configuration Probing"
+ default n
+ help
+ Enable support for probing devices with fw_config. This is a simple
+ bitmask broken into fields and options for probing.
+
+config FW_CONFIG_SOURCE_CBFS
+ bool "Obtain Firmware Configuration value from CBFS"
+ depends on FW_CONFIG
+ default n
+ help
+ With this option enabled coreboot will look for the 32bit firmware
+ configuration value in CBFS at the selected prefix with the file name
+ "fw_config". This option will override other sources and allow the
+ local image to preempt the mainboard selected source.
+
+config FW_CONFIG_SOURCE_CHROMEEC_CBI
+ bool "Obtain Firmware Configuration value from Google Chrome EC CBI"
+ depends on FW_CONFIG && EC_GOOGLE_CHROMEEC
+ default n
+ help
+ This option tells coreboot to read the firmware configuration value
+ from the Google Chrome Embedded Controller CBI interface. This source
+ is not tried if FW_CONFIG_SOURCE_CBFS is enabled and the value was
+ found in CBFS.
+
config HAVE_RAMPAYLOAD
bool
diff --git a/src/include/device/device.h b/src/include/device/device.h
index 46efbfe679..082dcbb4d3 100644
--- a/src/include/device/device.h
+++ b/src/include/device/device.h
@@ -8,6 +8,7 @@
#include <smbios.h>
#include <types.h>
+struct fw_config;
struct device;
struct pci_operations;
struct i2c_bus_operations;
@@ -147,6 +148,9 @@ struct device {
#endif
#endif
DEVTREE_CONST void *chip_info;
+
+ /* Zero-terminated array of fields and options to probe. */
+ DEVTREE_CONST struct fw_config *probe_list;
};
/**
diff --git a/src/include/fw_config.h b/src/include/fw_config.h
new file mode 100644
index 0000000000..d41afd6c5d
--- /dev/null
+++ b/src/include/fw_config.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __FW_CONFIG__
+#define __FW_CONFIG__
+
+#include <device/device.h>
+#include <static.h> /* Provides fw_config definitions from devicetree.cb */
+#include <stdbool.h>
+#include <stdint.h>
+
+/**
+ * struct fw_config - Firmware configuration field and option.
+ * @field_name: Name of the field that this option belongs to.
+ * @option_name: Name of the option within this field.
+ * @mask: Bitmask of the field.
+ * @value: Value of the option within the mask.
+ */
+struct fw_config {
+ const char *field_name;
+ const char *option_name;
+ uint32_t mask;
+ uint32_t value;
+};
+
+/* Generate a pointer to a compound literal of the fw_config structure. */
+#define FW_CONFIG(__field, __option) (&(const struct fw_config) { \
+ .field_name = FW_CONFIG_FIELD_##__field##_NAME, \
+ .option_name = FW_CONFIG_FIELD_##__field##_OPTION_##__option##_NAME, \
+ .mask = FW_CONFIG_FIELD_##__field##_MASK, \
+ .value = FW_CONFIG_FIELD_##__field##_OPTION_##__option##_VALUE \
+})
+
+#if CONFIG(FW_CONFIG)
+
+/**
+ * fw_config_probe() - Check if field and option matches.
+ * @match: Structure containing field and option to probe.
+ *
+ * Return %true if match is found, %false if match is not found.
+ */
+bool fw_config_probe(const struct fw_config *match);
+
+#else
+
+static inline bool fw_config_probe(const struct fw_config *match)
+{
+ /* Always return true when probing with disabled fw_config. */
+ return true;
+}
+
+#endif /* CONFIG(FW_CONFIG) */
+
+#endif /* __FW_CONFIG__ */
diff --git a/src/lib/Makefile.inc b/src/lib/Makefile.inc
index 6511c0c328..e0003bd1cf 100644
--- a/src/lib/Makefile.inc
+++ b/src/lib/Makefile.inc
@@ -159,6 +159,11 @@ romstage-y += hexdump.c
verstage-y += hexdump.c
smm-y += hexdump.c
+bootblock-$(CONFIG_FW_CONFIG) += fw_config.c
+verstage-$(CONFIG_FW_CONFIG) += fw_config.c
+romstage-$(CONFIG_FW_CONFIG) += fw_config.c
+ramstage-$(CONFIG_FW_CONFIG) += fw_config.c
+
bootblock-$(CONFIG_ESPI_DEBUG) += espi_debug.c
verstage-$(CONFIG_ESPI_DEBUG) += espi_debug.c
romstage-$(CONFIG_ESPI_DEBUG) += espi_debug.c
diff --git a/src/lib/fw_config.c b/src/lib/fw_config.c
new file mode 100644
index 0000000000..e97cfdc72a
--- /dev/null
+++ b/src/lib/fw_config.c
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <bootstate.h>
+#include <cbfs.h>
+#include <console/console.h>
+#include <device/device.h>
+#include <ec/google/chromeec/ec.h>
+#include <fw_config.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+/**
+ * fw_config_get() - Provide firmware configuration value.
+ *
+ * Return 32bit firmware configuration value determined for the system.
+ */
+static uint32_t fw_config_get(void)
+{
+ static uint32_t fw_config_value;
+ static bool fw_config_value_initialized;
+
+ /* Nothing to prepare if setup is already done. */
+ if (fw_config_value_initialized)
+ return fw_config_value;
+ fw_config_value_initialized = true;
+
+ /* Look in CBFS to allow override of value. */
+ if (CONFIG(FW_CONFIG_SOURCE_CBFS)) {
+ if (cbfs_boot_load_file(CONFIG_CBFS_PREFIX "/fw_config",
+ &fw_config_value, sizeof(fw_config_value),
+ CBFS_TYPE_RAW) != sizeof(fw_config_value)) {
+ printk(BIOS_WARNING, "%s: Could not get fw_config from CBFS\n",
+ __func__);
+ fw_config_value = 0;
+ } else {
+ printk(BIOS_INFO, "FW_CONFIG value from CBFS is 0x%08x\n",
+ fw_config_value);
+ return fw_config_value;
+ }
+ }
+
+ /* Read the value from EC CBI. */
+ if (CONFIG(FW_CONFIG_SOURCE_CHROMEEC_CBI)) {
+ if (google_chromeec_cbi_get_fw_config(&fw_config_value))
+ printk(BIOS_WARNING, "%s: Could not get fw_config from EC\n", __func__);
+ }
+
+ printk(BIOS_INFO, "FW_CONFIG value is 0x%08x\n", fw_config_value);
+ return fw_config_value;
+}
+
+bool fw_config_probe(const struct fw_config *match)
+{
+ /* Compare to system value. */
+ if ((fw_config_get() & match->mask) == match->value) {
+ if (match->field_name && match->option_name)
+ printk(BIOS_INFO, "fw_config match found: %s=%s\n", match->field_name,
+ match->option_name);
+ else
+ printk(BIOS_INFO, "fw_config match found: mask=0x%08x value=0x%08x\n",
+ match->mask, match->value);
+ return true;
+ }
+
+ return false;
+}
+
+#if ENV_RAMSTAGE
+static void fw_config_init(void *unused)
+{
+ struct device *dev;
+
+ for (dev = all_devices; dev; dev = dev->next) {
+ const struct fw_config *probe;
+ bool match = false;
+
+ if (!dev->probe_list)
+ continue;
+
+ for (probe = dev->probe_list; probe && probe->mask != 0; probe++) {
+ if (fw_config_probe(probe)) {
+ match = true;
+ break;
+ }
+ }
+
+ if (!match) {
+ printk(BIOS_INFO, "%s disabled by fw_config\n", dev_path(dev));
+ dev->enabled = 0;
+ }
+ }
+}
+BOOT_STATE_INIT_ENTRY(BS_DEV_ENUMERATE, BS_ON_ENTRY, fw_config_init, NULL);
+#endif