drivers/option/cfr: Add optional override table for default values

Add a mechanism for mainboards to override default values of CFR
objects defined in SoC or common code without duplicating object
metadata.

Mainboards can now declare a simple override table mapping option
names to new default values:

  const struct cfr_default_override mb_cfr_overrides[] = {
      CFR_OVERRIDE_BOOL("s0ix_enable", false),
      CFR_OVERRIDE_ENUM("pciexp_aspm", ASPM_DISABLE),
      CFR_OVERRIDE_END
  };

The CFR backend checks this table when writing options and uses the
override value if one exists. All other metadata (name, help text,
enum values, flags) comes from the original object.

Change-Id: Ifb3da90d605f2799bf0207ff58d69bee3415ccc2
Signed-off-by: Matt DeVillier <matt.devillier@gmail.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/89933
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
Matt DeVillier 2025-11-06 14:41:05 -06:00
commit 786ac14d48
3 changed files with 134 additions and 2 deletions

View file

@ -23,6 +23,50 @@ In both cases you have to add `C` structs in ramstage to describe the
option and group them together into a form. CFR objects should reside
on the heap as they can be modified to match the current boot flow.
### Overriding default values
Mainboards often want to reuse CFR objects defined in SoC or common code
but with different default values. Rather than duplicating the entire object
definition, mainboards can declare an override table that maps option names
to new default values.
**Example:** Override defaults for several SoC-defined options:
```
#include <drivers/option/cfr_frontend.h>
#include <intelblocks/pcie_rp.h>
const struct cfr_default_override mb_cfr_overrides[] = {
CFR_OVERRIDE_BOOL("s0ix_enable", false),
CFR_OVERRIDE_ENUM("pciexp_aspm", ASPM_DISABLE),
CFR_OVERRIDE_NUMBER("igd_dvmt", 64),
CFR_OVERRIDE_END
};
void mb_cfr_setup_menu(struct lb_cfr *cfr_root)
{
/* Register overrides before writing menu */
cfr_register_overrides(mb_cfr_overrides);
cfr_write_setup_menu(cfr_root, sm_root);
}
```
When the CFR system writes the setup menu, it will check the override table
for each option and use the override value if one exists. All other object
metadata (name, help text, enum values, flags) comes from the original object.
The following helper macros are available to populate the table:
- `CFR_OVERRIDE_BOOL(name, value)`
- `CFR_OVERRIDE_ENUM(name, value)`
- `CFR_OVERRIDE_NUMBER(name, value)`
- `CFR_OVERRIDE_VARCHAR(name, value)`
- `CFR_OVERRIDE_END`
Each macro encodes the override type, and the CFR backend validates that the
override type matches the original object's type. If the types do not match,
the override is ignored and a warning is printed.
### Updating CFR options
The CFR options should be updated before tables are written.
@ -143,4 +187,4 @@ static const __cfr_form struct sm_obj_form southbridge = {
NULL
},
};
```
```

View file

@ -10,6 +10,27 @@
#include <string.h>
#include <types.h>
/* Global override table registered by mainboard */
static const struct cfr_default_override *mb_overrides = NULL;
void cfr_register_overrides(const struct cfr_default_override *overrides)
{
mb_overrides = overrides;
}
/* Look up override for a given option name */
static const struct cfr_default_override *find_override(const char *opt_name)
{
if (!mb_overrides || !opt_name)
return NULL;
for (const struct cfr_default_override *ovr = mb_overrides; ovr->opt_name; ovr++) {
if (strcmp(ovr->opt_name, opt_name) == 0)
return ovr;
}
return NULL;
}
static uint32_t cfr_record_size(const char *startp, const char *endp)
{
const uintptr_t start = (uintptr_t)startp;
@ -111,6 +132,17 @@ static uint32_t write_numeric_option(char *current, uint32_t tag, const uint64_t
struct lb_cfr_numeric_option *option = (struct lb_cfr_numeric_option *)current;
size_t len;
/* Check for mainboard override of default value */
const struct cfr_default_override *ovr = find_override(opt_name);
if (ovr) {
if (ovr->kind != tag)
printk(BIOS_WARNING, "CFR: override for option '%s' has mismatched type; skipping.\n", opt_name);
else if (tag == CFR_TAG_OPTION_BOOL)
default_value = ovr->bool_value;
else
default_value = ovr->uint_value;
}
option->tag = tag;
option->object_id = object_id;
option->dependency_id = dep_id;
@ -187,6 +219,16 @@ static uint32_t sm_write_opt_varchar(char *current, const struct sm_obj_varchar
{
struct lb_cfr_varchar_option *option = (struct lb_cfr_varchar_option *)current;
size_t len;
const char *default_value = sm_varchar->default_value;
/* Check for mainboard override of default value */
const struct cfr_default_override *ovr = find_override(sm_varchar->opt_name);
if (ovr) {
if (ovr->kind == SM_OBJ_VARCHAR)
default_value = ovr->str_value;
else
printk(BIOS_WARNING, "CFR: override for option '%s' has mismatched type (not varchar); skipping.\n", sm_varchar->opt_name);
}
option->tag = CFR_TAG_OPTION_VARCHAR;
option->object_id = object_id;
@ -197,7 +239,7 @@ static uint32_t sm_write_opt_varchar(char *current, const struct sm_obj_varchar
option->size = sizeof(*option);
current += option->size;
current += sm_write_string_default_value(current, sm_varchar->default_value);
current += sm_write_string_default_value(current, default_value);
len = sm_write_opt_name(current, sm_varchar->opt_name);
if (!len)
return 0;

View file

@ -5,6 +5,7 @@
#include <commonlib/coreboot_tables.h>
#include <commonlib/cfr.h>
#include <string.h>
#include <types.h>
/* Front-end */
@ -119,6 +120,51 @@ struct sm_object {
.dep_values = ((const uint32_t[]) { __VA_ARGS__ }), \
.num_dep_values = sizeof((uint32_t[]) { __VA_ARGS__ }) / sizeof(uint32_t)
/*
* CFR Default Value Override System
*
* Mainboards can override default values of CFR objects defined in SoC/common
* code by declaring an override table. This avoids duplicating object metadata.
*
* Usage:
* 1. Declare overrides in mainboard cfr.c:
*
* const struct cfr_default_override mb_cfr_overrides[] = {
* CFR_OVERRIDE_BOOL("s0ix_enable", false),
* CFR_OVERRIDE_ENUM("pciexp_aspm", ASPM_DISABLE),
* CFR_OVERRIDE_END
* };
*
* 2. Register the table in mb_cfr_setup_menu():
*
* void mb_cfr_setup_menu(struct lb_cfr *cfr_root)
* {
* cfr_register_overrides(mb_cfr_overrides);
* cfr_write_setup_menu(cfr_root, sm_root);
* }
*/
/* Override table entry */
struct cfr_default_override {
const char *opt_name;
enum sm_object_kind kind;
union {
bool bool_value;
uint32_t uint_value;
const char *str_value;
};
};
/* Helper macros for override table entries */
#define CFR_OVERRIDE_BOOL(name, val) { .opt_name = (name), .kind = SM_OBJ_BOOL, .bool_value = (val) }
#define CFR_OVERRIDE_ENUM(name, val) { .opt_name = (name), .kind = SM_OBJ_ENUM, .uint_value = (val) }
#define CFR_OVERRIDE_NUMBER(name, val) { .opt_name = (name), .kind = SM_OBJ_NUMBER, .uint_value = (val) }
#define CFR_OVERRIDE_VARCHAR(name, val) { .opt_name = (name), .kind = SM_OBJ_VARCHAR, .str_value = (val) }
#define CFR_OVERRIDE_END { .opt_name = NULL }
/* Register mainboard override table (call before cfr_write_setup_menu) */
void cfr_register_overrides(const struct cfr_default_override *overrides);
void cfr_write_setup_menu(struct lb_cfr *cfr_root, struct sm_obj_form *sm_root[]);
#if ENV_RAMSTAGE