diff --git a/Documentation/drivers/cfr_internal.md b/Documentation/drivers/cfr_internal.md index b2355d7b62..6aa896ed68 100644 --- a/Documentation/drivers/cfr_internal.md +++ b/Documentation/drivers/cfr_internal.md @@ -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 +#include + +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 }, }; -``` \ No newline at end of file +``` diff --git a/src/drivers/option/cfr.c b/src/drivers/option/cfr.c index 26bdd55f4d..a332d906d6 100644 --- a/src/drivers/option/cfr.c +++ b/src/drivers/option/cfr.c @@ -10,6 +10,27 @@ #include #include +/* 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; diff --git a/src/drivers/option/cfr_frontend.h b/src/drivers/option/cfr_frontend.h index 17332bdca4..3beaaa7690 100644 --- a/src/drivers/option/cfr_frontend.h +++ b/src/drivers/option/cfr_frontend.h @@ -5,6 +5,7 @@ #include #include +#include #include /* 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