diff --git a/Documentation/drivers/cfr_internal.md b/Documentation/drivers/cfr_internal.md index 95fef66eee..b2355d7b62 100644 --- a/Documentation/drivers/cfr_internal.md +++ b/Documentation/drivers/cfr_internal.md @@ -50,8 +50,9 @@ static const struct sm_object serial_number = SM_DECLARE_VARCHAR({ The CFR options can have a dependency that must be evaluated at runtime by the OS/payload that parses the CFR record and displays the UI. -By using the `WITH_DEP()` macro you can specify another numberic option that -is checked to hide the current option. +By using the `WITH_DEP()` macro you can specify another numeric option that +is checked to hide the current option. The `WITH_DEP_VALUES()` macro allows +specifying one or more values that cause the dependent option to be displayed. **Example:** Declares a dependency from `sata_disable_port0` to `sata_enable`. The option `sata_disable_port0` will be hidden as long as "sata_enable" is 0. @@ -76,6 +77,37 @@ static struct sm_object sata_disable_port0 = SM_DECLARE_BOOL({ }, WITH_DEP(&sata_enable)); ``` +**Example:** Declares a dependency from `com1_termination` to `com1_mode`. +The option `com1_termination` will only be shown if `com1_mode` is set to RS-485. + +``` +#define COM_MODE_DISABLED 3 +#define COM_MODE_RS232 0 +#define COM_MODE_RS485 1 + +static struct sm_object com1_mode = SM_DECLARE_ENUM({ + .flags = CFR_OPTFLAG_RUNTIME, + .opt_name = "com1_mode", + .ui_name = "COM1 Mode", + .ui_helptext = NULL, + .default_value = 1, + .values = (const struct sm_enum_value[]) { + { "Disabled", COM_MODE_DISABLED }, + { "RS-232", COM_MODE_RS232 }, + { "RS-485", COM_MODE_RS485 }, + SM_ENUM_VALUE_END }, +}); + +static struct sm_object com1_termination = SM_DECLARE_BOOL({ + .flags = CFR_OPTFLAG_RUNTIME, + .opt_name = "com1_termination", + .ui_name = "Enable COM1 termination resistors", + .ui_helptext = NULL, + .default_value = false, +}, WITH_DEP_VALUES(&com1_mode, COM_MODE_RS485)); + +``` + ### Providing mainboard custom options A mainboard that uses CFR can provide a list of custom options diff --git a/src/commonlib/include/commonlib/cfr.h b/src/commonlib/include/commonlib/cfr.h index 5d8b37bf56..570ce3c2d1 100644 --- a/src/commonlib/include/commonlib/cfr.h +++ b/src/commonlib/include/commonlib/cfr.h @@ -63,6 +63,7 @@ enum cfr_tags { CFR_TAG_VARCHAR_UI_HELPTEXT = 9, CFR_TAG_VARCHAR_DEF_VALUE = 10, CFR_TAG_OPTION_COMMENT = 11, + CFR_TAG_DEP_VALUES = 12, }; /* @@ -104,7 +105,8 @@ enum cfr_option_flags { struct __packed lb_cfr_varbinary { uint32_t tag; /* * CFR_TAG_VARCHAR_OPT_NAME, CFR_TAG_VARCHAR_UI_NAME, - * CFR_TAG_VARCHAR_UI_HELPTEXT or CFR_TAG_VARCHAR_DEF_VALUE + * CFR_TAG_VARCHAR_UI_HELPTEXT, CFR_TAG_VARCHAR_DEF_VALUE + * or CFR_TAG_DEP_VALUES */ uint32_t size; /* Length of the entire structure */ uint32_t data_length; /* Length of data, including NULL terminator for strings */ @@ -136,6 +138,7 @@ struct __packed lb_cfr_numeric_option { * struct lb_cfr_varbinary opt_name * struct lb_cfr_varbinary ui_name * struct lb_cfr_varbinary ui_helptext (Optional) + * struct lb_cfr_varbinary dependency_values (Optional) * struct lb_cfr_enum_value enum_values[] */ }; @@ -153,6 +156,7 @@ struct __packed lb_cfr_varchar_option { * struct lb_cfr_varbinary opt_name * struct lb_cfr_varbinary ui_name * struct lb_cfr_varbinary ui_helptext (Optional) + * struct lb_cfr_varbinary dependency_values (Optional) */ }; @@ -172,6 +176,7 @@ struct __packed lb_cfr_option_comment { /* * struct lb_cfr_varbinary ui_name * struct lb_cfr_varbinary ui_helptext (Optional) + * struct lb_cfr_varbinary dependency_values (Optional) */ }; @@ -186,6 +191,7 @@ struct __packed lb_cfr_option_form { uint32_t flags; /* enum cfr_option_flags */ /* * struct lb_cfr_varbinary ui_name + * struct lb_cfr_varbinary dependency_values (Optional) * struct lb_cfr_varchar_option options[] */ }; diff --git a/src/drivers/option/cfr.c b/src/drivers/option/cfr.c index 15c4fd9b4e..c439ed9546 100644 --- a/src/drivers/option/cfr.c +++ b/src/drivers/option/cfr.c @@ -69,6 +69,25 @@ static uint32_t sm_write_ui_helptext(char *current, const char *string) return write_cfr_varchar(current, string, CFR_TAG_VARCHAR_UI_HELPTEXT); } +static uint32_t sm_write_dep_values(char *current, + const uint32_t *dep_values, const uint32_t num_dep_values) +{ + /* Dependency values are optional */ + if (!dep_values || !num_dep_values) + return 0; + + struct lb_cfr_varbinary *cfr_values = (struct lb_cfr_varbinary *)current; + cfr_values->tag = CFR_TAG_DEP_VALUES; + cfr_values->data_length = sizeof(*dep_values) * num_dep_values; + char *data = current + sizeof(*cfr_values); + memcpy(data, dep_values, cfr_values->data_length); + + /* Make sure that every TAG/SIZE field is always aligned to LB_ENTRY_ALIGN */ + cfr_values->size = ALIGN_UP(sizeof(*cfr_values) + cfr_values->data_length, LB_ENTRY_ALIGN); + + return cfr_values->size; +} + static uint32_t sm_write_enum_value(char *current, const struct sm_enum_value *e) { struct lb_cfr_enum_value *enum_val = (struct lb_cfr_enum_value *)current; @@ -86,7 +105,7 @@ static uint32_t sm_write_enum_value(char *current, const struct sm_enum_value *e static uint32_t write_numeric_option(char *current, uint32_t tag, const uint64_t object_id, const char *opt_name, const char *ui_name, const char *ui_helptext, uint32_t flags, uint32_t default_value, const struct sm_enum_value *values, - const uint64_t dep_id) + const uint64_t dep_id, const uint32_t *dep_values, const uint32_t num_dep_values) { struct lb_cfr_numeric_option *option = (struct lb_cfr_numeric_option *)current; size_t len; @@ -110,6 +129,7 @@ static uint32_t write_numeric_option(char *current, uint32_t tag, const uint64_t return 0; current += len; current += sm_write_ui_helptext(current, ui_helptext); + current += sm_write_dep_values(current, dep_values, num_dep_values); if (option->tag == CFR_TAG_OPTION_ENUM && values) { for (const struct sm_enum_value *e = values; e->ui_name; e++) { @@ -122,35 +142,41 @@ static uint32_t write_numeric_option(char *current, uint32_t tag, const uint64_t } static uint32_t sm_write_opt_enum(char *current, const struct sm_obj_enum *sm_enum, - const uint64_t object_id, const uint64_t dep_id) + const uint64_t object_id, const uint64_t dep_id, + const uint32_t *dep_values, const uint32_t num_dep_values) { return write_numeric_option(current, CFR_TAG_OPTION_ENUM, object_id, sm_enum->opt_name, sm_enum->ui_name, sm_enum->ui_helptext, sm_enum->flags, sm_enum->default_value, sm_enum->values, - dep_id); + dep_id, dep_values, num_dep_values); } static uint32_t sm_write_opt_number(char *current, const struct sm_obj_number *sm_number, - const uint64_t object_id, const uint64_t dep_id) + const uint64_t object_id, const uint64_t dep_id, + const uint32_t *dep_values, const uint32_t num_dep_values) { return write_numeric_option(current, CFR_TAG_OPTION_NUMBER, object_id, sm_number->opt_name, sm_number->ui_name, sm_number->ui_helptext, - sm_number->flags, sm_number->default_value, NULL, dep_id); + sm_number->flags, sm_number->default_value, NULL, dep_id, + dep_values, num_dep_values); } static uint32_t sm_write_opt_bool(char *current, const struct sm_obj_bool *sm_bool, - const uint64_t object_id, const uint64_t dep_id) + const uint64_t object_id, const uint64_t dep_id, + const uint32_t *dep_values, const uint32_t num_dep_values) { return write_numeric_option(current, CFR_TAG_OPTION_BOOL, object_id, sm_bool->opt_name, sm_bool->ui_name, sm_bool->ui_helptext, - sm_bool->flags, sm_bool->default_value, NULL, dep_id); + sm_bool->flags, sm_bool->default_value, NULL, dep_id, + dep_values, num_dep_values); } static uint32_t sm_write_opt_varchar(char *current, const struct sm_obj_varchar *sm_varchar, - const uint64_t object_id, const uint64_t dep_id) + const uint64_t object_id, const uint64_t dep_id, + const uint32_t *dep_values, const uint32_t num_dep_values) { struct lb_cfr_varchar_option *option = (struct lb_cfr_varchar_option *)current; @@ -175,13 +201,15 @@ static uint32_t sm_write_opt_varchar(char *current, const struct sm_obj_varchar return 0; current += len; current += sm_write_ui_helptext(current, sm_varchar->ui_helptext); + current += sm_write_dep_values(current, dep_values, num_dep_values); option->size = cfr_record_size((char *)option, current); return option->size; } static uint32_t sm_write_opt_comment(char *current, const struct sm_obj_comment *sm_comment, - const uint32_t object_id, const uint32_t dep_id) + const uint32_t object_id, const uint32_t dep_id, + const uint32_t *dep_values, const uint32_t num_dep_values) { struct lb_cfr_option_comment *comment = (struct lb_cfr_option_comment *)current; size_t len; @@ -200,6 +228,7 @@ static uint32_t sm_write_opt_comment(char *current, const struct sm_obj_comment return 0; current += len; current += sm_write_ui_helptext(current, sm_comment->ui_helptext); + current += sm_write_dep_values(current, dep_values, num_dep_values); comment->size = cfr_record_size((char *)comment, current); return comment->size; @@ -215,7 +244,8 @@ static uint64_t sm_gen_obj_id(void *ptr) static uint32_t sm_write_object(char *current, const struct sm_object *sm_obj); static uint32_t sm_write_form(char *current, struct sm_obj_form *sm_form, - const uint64_t object_id, const uint64_t dep_id) + const uint64_t object_id, const uint64_t dep_id, + const uint32_t *dep_values, const uint32_t num_dep_values) { struct lb_cfr_option_form *form = (struct lb_cfr_option_form *)current; size_t len; @@ -234,6 +264,7 @@ static uint32_t sm_write_form(char *current, struct sm_obj_form *sm_form, if (!len) return 0; current += len; + current += sm_write_dep_values(current, dep_values, num_dep_values); while (sm_form->obj_list[i]) current += sm_write_object(current, sm_form->obj_list[i++]); @@ -245,6 +276,8 @@ static uint32_t sm_write_form(char *current, struct sm_obj_form *sm_form, static uint32_t sm_write_object(char *current, const struct sm_object *sm_obj) { uint64_t dep_id, obj_id; + const uint32_t *dep_values; + uint32_t num_dep_values; struct sm_object sm_obj_copy; assert(sm_obj); @@ -253,10 +286,14 @@ static uint32_t sm_write_object(char *current, const struct sm_object *sm_obj) /* Set dependency ID */ dep_id = 0; + dep_values = NULL; + num_dep_values = 0; if (sm_obj->dep) { - assert(sm_obj->dep->kind == SM_OBJ_BOOL); - if (sm_obj->dep->kind == SM_OBJ_BOOL) + if (sm_obj->dep->kind == SM_OBJ_BOOL || sm_obj->dep->kind == SM_OBJ_ENUM) { dep_id = sm_gen_obj_id((void *)sm_obj->dep); + dep_values = sm_obj->dep_values; + num_dep_values = sm_obj->num_dep_values; + } } /* Invoke callback to update fields */ @@ -273,21 +310,22 @@ static uint32_t sm_write_object(char *current, const struct sm_object *sm_obj) return 0; case SM_OBJ_ENUM: return sm_write_opt_enum(current, &sm_obj->sm_enum, obj_id, - dep_id); + dep_id, dep_values, num_dep_values); case SM_OBJ_NUMBER: return sm_write_opt_number(current, &sm_obj->sm_number, obj_id, - dep_id); + dep_id, dep_values, num_dep_values); case SM_OBJ_BOOL: return sm_write_opt_bool(current, &sm_obj->sm_bool, obj_id, - dep_id); + dep_id, dep_values, num_dep_values); case SM_OBJ_VARCHAR: return sm_write_opt_varchar(current, &sm_obj->sm_varchar, obj_id, - dep_id); + dep_id, dep_values, num_dep_values); case SM_OBJ_COMMENT: return sm_write_opt_comment(current, &sm_obj->sm_comment, obj_id, - dep_id); + dep_id, dep_values, num_dep_values); case SM_OBJ_FORM: - return sm_write_form(current, (struct sm_obj_form *)&sm_obj->sm_form, obj_id, dep_id); + return sm_write_form(current, (struct sm_obj_form *)&sm_obj->sm_form, obj_id, + dep_id, dep_values, num_dep_values); default: BUG(); printk(BIOS_ERR, "Unknown setup menu object kind %u, ignoring\n", sm_obj->kind); @@ -311,13 +349,13 @@ void cfr_write_setup_menu(struct lb_cfr *cfr_root, struct sm_obj_form *sm_root[] current += cfr_root->size; while (sm_root && sm_root[i]) - current += sm_write_form(current, sm_root[i++], 0, 0); + current += sm_write_form(current, sm_root[i++], 0, 0, NULL, 0); /* * Add generic forms. */ for (obj = &_cfr_forms[0]; obj != &_ecfr_forms[0]; obj++) - current += sm_write_form(current, obj, 0, 0); + current += sm_write_form(current, obj, 0, 0, NULL, 0); cfr_root->size = cfr_record_size((char *)cfr_root, current); diff --git a/src/drivers/option/cfr_frontend.h b/src/drivers/option/cfr_frontend.h index 2161853f01..21c18e779f 100644 --- a/src/drivers/option/cfr_frontend.h +++ b/src/drivers/option/cfr_frontend.h @@ -75,6 +75,8 @@ enum sm_object_kind { struct sm_object { enum sm_object_kind kind; const struct sm_object *dep; + const uint32_t *dep_values; + const uint32_t num_dep_values; void (*ctor)(const struct sm_object *obj, struct sm_object *new); /* Called on object creation */ union { struct sm_obj_enum sm_enum; @@ -87,21 +89,31 @@ struct sm_object { }; /* sm_object helpers with type checking */ -#define SM_DECLARE_ENUM(...) { .kind = SM_OBJ_ENUM, .dep = NULL, \ +#define SM_DECLARE_ENUM(...) { .kind = SM_OBJ_ENUM, .dep = NULL, \ + .dep_values = NULL, .num_dep_values = 0, \ .ctor = NULL, .sm_enum = __VA_ARGS__ } -#define SM_DECLARE_NUMBER(...) { .kind = SM_OBJ_NUMBER, .dep = NULL, \ +#define SM_DECLARE_NUMBER(...) { .kind = SM_OBJ_NUMBER, .dep = NULL, \ + .dep_values = NULL, .num_dep_values = 0, \ .ctor = NULL, .sm_number = __VA_ARGS__ } -#define SM_DECLARE_BOOL(...) { .kind = SM_OBJ_BOOL, .dep = NULL, \ +#define SM_DECLARE_BOOL(...) { .kind = SM_OBJ_BOOL, .dep = NULL, \ + .dep_values = NULL, .num_dep_values = 0, \ .ctor = NULL, .sm_bool = __VA_ARGS__ } -#define SM_DECLARE_VARCHAR(...) { .kind = SM_OBJ_VARCHAR, .dep = NULL, \ +#define SM_DECLARE_VARCHAR(...) { .kind = SM_OBJ_VARCHAR, .dep = NULL, \ + .dep_values = NULL, .num_dep_values = 0, \ .ctor = NULL, .sm_varchar = __VA_ARGS__ } -#define SM_DECLARE_COMMENT(...) { .kind = SM_OBJ_COMMENT, .dep = NULL, \ +#define SM_DECLARE_COMMENT(...) { .kind = SM_OBJ_COMMENT, .dep = NULL, \ + .dep_values = NULL, .num_dep_values = 0, \ .ctor = NULL, .sm_comment = __VA_ARGS__ } -#define SM_DECLARE_FORM(...) { .kind = SM_OBJ_FORM, .dep = NULL, \ +#define SM_DECLARE_FORM(...) { .kind = SM_OBJ_FORM, .dep = NULL, \ + .dep_values = NULL, .num_dep_values = 0, \ .ctor = NULL, .sm_form = __VA_ARGS__ } #define WITH_CALLBACK(c) .ctor = (c) #define WITH_DEP(d) .dep = (d) +#define WITH_DEP_VALUES(d, ...) \ + .dep = (d), \ + .dep_values = ((const uint32_t[]) { __VA_ARGS__ }), \ + .num_dep_values = sizeof((uint32_t[]) { __VA_ARGS__ }) / sizeof(uint32_t) void cfr_write_setup_menu(struct lb_cfr *cfr_root, struct sm_obj_form *sm_root[]);