A few new x86 commands and minor improvements

expo improvements
 binman support for signing FIT images
 -----BEGIN PGP SIGNATURE-----
 
 iQFFBAABCgAvFiEEslwAIq+Gp8wWVbYnfxc6PpAIreYFAmcSwRQRHHNqZ0BjaHJv
 bWl1bS5vcmcACgkQfxc6PpAIreYWLgf/dnh+7hFENA/pIp57kiihzLc4sH+XaCiR
 A6MDkSnYWFCJpklz2rPPGsyuxEVtNgVtHEpPwA8OPaXitfuyLW/u5woBN6Sdtcvw
 Fqvq72IIMIt3BR6pU2uYZ6w/4t0r0/2pdSYG7L77QJjGsFSe35/RbVaGeln//SEk
 7/5tnqQ13QbhfazwLWNGT7+8R65NcpSwywxGqMcKWMPLt0CW+DMmon4oBuya20pv
 i5uDIp7WWtyxFaCxB3vzKmaXVmX74x9wyWA/K6uWsWt0/9JnEpQh5+IcgKw5ifGF
 sz2kVyZgCXilw1itrmO67O98KPfbRSFIQO2gSZ5NnNB4wrIgFXK2KA==
 =1pdb
 -----END PGP SIGNATURE-----

Merge tag 'dm-pull-17oct24-take2' of https://gitlab.denx.de/u-boot/custodians/u-boot-dm

A few new x86 commands and minor improvements
expo improvements
binman support for signing FIT images
This commit is contained in:
Tom Rini 2024-10-18 22:32:45 -06:00
commit 7036abbd5c
85 changed files with 2237 additions and 345 deletions

View file

@ -916,6 +916,7 @@ BINMAN
M: Simon Glass <sjg@chromium.org> M: Simon Glass <sjg@chromium.org>
M: Alper Nebi Yasak <alpernebiyasak@gmail.com> M: Alper Nebi Yasak <alpernebiyasak@gmail.com>
S: Maintained S: Maintained
F: doc/develop/binman_tests.rst
F: tools/binman/ F: tools/binman/
BLKMAP BLKMAP

View file

@ -55,9 +55,10 @@ void board_init_f(ulong flag)
void board_boot_order(u32 *spl_boot_list) void board_boot_order(u32 *spl_boot_list)
{ {
struct sandbox_state *state = state_get_current();
spl_boot_list[0] = BOOT_DEVICE_VBE; spl_boot_list[0] = BOOT_DEVICE_VBE;
spl_boot_list[1] = BOOT_DEVICE_UPL; spl_boot_list[1] = state->upl ? BOOT_DEVICE_UPL : BOOT_DEVICE_BOARD;
spl_boot_list[2] = BOOT_DEVICE_BOARD;
} }
static int spl_board_load_file(struct spl_image_info *spl_image, static int spl_board_load_file(struct spl_image_info *spl_image,

View file

@ -39,6 +39,9 @@
/* IDs for the menu items */ /* IDs for the menu items */
item-id = <ID_CPU_SPEED_1 ID_CPU_SPEED_2 item-id = <ID_CPU_SPEED_1 ID_CPU_SPEED_2
ID_CPU_SPEED_3>; ID_CPU_SPEED_3>;
/* values for the menu items */
item-value = <0 3 6>;
}; };
power-loss { power-loss {

View file

@ -10,6 +10,7 @@
&sandbox_pmic { &sandbox_pmic {
compatible = "sandbox,pmic"; compatible = "sandbox,pmic";
sandbox,emul = <&emul_pmic0>;
buck1 { buck1 {
regulator-name = "SUPPLY_1.2V"; regulator-name = "SUPPLY_1.2V";

View file

@ -88,18 +88,6 @@ int checkcpu(void)
return 0; return 0;
} }
int print_cpuinfo(void)
{
char processor_name[CPU_MAX_NAME_LEN];
const char *name;
/* Print processor name */
name = cpu_get_name(processor_name);
printf("CPU: %s\n", name);
return 0;
}
void board_debug_uart_init(void) void board_debug_uart_init(void)
{ {
/* com1 / com2 decode range */ /* com1 / com2 decode range */

View file

@ -38,16 +38,6 @@ int arch_cpu_init(void)
return 0; return 0;
} }
int checkcpu(void)
{
return 0;
}
int print_cpuinfo(void)
{
return default_print_cpuinfo();
}
static void board_final_init(void) static void board_final_init(void)
{ {
/* /*
@ -82,6 +72,8 @@ static void board_final_init(void)
static int last_stage_init(void) static int last_stage_init(void)
{ {
timestamp_add_to_bootstage();
if (IS_ENABLED(CONFIG_XPL_BUILD)) if (IS_ENABLED(CONFIG_XPL_BUILD))
return 0; return 0;

View file

@ -6,13 +6,12 @@
*/ */
#include <bootstage.h> #include <bootstage.h>
#include <errno.h>
#include <asm/arch/timestamp.h> #include <asm/arch/timestamp.h>
#include <asm/cb_sysinfo.h> #include <asm/cb_sysinfo.h>
#include <asm/u-boot-x86.h> #include <asm/u-boot-x86.h>
#include <linux/compiler.h> #include <linux/compiler.h>
static struct timestamp_table *ts_table __section(".data");
void timestamp_init(void) void timestamp_init(void)
{ {
timestamp_add_now(TS_U_BOOT_INITTED); timestamp_add_now(TS_U_BOOT_INITTED);
@ -20,6 +19,8 @@ void timestamp_init(void)
void timestamp_add(enum timestamp_id id, uint64_t ts_time) void timestamp_add(enum timestamp_id id, uint64_t ts_time)
{ {
const struct sysinfo_t *info = cb_get_sysinfo();
struct timestamp_table *ts_table = info->tstamp_table;
struct timestamp_entry *tse; struct timestamp_entry *tse;
if (!ts_table || (ts_table->num_entries == ts_table->max_entries)) if (!ts_table || (ts_table->num_entries == ts_table->max_entries))
@ -37,13 +38,15 @@ void timestamp_add_now(enum timestamp_id id)
int timestamp_add_to_bootstage(void) int timestamp_add_to_bootstage(void)
{ {
const struct sysinfo_t *info = cb_get_sysinfo();
const struct timestamp_table *ts_table = info->tstamp_table;
uint i; uint i;
if (!ts_table) if (!ts_table)
return -1; return -ENOENT;
for (i = 0; i < ts_table->num_entries; i++) { for (i = 0; i < ts_table->num_entries; i++) {
struct timestamp_entry *tse = &ts_table->entries[i]; const struct timestamp_entry *tse = &ts_table->entries[i];
const char *name = NULL; const char *name = NULL;
switch (tse->entry_id) { switch (tse->entry_id) {

View file

@ -163,8 +163,11 @@ char *cpu_get_name(char *name)
return ptr; return ptr;
} }
int default_print_cpuinfo(void) #if !CONFIG_IS_ENABLED(CPU)
int print_cpuinfo(void)
{ {
post_code(POST_CPU_INFO);
printf("CPU: %s, vendor %s, device %xh\n", printf("CPU: %s, vendor %s, device %xh\n",
cpu_has_64bit() ? "x86_64" : "x86", cpu_has_64bit() ? "x86_64" : "x86",
cpu_vendor_name(gd->arch.x86_vendor), gd->arch.x86_device); cpu_vendor_name(gd->arch.x86_vendor), gd->arch.x86_device);
@ -176,6 +179,7 @@ int default_print_cpuinfo(void)
return 0; return 0;
} }
#endif
#if CONFIG_IS_ENABLED(SHOW_BOOT_PROGRESS) #if CONFIG_IS_ENABLED(SHOW_BOOT_PROGRESS)
void show_boot_progress(int val) void show_boot_progress(int val)
@ -336,7 +340,7 @@ int reserve_arch(void)
} }
#endif #endif
long detect_coreboot_table_at(ulong start, ulong size) static long detect_coreboot_table_at(ulong start, ulong size)
{ {
u32 *ptr, *end; u32 *ptr, *end;

View file

@ -7,6 +7,7 @@
#include <dm.h> #include <dm.h>
#include <errno.h> #include <errno.h>
#include <asm/cpu.h> #include <asm/cpu.h>
#include <asm/cpu_x86.h>
#include <asm/global_data.h> #include <asm/global_data.h>
DECLARE_GLOBAL_DATA_PTR; DECLARE_GLOBAL_DATA_PTR;

View file

@ -19,11 +19,6 @@ int checkcpu(void)
return 0; return 0;
} }
int print_cpuinfo(void)
{
return default_print_cpuinfo();
}
void board_final_init(void) void board_final_init(void)
{ {
} }

View file

@ -144,11 +144,6 @@ int checkcpu(void)
return 0; return 0;
} }
int print_cpuinfo(void)
{
return default_print_cpuinfo();
}
/* Find any available tables and copy them to a safe place */ /* Find any available tables and copy them to a safe place */
int reserve_arch(void) int reserve_arch(void)
{ {

View file

@ -263,6 +263,49 @@ static int build_vendor_name(char *vendor_name)
} }
#endif #endif
int x86_cpu_vendor_info(char *name)
{
uint cpu_device;
cpu_device = 0;
/* gcc 7.3 does not want to drop x86_vendors, so use #ifdef */
#ifndef CONFIG_TPL_BUILD
*name = '\0'; /* Unset */
/* Find the id and vendor_name */
if (!has_cpuid()) {
/* Its a 486 if we can modify the AC flag */
if (flag_is_changeable_p(X86_EFLAGS_AC))
cpu_device = 0x00000400; /* 486 */
else
cpu_device = 0x00000300; /* 386 */
if (cpu_device == 0x00000400 && test_cyrix_52div()) {
/* If we ever care we can enable cpuid here */
memcpy(name, "CyrixInstead", 13);
/* Detect NexGen with old hypercode */
} else if (deep_magic_nexgen_probe()) {
memcpy(name, "NexGenDriven", 13);
}
} else {
int cpuid_level;
cpuid_level = build_vendor_name(name);
name[12] = '\0';
/* Intel-defined flags: level 0x00000001 */
if (cpuid_level >= 0x00000001)
cpu_device = cpuid_eax(0x00000001);
else
/* Have CPUID level 0 only unheard of */
cpu_device = 0x00000400;
}
#endif /* CONFIG_TPL_BUILD */
return cpu_device;
}
static void identify_cpu(struct cpu_device_id *cpu) static void identify_cpu(struct cpu_device_id *cpu)
{ {
cpu->device = 0; /* fix gcc 4.4.4 warning */ cpu->device = 0; /* fix gcc 4.4.4 warning */
@ -289,41 +332,13 @@ static void identify_cpu(struct cpu_device_id *cpu)
return; return;
} }
/* gcc 7.3 does not want to drop x86_vendors, so use #ifdef */
#ifndef CONFIG_TPL_BUILD #ifndef CONFIG_TPL_BUILD
{
char vendor_name[16]; char vendor_name[16];
int i; int i;
vendor_name[0] = '\0'; /* Unset */ cpu->device = x86_cpu_vendor_info(vendor_name);
/* Find the id and vendor_name */
if (!has_cpuid()) {
/* Its a 486 if we can modify the AC flag */
if (flag_is_changeable_p(X86_EFLAGS_AC))
cpu->device = 0x00000400; /* 486 */
else
cpu->device = 0x00000300; /* 386 */
if ((cpu->device == 0x00000400) && test_cyrix_52div()) {
memcpy(vendor_name, "CyrixInstead", 13);
/* If we ever care we can enable cpuid here */
}
/* Detect NexGen with old hypercode */
else if (deep_magic_nexgen_probe())
memcpy(vendor_name, "NexGenDriven", 13);
} else {
int cpuid_level;
cpuid_level = build_vendor_name(vendor_name);
vendor_name[12] = '\0';
/* Intel-defined flags: level 0x00000001 */
if (cpuid_level >= 0x00000001) {
cpu->device = cpuid_eax(0x00000001);
} else {
/* Have CPUID level 0 only unheard of */
cpu->device = 0x00000400;
}
}
cpu->vendor = X86_VENDOR_UNKNOWN; cpu->vendor = X86_VENDOR_UNKNOWN;
for (i = 0; i < ARRAY_SIZE(x86_vendors); i++) { for (i = 0; i < ARRAY_SIZE(x86_vendors); i++) {
if (memcmp(vendor_name, x86_vendors[i].name, 12) == 0) { if (memcmp(vendor_name, x86_vendors[i].name, 12) == 0) {
@ -331,6 +346,7 @@ static void identify_cpu(struct cpu_device_id *cpu)
break; break;
} }
} }
}
#endif #endif
} }
@ -485,6 +501,11 @@ int x86_cpu_reinit_f(void)
return 0; return 0;
} }
void x86_get_identity_for_timer(void)
{
setup_identity();
}
void x86_enable_caches(void) void x86_enable_caches(void)
{ {
unsigned long cr0; unsigned long cr0;

View file

@ -237,7 +237,7 @@ void *x86_get_idt(void)
return &idt_ptr; return &idt_ptr;
} }
void __do_irq(int irq) static void __do_irq(int irq)
{ {
printf("Unhandled IRQ : %d\n", irq); printf("Unhandled IRQ : %d\n", irq);
} }

View file

@ -182,20 +182,6 @@ int checkcpu(void)
return 0; return 0;
} }
int print_cpuinfo(void)
{
char processor_name[CPU_MAX_NAME_LEN];
const char *name;
/* Print processor name */
name = cpu_get_name(processor_name);
printf("CPU: %s\n", name);
post_code(POST_CPU_INFO);
return 0;
}
void board_debug_uart_init(void) void board_debug_uart_init(void)
{ {
/* This enables the debug UART */ /* This enables the debug UART */

View file

@ -87,7 +87,7 @@ void mtrr_read_all(struct mtrr_info *info)
} }
} }
void mtrr_write_all(struct mtrr_info *info) static void mtrr_write_all(struct mtrr_info *info)
{ {
int reg_count = mtrr_get_var_count(); int reg_count = mtrr_get_var_count();
struct mtrr_state state; struct mtrr_state state;

View file

@ -109,12 +109,6 @@ int checkcpu(void)
{ {
return 0; return 0;
} }
int print_cpuinfo(void)
{
post_code(POST_CPU_INFO);
return default_print_cpuinfo();
}
#endif #endif
int arch_early_init_r(void) int arch_early_init_r(void)

View file

@ -266,12 +266,6 @@ int checkcpu(void)
return 0; return 0;
} }
int print_cpuinfo(void)
{
post_code(POST_CPU_INFO);
return default_print_cpuinfo();
}
static void quark_pcie_init(void) static void quark_pcie_init(void)
{ {
u32 val; u32 val;

View file

@ -54,8 +54,3 @@ int checkcpu(void)
{ {
return 0; return 0;
} }
int print_cpuinfo(void)
{
return default_print_cpuinfo();
}

View file

@ -19,8 +19,3 @@ int checkcpu(void)
{ {
return 0; return 0;
} }
int print_cpuinfo(void)
{
return default_print_cpuinfo();
}

View file

@ -75,3 +75,9 @@ void board_debug_uart_init(void)
/* this was already done in SPL */ /* this was already done in SPL */
} }
#endif #endif
void x86_get_identity_for_timer(void)
{
/* set the vendor to Intel so that native_calibrate_tsc() works */
gd->arch.x86_vendor = X86_VENDOR_INTEL;
}

View file

@ -32,9 +32,4 @@ int checkcpu(void)
{ {
return 0; return 0;
} }
int print_cpuinfo(void)
{
return 0;
}
#endif #endif

View file

@ -293,4 +293,11 @@ u32 cpu_get_stepping(void);
*/ */
int cpu_phys_address_size(void); int cpu_phys_address_size(void);
void board_final_init(void);
void board_final_cleanup(void);
#ifndef CONFIG_EFI_STUB
int reserve_arch(void);
#endif
#endif #endif

View file

@ -43,6 +43,15 @@ int x86_cpu_reinit_f(void);
*/ */
int x86_cpu_init_tpl(void); int x86_cpu_init_tpl(void);
/**
* x86_get_identity_for_timer() - Set up CPU identity for use by the early timer
*
* The timer can be needed early in board_f if bootstage is enabled. This
* function can be called from the TSC timer to make sure that the CPU-identity
* info has been set up
*/
void x86_get_identity_for_timer(void);
/** /**
* cpu_reinit_fpu() - Reinit the FPU if something is wrong with it * cpu_reinit_fpu() - Reinit the FPU if something is wrong with it
* *
@ -51,6 +60,14 @@ int x86_cpu_init_tpl(void);
*/ */
void cpu_reinit_fpu(void); void cpu_reinit_fpu(void);
/**
* x86_cpu_vendor_info() - Get the CPU-vendor name and device number
*
* @name: 13-byte area to hold the returned string
* Return: CPU device number read from cpuid
*/
int x86_cpu_vendor_info(char *name);
int cpu_init_f(void); int cpu_init_f(void);
void setup_gdt(struct global_data *id, u64 *gdt_addr); void setup_gdt(struct global_data *id, u64 *gdt_addr);
/* /*
@ -78,7 +95,6 @@ void x86_enable_caches(void);
void x86_disable_caches(void); void x86_disable_caches(void);
int x86_init_cache(void); int x86_init_cache(void);
phys_addr_t board_get_usable_ram_top(phys_size_t total_size); phys_addr_t board_get_usable_ram_top(phys_size_t total_size);
int default_print_cpuinfo(void);
/* Set up a UART which can be used with printch(), printhex8(), etc. */ /* Set up a UART which can be used with printch(), printhex8(), etc. */
int setup_internal_uart(int enable); int setup_internal_uart(int enable);

View file

@ -19,7 +19,12 @@ void arch_print_bdinfo(void)
bdinfo_print_num_l("clock_rate", gd->arch.clock_rate); bdinfo_print_num_l("clock_rate", gd->arch.clock_rate);
bdinfo_print_num_l("tsc_base", gd->arch.tsc_base); bdinfo_print_num_l("tsc_base", gd->arch.tsc_base);
bdinfo_print_num_l("vendor", gd->arch.x86_vendor); bdinfo_print_num_l("vendor", gd->arch.x86_vendor);
bdinfo_print_str(" name", cpu_vendor_name(gd->arch.x86_vendor)); if (!IS_ENABLED(CONFIG_X86_64)) {
char vendor_name[16];
x86_cpu_vendor_info(vendor_name);
bdinfo_print_str(" name", vendor_name);
}
bdinfo_print_num_l("model", gd->arch.x86_model); bdinfo_print_num_l("model", gd->arch.x86_model);
bdinfo_print_num_l("phys_addr in bits", cpu_phys_address_size()); bdinfo_print_num_l("phys_addr in bits", cpu_phys_address_size());
bdinfo_print_num_l("table start", gd->arch.table_start); bdinfo_print_num_l("table start", gd->arch.table_start);

View file

@ -26,12 +26,6 @@ int checkcpu(void)
return 0; return 0;
} }
int print_cpuinfo(void)
{
post_code(POST_CPU_INFO);
return default_print_cpuinfo();
}
int fsp_init_phase_pci(void) int fsp_init_phase_pci(void)
{ {
u32 status; u32 status;

View file

@ -434,6 +434,9 @@ int bootdev_find_by_label(const char *label, struct udevice **devp,
struct uclass *uc; struct uclass *uc;
enum uclass_id id; enum uclass_id id;
if (!CONFIG_IS_ENABLED(BLK))
return -ENOSYS;
ret = label_to_uclass(label, &seq, &method_flags); ret = label_to_uclass(label, &seq, &method_flags);
if (ret < 0) if (ret < 0)
return log_msg_ret("uc", ret); return log_msg_ret("uc", ret);

View file

@ -51,10 +51,11 @@ struct cedit_iter_priv {
int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id) int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id)
{ {
struct expo_arrange_info arr;
struct scene_obj_txt *txt; struct scene_obj_txt *txt;
struct scene_obj *obj; struct scene_obj *obj;
struct scene *scn; struct scene *scn;
int y; int y, ret;
scn = expo_lookup_scene_id(exp, scene_id); scn = expo_lookup_scene_id(exp, scene_id);
if (!scn) if (!scn)
@ -68,6 +69,11 @@ int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id)
if (txt) if (txt)
scene_obj_set_pos(scn, txt->obj.id, 200, 10); scene_obj_set_pos(scn, txt->obj.id, 200, 10);
memset(&arr, '\0', sizeof(arr));
ret = scene_calc_arrange(scn, &arr);
if (ret < 0)
return log_msg_ret("arr", ret);
y = 100; y = 100;
list_for_each_entry(obj, &scn->obj_head, sibling) { list_for_each_entry(obj, &scn->obj_head, sibling) {
switch (obj->type) { switch (obj->type) {
@ -77,12 +83,13 @@ int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id)
break; break;
case SCENEOBJT_MENU: case SCENEOBJT_MENU:
scene_obj_set_pos(scn, obj->id, 50, y); scene_obj_set_pos(scn, obj->id, 50, y);
scene_menu_arrange(scn, (struct scene_obj_menu *)obj); scene_menu_arrange(scn, &arr,
(struct scene_obj_menu *)obj);
y += 50; y += 50;
break; break;
case SCENEOBJT_TEXTLINE: case SCENEOBJT_TEXTLINE:
scene_obj_set_pos(scn, obj->id, 50, y); scene_obj_set_pos(scn, obj->id, 50, y);
scene_textline_arrange(scn, scene_textline_arrange(scn, &arr,
(struct scene_obj_textline *)obj); (struct scene_obj_textline *)obj);
y += 50; y += 50;
break; break;
@ -147,7 +154,7 @@ int cedit_run(struct expo *exp)
struct video_priv *vid_priv; struct video_priv *vid_priv;
uint scene_id; uint scene_id;
struct scene *scn; struct scene *scn;
bool done; bool done, save;
int ret; int ret;
cli_ch_init(cch); cli_ch_init(cch);
@ -157,6 +164,7 @@ int cedit_run(struct expo *exp)
scene_id = ret; scene_id = ret;
done = false; done = false;
save = false;
do { do {
struct expo_action act; struct expo_action act;
int ichar, key; int ichar, key;
@ -201,6 +209,15 @@ int cedit_run(struct expo *exp)
case EXPOACT_OPEN: case EXPOACT_OPEN:
scene_set_open(scn, act.select.id, true); scene_set_open(scn, act.select.id, true);
cedit_arange(exp, vid_priv, scene_id); cedit_arange(exp, vid_priv, scene_id);
switch (scn->highlight_id) {
case EXPOID_SAVE:
done = true;
save = true;
break;
case EXPOID_DISCARD:
done = true;
break;
}
break; break;
case EXPOACT_CLOSE: case EXPOACT_CLOSE:
scene_set_open(scn, act.select.id, false); scene_set_open(scn, act.select.id, false);
@ -222,6 +239,8 @@ int cedit_run(struct expo *exp)
if (ret) if (ret)
return log_msg_ret("end", ret); return log_msg_ret("end", ret);
if (!save)
return -EACCES;
return 0; return 0;
} }
@ -274,14 +293,84 @@ static int get_cur_menuitem_text(const struct scene_obj_menu *menu,
return 0; return 0;
} }
/**
* get_cur_menuitem_val() - Get the value of a menu's current item
*
* Obtains the value of the current item in the menu. If no value, then
* enumerates the items of a menu (0, 1, 2) and returns the sequence number of
* the currently selected item. If the first item is selected, this returns 0;
* if the second, 1; etc.
*
* @menu: Menu to check
* @valp: Returns current-item value / sequence number
* Return: 0 on success, else -ve error value
*/
static int get_cur_menuitem_val(const struct scene_obj_menu *menu, int *valp)
{
const struct scene_menitem *mi;
int seq;
seq = 0;
list_for_each_entry(mi, &menu->item_head, sibling) {
if (mi->id == menu->cur_item_id) {
*valp = mi->value == INT_MAX ? seq : mi->value;
return 0;
}
seq++;
}
return log_msg_ret("nf", -ENOENT);
}
/**
* write_dt_string() - Write a string to the devicetree, expanding if needed
*
* If this fails, it tries again after expanding the devicetree a little
*
* @buf: Buffer containing the devicetree
* @name: Property name to use
* @str: String value
* Return: 0 if OK, -EFAULT if something went horribly wrong
*/
static int write_dt_string(struct abuf *buf, const char *name, const char *str) static int write_dt_string(struct abuf *buf, const char *name, const char *str)
{ {
int ret, i; int ret, i;
ret = -EAGAIN;
for (i = 0; ret && i < 2; i++) {
ret = fdt_property_string(abuf_data(buf), name, str);
if (!i) {
ret = check_space(ret, buf);
if (ret)
return log_msg_ret("rs2", -ENOMEM);
}
}
/* this should not happen */
if (ret)
return log_msg_ret("str", -EFAULT);
return 0;
}
/**
* write_dt_u32() - Write an int to the devicetree, expanding if needed
*
* If this fails, it tries again after expanding the devicetree a little
*
* @buf: Buffer containing the devicetree
* @name: Property name to use
* @lva: Integer value
* Return: 0 if OK, -EFAULT if something went horribly wrong
*/
static int write_dt_u32(struct abuf *buf, const char *name, uint val)
{
int ret, i;
/* write the text of the current item */ /* write the text of the current item */
ret = -EAGAIN; ret = -EAGAIN;
for (i = 0; ret && i < 2; i++) { for (i = 0; ret && i < 2; i++) {
ret = fdt_property_string(abuf_data(buf), name, str); ret = fdt_property_u32(abuf_data(buf), name, val);
if (!i) { if (!i) {
ret = check_space(ret, buf); ret = check_space(ret, buf);
if (ret) if (ret)
@ -320,23 +409,21 @@ static int h_write_settings(struct scene_obj *obj, void *vpriv)
const struct scene_obj_menu *menu; const struct scene_obj_menu *menu;
const char *str; const char *str;
char name[80]; char name[80];
int i; int val;
/* write the ID of the current item */ /* write the ID of the current item */
menu = (struct scene_obj_menu *)obj; menu = (struct scene_obj_menu *)obj;
ret = -EAGAIN; ret = write_dt_u32(buf, obj->name, menu->cur_item_id);
for (i = 0; ret && i < 2; i++) {
ret = fdt_property_u32(abuf_data(buf), obj->name,
menu->cur_item_id);
if (!i) {
ret = check_space(ret, buf);
if (ret) if (ret)
return log_msg_ret("res", -ENOMEM); return log_msg_ret("wrt", ret);
}
} snprintf(name, sizeof(name), "%s-value", obj->name);
/* this should not happen */ ret = get_cur_menuitem_val(menu, &val);
if (ret < 0)
return log_msg_ret("cur", ret);
ret = write_dt_u32(buf, name, val);
if (ret) if (ret)
return log_msg_ret("wrt", -EFAULT); return log_msg_ret("wr2", ret);
ret = get_cur_menuitem_text(menu, &str); ret = get_cur_menuitem_text(menu, &str);
if (ret) if (ret)
@ -470,6 +557,9 @@ static int h_write_settings_env(struct scene_obj *obj, void *vpriv)
const char *str; const char *str;
int val, ret; int val, ret;
if (obj->id < EXPOID_BASE_ID)
return 0;
snprintf(var, sizeof(var), "c.%s", obj->name); snprintf(var, sizeof(var), "c.%s", obj->name);
switch (obj->type) { switch (obj->type) {
@ -499,6 +589,14 @@ static int h_write_settings_env(struct scene_obj *obj, void *vpriv)
ret = env_set(name, str); ret = env_set(name, str);
if (ret) if (ret)
return log_msg_ret("st2", ret); return log_msg_ret("st2", ret);
ret = get_cur_menuitem_val(menu, &val);
if (ret < 0)
return log_msg_ret("cur", ret);
snprintf(name, sizeof(name), "c.%s-value", obj->name);
if (priv->verbose)
printf("%s=%d\n", name, val);
break; break;
case SCENEOBJT_TEXTLINE: { case SCENEOBJT_TEXTLINE: {
const struct scene_obj_textline *tline; const struct scene_obj_textline *tline;
@ -542,6 +640,9 @@ static int h_read_settings_env(struct scene_obj *obj, void *vpriv)
char var[60]; char var[60];
int val; int val;
if (obj->id < EXPOID_BASE_ID)
return 0;
snprintf(var, sizeof(var), "c.%s", obj->name); snprintf(var, sizeof(var), "c.%s", obj->name);
switch (obj->type) { switch (obj->type) {
@ -559,7 +660,7 @@ static int h_read_settings_env(struct scene_obj *obj, void *vpriv)
/* /*
* note that no validation is done here, to make sure the ID is * note that no validation is done here, to make sure the ID is
* valid * and actually points to a menu item * valid and actually points to a menu item
*/ */
menu->cur_item_id = val; menu->cur_item_id = val;
break; break;
@ -599,55 +700,23 @@ int cedit_read_settings_env(struct expo *exp, bool verbose)
return 0; return 0;
} }
/**
* get_cur_menuitem_seq() - Get the sequence number of a menu's current item
*
* Enumerates the items of a menu (0, 1, 2) and returns the sequence number of
* the currently selected item. If the first item is selected, this returns 0;
* if the second, 1; etc.
*
* @menu: Menu to check
* Return: Sequence number on success, else -ve error value
*/
static int get_cur_menuitem_seq(const struct scene_obj_menu *menu)
{
const struct scene_menitem *mi;
int seq, found;
seq = 0;
found = -1;
list_for_each_entry(mi, &menu->item_head, sibling) {
if (mi->id == menu->cur_item_id) {
found = seq;
break;
}
seq++;
}
if (found == -1)
return log_msg_ret("nf", -ENOENT);
return found;
}
static int h_write_settings_cmos(struct scene_obj *obj, void *vpriv) static int h_write_settings_cmos(struct scene_obj *obj, void *vpriv)
{ {
const struct scene_obj_menu *menu; const struct scene_obj_menu *menu;
struct cedit_iter_priv *priv = vpriv; struct cedit_iter_priv *priv = vpriv;
int val, ret; int val, ret;
uint i, seq; uint i;
if (obj->type != SCENEOBJT_MENU) if (obj->type != SCENEOBJT_MENU || obj->id < EXPOID_BASE_ID)
return 0; return 0;
menu = (struct scene_obj_menu *)obj; menu = (struct scene_obj_menu *)obj;
val = menu->cur_item_id; val = menu->cur_item_id;
ret = get_cur_menuitem_seq(menu); ret = get_cur_menuitem_val(menu, &val);
if (ret < 0) if (ret < 0)
return log_msg_ret("cur", ret); return log_msg_ret("cur", ret);
seq = ret; log_debug("%s: val=%d\n", menu->obj.name, val);
log_debug("%s: seq=%d\n", menu->obj.name, seq);
/* figure out where to place this item */ /* figure out where to place this item */
if (!obj->bit_length) if (!obj->bit_length)
@ -655,11 +724,11 @@ static int h_write_settings_cmos(struct scene_obj *obj, void *vpriv)
if (obj->start_bit + obj->bit_length > CMOS_MAX_BITS) if (obj->start_bit + obj->bit_length > CMOS_MAX_BITS)
return log_msg_ret("bit", -E2BIG); return log_msg_ret("bit", -E2BIG);
for (i = 0; i < obj->bit_length; i++, seq >>= 1) { for (i = 0; i < obj->bit_length; i++, val >>= 1) {
uint bitnum = obj->start_bit + i; uint bitnum = obj->start_bit + i;
priv->mask[CMOS_BYTE(bitnum)] |= 1 << CMOS_BIT(bitnum); priv->mask[CMOS_BYTE(bitnum)] |= 1 << CMOS_BIT(bitnum);
if (seq & 1) if (val & 1)
priv->value[CMOS_BYTE(bitnum)] |= BIT(CMOS_BIT(bitnum)); priv->value[CMOS_BYTE(bitnum)] |= BIT(CMOS_BIT(bitnum));
log_debug("bit %x %x %x\n", bitnum, log_debug("bit %x %x %x\n", bitnum,
priv->mask[CMOS_BYTE(bitnum)], priv->mask[CMOS_BYTE(bitnum)],
@ -693,6 +762,7 @@ int cedit_write_settings_cmos(struct expo *exp, struct udevice *dev,
} }
/* write the data to the RTC */ /* write the data to the RTC */
log_debug("Writing CMOS\n");
first = CMOS_MAX_BYTES; first = CMOS_MAX_BYTES;
last = -1; last = -1;
for (i = 0, count = 0; i < CMOS_MAX_BYTES; i++) { for (i = 0, count = 0; i < CMOS_MAX_BYTES; i++) {
@ -727,7 +797,7 @@ static int h_read_settings_cmos(struct scene_obj *obj, void *vpriv)
int val, ret; int val, ret;
uint i; uint i;
if (obj->type != SCENEOBJT_MENU) if (obj->type != SCENEOBJT_MENU || obj->id < EXPOID_BASE_ID)
return 0; return 0;
menu = (struct scene_obj_menu *)obj; menu = (struct scene_obj_menu *)obj;
@ -760,7 +830,8 @@ static int h_read_settings_cmos(struct scene_obj *obj, void *vpriv)
} }
/* update the current item */ /* update the current item */
mi = scene_menuitem_find_seq(menu, val); log_debug("look for menuitem value %d in menu %d\n", val, menu->obj.id);
mi = scene_menuitem_find_val(menu, val);
if (!mi) if (!mi)
return log_msg_ret("seq", -ENOENT); return log_msg_ret("seq", -ENOENT);
@ -794,7 +865,7 @@ int cedit_read_settings_cmos(struct expo *exp, struct udevice *dev,
goto done; goto done;
} }
/* read the data to the RTC */ /* indicate what bytes were read from the RTC */
first = CMOS_MAX_BYTES; first = CMOS_MAX_BYTES;
last = -1; last = -1;
for (i = 0, count = 0; i < CMOS_MAX_BYTES; i++) { for (i = 0, count = 0; i < CMOS_MAX_BYTES; i++) {

View file

@ -29,6 +29,7 @@ int expo_new(const char *name, void *priv, struct expo **expp)
exp->priv = priv; exp->priv = priv;
INIT_LIST_HEAD(&exp->scene_head); INIT_LIST_HEAD(&exp->scene_head);
INIT_LIST_HEAD(&exp->str_head); INIT_LIST_HEAD(&exp->str_head);
exp->next_id = EXPOID_BASE_ID;
*expp = exp; *expp = exp;
@ -258,6 +259,8 @@ int expo_apply_theme(struct expo *exp, ofnode node)
ofnode_read_u32(node, "font-size", &theme->font_size); ofnode_read_u32(node, "font-size", &theme->font_size);
ofnode_read_u32(node, "menu-inset", &theme->menu_inset); ofnode_read_u32(node, "menu-inset", &theme->menu_inset);
ofnode_read_u32(node, "menuitem-gap-y", &theme->menuitem_gap_y); ofnode_read_u32(node, "menuitem-gap-y", &theme->menuitem_gap_y);
ofnode_read_u32(node, "menu-title-margin-x",
&theme->menu_title_margin_x);
list_for_each_entry(scn, &exp->scene_head, sibling) { list_for_each_entry(scn, &exp->scene_head, sibling) {
ret = scene_apply_theme(scn, theme); ret = scene_apply_theme(scn, theme);

View file

@ -46,7 +46,6 @@ int add_txt_str(struct build_info *info, ofnode node, struct scene *scn,
const char *find_name, uint obj_id) const char *find_name, uint obj_id)
{ {
const char *text; const char *text;
uint str_id;
int ret; int ret;
info->err_prop = find_name; info->err_prop = find_name;
@ -67,12 +66,7 @@ int add_txt_str(struct build_info *info, ofnode node, struct scene *scn,
return log_msg_ret("id", -EINVAL); return log_msg_ret("id", -EINVAL);
} }
ret = expo_str(scn->expo, find_name, 0, text); ret = scene_txt_str(scn, find_name, obj_id, 0, text, NULL);
if (ret < 0)
return log_msg_ret("add", ret);
str_id = ret;
ret = scene_txt_str(scn, find_name, obj_id, str_id, text, NULL);
if (ret < 0) if (ret < 0)
return log_msg_ret("add", ret); return log_msg_ret("add", ret);
@ -94,7 +88,6 @@ int add_txt_str_list(struct build_info *info, ofnode node, struct scene *scn,
const char *find_name, int index, uint obj_id) const char *find_name, int index, uint obj_id)
{ {
const char *text; const char *text;
uint str_id;
int ret; int ret;
ret = ofnode_read_string_index(node, find_name, index, &text); ret = ofnode_read_string_index(node, find_name, index, &text);
@ -114,12 +107,7 @@ int add_txt_str_list(struct build_info *info, ofnode node, struct scene *scn,
return log_msg_ret("id", -EINVAL); return log_msg_ret("id", -EINVAL);
} }
ret = expo_str(scn->expo, find_name, 0, text); ret = scene_txt_str(scn, find_name, obj_id, 0, text, NULL);
if (ret < 0)
return log_msg_ret("add", ret);
str_id = ret;
ret = scene_txt_str(scn, find_name, obj_id, str_id, text, NULL);
if (ret < 0) if (ret < 0)
return log_msg_ret("add", ret); return log_msg_ret("add", ret);
@ -227,10 +215,10 @@ static void list_strings(struct build_info *info)
static int menu_build(struct build_info *info, ofnode node, struct scene *scn, static int menu_build(struct build_info *info, ofnode node, struct scene *scn,
uint id, struct scene_obj **objp) uint id, struct scene_obj **objp)
{ {
const u32 *item_ids, *item_values;
struct scene_obj_menu *menu; struct scene_obj_menu *menu;
int ret, size, i, num_items;
uint title_id, menu_id; uint title_id, menu_id;
const u32 *item_ids;
int ret, size, i;
const char *name; const char *name;
name = ofnode_get_name(node); name = ofnode_get_name(node);
@ -254,9 +242,15 @@ static int menu_build(struct build_info *info, ofnode node, struct scene *scn,
return log_msg_ret("itm", -EINVAL); return log_msg_ret("itm", -EINVAL);
if (!size || size % sizeof(u32)) if (!size || size % sizeof(u32))
return log_msg_ret("isz", -EINVAL); return log_msg_ret("isz", -EINVAL);
size /= sizeof(u32); num_items = size / sizeof(u32);
for (i = 0; i < size; i++) { item_values = ofnode_read_prop(node, "item-value", &size);
if (item_values) {
if (size != num_items * sizeof(u32))
return log_msg_ret("vsz", -EINVAL);
}
for (i = 0; i < num_items; i++) {
struct scene_menitem *item; struct scene_menitem *item;
uint label, key, desc; uint label, key, desc;
@ -280,6 +274,8 @@ static int menu_build(struct build_info *info, ofnode node, struct scene *scn,
desc, 0, 0, &item); desc, 0, 0, &item);
if (ret < 0) if (ret < 0)
return log_msg_ret("mi", ret); return log_msg_ret("mi", ret);
if (item_values)
item->value = fdt32_to_cpu(item_values[i]);
} }
*objp = &menu->obj; *objp = &menu->obj;
@ -408,7 +404,7 @@ static int scene_build(struct build_info *info, ofnode scn_node,
if (ret < 0) if (ret < 0)
return log_msg_ret("tit", ret); return log_msg_ret("tit", ret);
title_id = ret; title_id = ret;
scene_title_set(scn, title_id); scn->title_id = title_id;
ret = add_txt_str(info, scn_node, scn, "prompt", 0); ret = add_txt_str(info, scn_node, scn, "prompt", 0);
if (ret < 0) if (ret < 0)
@ -424,7 +420,7 @@ static int scene_build(struct build_info *info, ofnode scn_node,
return 0; return 0;
} }
int build_it(struct build_info *info, ofnode root, struct expo **expp) static int build_it(struct build_info *info, ofnode root, struct expo **expp)
{ {
ofnode scenes, node; ofnode scenes, node;
struct expo *exp; struct expo *exp;

View file

@ -70,13 +70,6 @@ void scene_destroy(struct scene *scn)
free(scn); free(scn);
} }
int scene_title_set(struct scene *scn, uint id)
{
scn->title_id = id;
return 0;
}
int scene_obj_count(struct scene *scn) int scene_obj_count(struct scene *scn)
{ {
return list_count_nodes(&scn->obj_head); return list_count_nodes(&scn->obj_head);
@ -339,7 +332,7 @@ static void scene_render_background(struct scene_obj *obj, bool box_only)
/* draw a background for the object */ /* draw a background for the object */
if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) { if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
fore = VID_BLACK; fore = VID_DARK_GREY;
back = VID_WHITE; back = VID_WHITE;
} else { } else {
fore = VID_LIGHT_GRAY; fore = VID_LIGHT_GRAY;
@ -471,11 +464,59 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode)
return 0; return 0;
} }
int scene_arrange(struct scene *scn) int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr)
{ {
struct scene_obj *obj; struct scene_obj *obj;
arr->label_width = 0;
list_for_each_entry(obj, &scn->obj_head, sibling) {
uint label_id = 0;
int width;
switch (obj->type) {
case SCENEOBJT_NONE:
case SCENEOBJT_IMAGE:
case SCENEOBJT_TEXT:
break;
case SCENEOBJT_MENU: {
struct scene_obj_menu *menu;
menu = (struct scene_obj_menu *)obj,
label_id = menu->title_id;
break;
}
case SCENEOBJT_TEXTLINE: {
struct scene_obj_textline *tline;
tline = (struct scene_obj_textline *)obj,
label_id = tline->label_id;
break;
}
}
if (label_id) {
int ret; int ret;
ret = scene_obj_get_hw(scn, label_id, &width);
if (ret < 0)
return log_msg_ret("hei", ret);
arr->label_width = max(arr->label_width, width);
}
}
return 0;
}
int scene_arrange(struct scene *scn)
{
struct expo_arrange_info arr;
struct scene_obj *obj;
int ret;
ret = scene_calc_arrange(scn, &arr);
if (ret < 0)
return log_msg_ret("arr", ret);
list_for_each_entry(obj, &scn->obj_head, sibling) { list_for_each_entry(obj, &scn->obj_head, sibling) {
switch (obj->type) { switch (obj->type) {
case SCENEOBJT_NONE: case SCENEOBJT_NONE:
@ -486,7 +527,7 @@ int scene_arrange(struct scene *scn)
struct scene_obj_menu *menu; struct scene_obj_menu *menu;
menu = (struct scene_obj_menu *)obj, menu = (struct scene_obj_menu *)obj,
ret = scene_menu_arrange(scn, menu); ret = scene_menu_arrange(scn, &arr, menu);
if (ret) if (ret)
return log_msg_ret("arr", ret); return log_msg_ret("arr", ret);
break; break;
@ -495,7 +536,7 @@ int scene_arrange(struct scene *scn)
struct scene_obj_textline *tline; struct scene_obj_textline *tline;
tline = (struct scene_obj_textline *)obj, tline = (struct scene_obj_textline *)obj,
ret = scene_textline_arrange(scn, tline); ret = scene_textline_arrange(scn, &arr, tline);
if (ret) if (ret)
return log_msg_ret("arr", ret); return log_msg_ret("arr", ret);
break; break;

View file

@ -96,10 +96,12 @@ int scene_calc_dims(struct scene *scn, bool do_menus);
* if not already done * if not already done
* *
* @scn: Scene to update * @scn: Scene to update
* @arr: Arrangement information
* @menu: Menu to process * @menu: Menu to process
* Returns: 0 if OK, -ve on error * Returns: 0 if OK, -ve on error
*/ */
int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu); int scene_menu_arrange(struct scene *scn, struct expo_arrange_info *arr,
struct scene_obj_menu *menu);
/** /**
* scene_textline_arrange() - Set the position of things in a textline * scene_textline_arrange() - Set the position of things in a textline
@ -108,10 +110,12 @@ int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu);
* positioned correctly relative to the textline. * positioned correctly relative to the textline.
* *
* @scn: Scene to update * @scn: Scene to update
* @arr: Arrangement information
* @tline: textline to process * @tline: textline to process
* Returns: 0 if OK, -ve on error * Returns: 0 if OK, -ve on error
*/ */
int scene_textline_arrange(struct scene *scn, struct scene_obj_textline *tline); int scene_textline_arrange(struct scene *scn, struct expo_arrange_info *arr,
struct scene_obj_textline *tline);
/** /**
* scene_apply_theme() - Apply a theme to a scene * scene_apply_theme() - Apply a theme to a scene
@ -277,6 +281,16 @@ struct scene_menitem *scene_menuitem_find(const struct scene_obj_menu *menu,
struct scene_menitem *scene_menuitem_find_seq(const struct scene_obj_menu *menu, struct scene_menitem *scene_menuitem_find_seq(const struct scene_obj_menu *menu,
uint seq); uint seq);
/**
* scene_menuitem_find_val() - Find the menu item with a given value
*
* @menu: Menu to check
* @find_val: Value to look for
* Return: menu item if found, else NULL
*/
struct scene_menitem *scene_menuitem_find_val(const struct scene_obj_menu *menu,
int val);
/** /**
* scene_bbox_union() - update bouding box with the demensions of an object * scene_bbox_union() - update bouding box with the demensions of an object
* *
@ -358,4 +372,16 @@ int scene_textline_open(struct scene *scn, struct scene_obj_textline *tline);
*/ */
int scene_textline_close(struct scene *scn, struct scene_obj_textline *tline); int scene_textline_close(struct scene *scn, struct scene_obj_textline *tline);
/**
* scene_calc_arrange() - Calculate sizes needed to arrange a scene
*
* Checks the size of some objects and stores this info to help with a later
* scene arrangement
*
* @scn: Scene to check
* @arr: Place to put scene-arrangement info
* Returns: 0 if OK, -ve on error
*/
int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr);
#endif /* __SCENE_INTERNAL_H */ #endif /* __SCENE_INTERNAL_H */

View file

@ -61,6 +61,22 @@ struct scene_menitem *scene_menuitem_find_seq(const struct scene_obj_menu *menu,
return NULL; return NULL;
} }
struct scene_menitem *scene_menuitem_find_val(const struct scene_obj_menu *menu,
int val)
{
struct scene_menitem *item;
uint i;
i = 0;
list_for_each_entry(item, &menu->item_head, sibling) {
if (item->value == INT_MAX ? val == i : item->value == val)
return item;
i++;
}
return NULL;
}
/** /**
* update_pointers() - Update the pointer object and handle highlights * update_pointers() - Update the pointer object and handle highlights
* *
@ -168,7 +184,8 @@ int scene_menu_calc_dims(struct scene_obj_menu *menu)
return 0; return 0;
} }
int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu) int scene_menu_arrange(struct scene *scn, struct expo_arrange_info *arr,
struct scene_obj_menu *menu)
{ {
const bool open = menu->obj.flags & SCENEOF_OPEN; const bool open = menu->obj.flags & SCENEOF_OPEN;
struct expo *exp = scn->expo; struct expo *exp = scn->expo;
@ -182,16 +199,18 @@ int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu)
x = menu->obj.dim.x; x = menu->obj.dim.x;
y = menu->obj.dim.y; y = menu->obj.dim.y;
if (menu->title_id) { if (menu->title_id) {
int width;
ret = scene_obj_set_pos(scn, menu->title_id, menu->obj.dim.x, y); ret = scene_obj_set_pos(scn, menu->title_id, menu->obj.dim.x, y);
if (ret < 0) if (ret < 0)
return log_msg_ret("tit", ret); return log_msg_ret("tit", ret);
ret = scene_obj_get_hw(scn, menu->title_id, NULL); ret = scene_obj_get_hw(scn, menu->title_id, &width);
if (ret < 0) if (ret < 0)
return log_msg_ret("hei", ret); return log_msg_ret("hei", ret);
if (stack) if (stack)
x += 200; x += arr->label_width + theme->menu_title_margin_x;
else else
y += ret * 2; y += ret * 2;
} }
@ -413,6 +432,7 @@ int scene_menuitem(struct scene *scn, uint menu_id, const char *name, uint id,
item->desc_id = desc_id; item->desc_id = desc_id;
item->preview_id = preview_id; item->preview_id = preview_id;
item->flags = flags; item->flags = flags;
item->value = INT_MAX;
list_add_tail(&item->sibling, &menu->item_head); list_add_tail(&item->sibling, &menu->item_head);
if (itemp) if (itemp)

View file

@ -87,7 +87,8 @@ int scene_textline_calc_dims(struct scene_obj_textline *tline)
return 0; return 0;
} }
int scene_textline_arrange(struct scene *scn, struct scene_obj_textline *tline) int scene_textline_arrange(struct scene *scn, struct expo_arrange_info *arr,
struct scene_obj_textline *tline)
{ {
const bool open = tline->obj.flags & SCENEOF_OPEN; const bool open = tline->obj.flags & SCENEOF_OPEN;
bool point; bool point;

View file

@ -2291,6 +2291,7 @@ config CMD_DATE
config CMD_RTC config CMD_RTC
bool "rtc" bool "rtc"
depends on DM_RTC depends on DM_RTC
default y if X86
help help
Enable the 'rtc' command for low-level access to RTC devices. Enable the 'rtc' command for low-level access to RTC devices.

View file

@ -55,9 +55,6 @@ static int do_font_size(struct cmd_tbl *cmdtp, int flag, int argc,
uint size; uint size;
int ret; int ret;
if (argc != 2)
return CMD_RET_USAGE;
if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev)) if (uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev))
return CMD_RET_FAILURE; return CMD_RET_FAILURE;
ret = vidconsole_get_font_size(dev, &font_name, &size); ret = vidconsole_get_font_size(dev, &font_name, &size);
@ -66,6 +63,9 @@ static int do_font_size(struct cmd_tbl *cmdtp, int flag, int argc,
return CMD_RET_FAILURE; return CMD_RET_FAILURE;
} }
if (argc < 2) {
printf("%d\n", size);
} else {
size = dectoul(argv[1], NULL); size = dectoul(argv[1], NULL);
ret = vidconsole_select_font(dev, font_name, size); ret = vidconsole_select_font(dev, font_name, size);
@ -73,6 +73,7 @@ static int do_font_size(struct cmd_tbl *cmdtp, int flag, int argc,
printf("Failed (error %d)\n", ret); printf("Failed (error %d)\n", ret);
return CMD_RET_FAILURE; return CMD_RET_FAILURE;
} }
}
return 0; return 0;
} }

View file

@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0+ # SPDX-License-Identifier: GPL-2.0+
obj-$(CONFIG_CMD_CBSYSINFO) += cbsysinfo.o obj-$(CONFIG_CMD_CBSYSINFO) += cbsysinfo.o
obj-y += mtrr.o obj-y += cpuid.o msr.o mtrr.o
obj-$(CONFIG_CMD_EXCEPTION) += exception.o obj-$(CONFIG_CMD_EXCEPTION) += exception.o
obj-$(CONFIG_USE_HOB) += hob.o obj-$(CONFIG_USE_HOB) += hob.o
obj-$(CONFIG_HAVE_FSP) += fsp.o obj-$(CONFIG_HAVE_FSP) += fsp.o

37
cmd/x86/cpuid.c Normal file
View file

@ -0,0 +1,37 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* The 'cpuid' command provides access to the CPU's cpuid information
*
* Copyright 2024 Google, LLC
* Written by Simon Glass <sjg@chromium.org>
*/
#include <command.h>
#include <vsprintf.h>
#include <asm/cpu.h>
static int do_cpuid(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
struct cpuid_result res;
ulong op;
if (argc < 2)
return CMD_RET_USAGE;
op = hextoul(argv[1], NULL);
res = cpuid(op);
printf("eax %08x\n", res.eax);
printf("ebx %08x\n", res.ebx);
printf("ecx %08x\n", res.ecx);
printf("edx %08x\n", res.edx);
return 0;
}
U_BOOT_LONGHELP(cpuid, "Show CPU Identification information");
U_BOOT_CMD(
cpuid, 2, 1, do_cpuid,
"cpuid <op>", cpuid_help_text
);

52
cmd/x86/msr.c Normal file
View file

@ -0,0 +1,52 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* The 'cpuid' command provides access to the CPU's cpuid information
*
* Copyright 2024 Google, LLC
* Written by Simon Glass <sjg@chromium.org>
*/
#include <command.h>
#include <vsprintf.h>
#include <asm/msr.h>
static int do_read(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
struct msr_t msr;
ulong op;
if (argc < 2)
return CMD_RET_USAGE;
op = hextoul(argv[1], NULL);
msr = msr_read(op);
printf("%08x %08x\n", msr.hi, msr.lo);
return 0;
}
static int do_write(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
{
struct msr_t msr;
ulong op;
if (argc < 4)
return CMD_RET_USAGE;
op = hextoul(argv[1], NULL);
msr.hi = hextoul(argv[2], NULL);
msr.lo = hextoul(argv[3], NULL);
msr_write(op, msr);
return 0;
}
U_BOOT_LONGHELP(msr,
"read <op> - read a machine-status register (MSR) as <hi 32-bits> <lo 32-bits>\n"
"write <op< <hi> <lo> - write an MSR");
U_BOOT_CMD_WITH_SUBCMDS(msr, "Machine Status Registers", msr_help_text,
U_BOOT_CMD_MKENT(read, CONFIG_SYS_MAXARGS, 1, do_read, "", ""),
U_BOOT_CMD_MKENT(write, CONFIG_SYS_MAXARGS, 1, do_write, "", ""));

View file

@ -61,6 +61,9 @@ CONFIG_SOUND=y
CONFIG_SOUND_I8254=y CONFIG_SOUND_I8254=y
CONFIG_VIDEO_COPY=y CONFIG_VIDEO_COPY=y
CONFIG_CONSOLE_TRUETYPE=y CONFIG_CONSOLE_TRUETYPE=y
CONFIG_CONSOLE_TRUETYPE_SIZE=20
# CONFIG_CONSOLE_TRUETYPE_NIMBUS is not set
CONFIG_CONSOLE_TRUETYPE_ANKACODER=y
CONFIG_CONSOLE_SCROLL_LINES=5 CONFIG_CONSOLE_SCROLL_LINES=5
CONFIG_SPL_ACPI=y CONFIG_SPL_ACPI=y
CONFIG_CMD_DHRYSTONE=y CONFIG_CMD_DHRYSTONE=y

View file

@ -11,6 +11,7 @@ CONFIG_SYS_MONITOR_BASE=0x01110000
CONFIG_FIT=y CONFIG_FIT=y
CONFIG_FIT_SIGNATURE=y CONFIG_FIT_SIGNATURE=y
CONFIG_BOOTSTD_FULL=y CONFIG_BOOTSTD_FULL=y
CONFIG_BOOTSTAGE=y
CONFIG_SHOW_BOOT_PROGRESS=y CONFIG_SHOW_BOOT_PROGRESS=y
CONFIG_USE_BOOTARGS=y CONFIG_USE_BOOTARGS=y
CONFIG_BOOTARGS="root=/dev/sdb3 init=/sbin/init rootwait ro" CONFIG_BOOTARGS="root=/dev/sdb3 init=/sbin/init rootwait ro"
@ -30,6 +31,7 @@ CONFIG_CMD_USB=y
CONFIG_BOOTP_BOOTFILESIZE=y CONFIG_BOOTP_BOOTFILESIZE=y
CONFIG_CMD_TIME=y CONFIG_CMD_TIME=y
CONFIG_CMD_SOUND=y CONFIG_CMD_SOUND=y
CONFIG_CMD_BOOTSTAGE=y
CONFIG_CMD_EXT4_WRITE=y CONFIG_CMD_EXT4_WRITE=y
CONFIG_MAC_PARTITION=y CONFIG_MAC_PARTITION=y
CONFIG_ENV_OVERWRITE=y CONFIG_ENV_OVERWRITE=y
@ -55,6 +57,9 @@ CONFIG_SOUND=y
CONFIG_SOUND_I8254=y CONFIG_SOUND_I8254=y
CONFIG_VIDEO_COPY=y CONFIG_VIDEO_COPY=y
CONFIG_CONSOLE_TRUETYPE=y CONFIG_CONSOLE_TRUETYPE=y
CONFIG_CONSOLE_TRUETYPE_SIZE=20
# CONFIG_CONSOLE_TRUETYPE_NIMBUS is not set
CONFIG_CONSOLE_TRUETYPE_ANKACODER=y
CONFIG_CONSOLE_SCROLL_LINES=5 CONFIG_CONSOLE_SCROLL_LINES=5
CONFIG_CMD_DHRYSTONE=y CONFIG_CMD_DHRYSTONE=y
# CONFIG_GZIP is not set # CONFIG_GZIP is not set

View file

@ -181,16 +181,4 @@ coreboot in CI
CI runs tests using a pre-built coreboot image. This ensures that U-Boot can CI runs tests using a pre-built coreboot image. This ensures that U-Boot can
boot as a coreboot payload, based on a known-good build of coreboot. boot as a coreboot payload, based on a known-good build of coreboot.
To update the `coreboot.rom` file which is used: To update the `coreboot.rom` file which is used, see ``tools/Dockerfile``
#. Build coreboot with `CONFIG_GENERIC_LINEAR_FRAMEBUFFER=y`. If using
`make menuconfig`, this is under
`Devices->Display->Framebuffer mode->Linear "high resolution" framebuffer`.
#. Compress the resulting `coreboot.rom`::
xz -c /path/to/coreboot/build/coreboot.rom > coreboot.rom.xz
#. Upload the file to Google drive
#. Send a patch to change the file ID used by wget in the CI yaml files.

View file

@ -0,0 +1,734 @@
.. SPDX-License-Identifier: GPL-2.0+
.. toctree::
:maxdepth: 1
Binman Tests
============
.. contents::
:depth: 2
:local:
There is some material on writing tests in the main Binman documentation
(see :doc:`package/index`). This short guide is separate so people don't
feel they have to read as much.
Code and output is mostly included verbatim, which makes the doc longer, but
avoids its becoming confusing when the output or referenced code changes in the
future.
Purpose
-------
The main purpose of tests in Binman is to make sure that Binman actually does
what it is supposed to. Various people contribute code, refactoring is done
over time, but U-Boot users (developers, SoC vendors, board vendors) rely on
Binman producing images which function correctly. Without tests, a one-line
change could unintentionally break a corner-case and the problem might not be
noticed for months. Debugging an image-generation problem with a board you
don't have can be very hard.
A secondary purpose is productivity. U-Boot contributors are busy and often
have too much on their plate. Trying to figure out why their patch broke
some other vendor's workflow can be very time-consuming and frustrating. By
building in tests from the start, this is largely avoided. If your change has
full test coverage and doesn't break any test, all is well and no one can
complain.
A lessor purpose is to document what Binman actually does. If a test covers a
feature, it works. If there is no test coverage, no one can say for sure
whether it works in all expected situations, certainly not wihout manual
effort.
In fact, strictly speaking it isn't completely clear what 'works' even means in
the case where these is no test to cover the code. We are often left guessing
as to what the documentation means, what was actually intended, etc.
Finally, code-coverage helps to remove 'zombie code', copied from elsewhere
because it looks reasonable, but not actually needed. The same situation arises
in silicon-chip design, where a part of the chip is not validated. If it isn't
validated, it can be assumed not to work, either now or later, so it is best to
remove that logic to avoid it causing problems.
Setting up
----------
Binman tests use various utility programs. Most of these are documented in
:doc:`../build/gcc`. But some are SoC-specific. To fetch these, tell Binman to
fetch or build any missing tools:
.. code-block:: bash
$ binman tool -f missing
When this completes successfully, you can list the tools. You should see
something like this:
.. code-block:: bash
$ binman tool -l
Name Version Description Path
--------------- ----------- ------------------------- ------------------------------
bootgen ****** Bootg Xilinx Bootgen /home/sglass/.binman-tools/bootgen
bzip2 1.0.8 bzip2 compression /usr/bin/bzip2
cbfstool unknown Manipulate CBFS files /home/sglass/bin/cbfstool
fdt_add_pubkey unknown Generate image for U-Boot /home/sglass/bin/fdt_add_pubkey
fdtgrep unknown Grep devicetree files /home/sglass/bin/fdtgrep
fiptool v2.11.0(rele Manipulate ATF FIP files /home/sglass/.binman-tools/fiptool
futility v0.0.1-9f2e9 Chromium OS firmware utili /home/sglass/.binman-tools/futility
gzip 1.12 gzip compression /usr/bin/gzip
ifwitool unknown Manipulate Intel IFWI file /home/sglass/.binman-tools/ifwitool
lz4 v1.9.4 lz4 compression /usr/bin/lz4
lzma_alone 9.22 beta lzma_alone compression /usr/bin/lzma_alone
lzop v1.04 lzo compression /usr/bin/lzop
mkeficapsule 2024.10-rc5- mkeficapsule tool for gene /home/sglass/bin/mkeficapsule
mkimage 2024.10-rc5- Generate image for U-Boot /home/sglass/bin/mkimage
openssl 3.0.13 30 Ja openssl cryptography toolk /usr/bin/openssl
xz 5.4.5 xz compression /usr/bin/xz
zstd v1.5.5 zstd compression /usr/bin/zstd
The tools are written to ``~/.binman-tools`` so add that to your ``PATH``.
It's fine to have some of the tools elsewhere (e.g. ``~/bin``) so long as they
are up-to-date. This allows you use the version of the tools intended for
running tests.
Now you should be able to actually run the tests:
.. code-block:: bash
$ binman test
======================== Running binman tests ========================
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
........
----------------------------------------------------------------------
Ran 568 tests in 2.578s
OK
If this doesn't work, see if you can have some missing tools. Check that the
dependencies are all there as above. If it is very slow, try installing
concurrencytest so that the tests run in parallel.
The next thing to set up is code coverage, using the -T flag:
.. code-block:: bash
$ binman test -T
======================== Running binman tests ========================
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
......................................................................
........
----------------------------------------------------------------------
Ran 568 tests in 17.367s
OK
99%
Name Stmts Miss Cover
---------------------------------------------------------------------------
tools/binman/__init__.py 0 0 100%
tools/binman/bintool.py 263 0 100%
tools/binman/btool/bootgen.py 21 0 100%
tools/binman/btool/btool_gzip.py 5 0 100%
tools/binman/btool/bzip2.py 5 0 100%
tools/binman/btool/cbfstool.py 24 0 100%
tools/binman/btool/cst.py 15 4 73%
tools/binman/btool/fdt_add_pubkey.py 21 0 100%
tools/binman/btool/fdtgrep.py 26 0 100%
tools/binman/btool/fiptool.py 19 0 100%
tools/binman/btool/futility.py 19 0 100%
tools/binman/btool/ifwitool.py 22 0 100%
tools/binman/btool/lz4.py 22 0 100%
tools/binman/btool/lzma_alone.py 34 0 100%
tools/binman/btool/lzop.py 5 0 100%
tools/binman/btool/mkeficapsule.py 27 0 100%
tools/binman/btool/mkimage.py 23 0 100%
tools/binman/btool/openssl.py 42 0 100%
tools/binman/btool/xz.py 5 0 100%
tools/binman/btool/zstd.py 5 0 100%
tools/binman/cbfs_util.py 376 0 100%
tools/binman/cmdline.py 90 0 100%
tools/binman/control.py 409 0 100%
tools/binman/elf.py 241 0 100%
tools/binman/entry.py 548 0 100%
tools/binman/etype/alternates_fdt.py 58 0 100%
tools/binman/etype/atf_bl31.py 5 0 100%
tools/binman/etype/atf_fip.py 67 0 100%
tools/binman/etype/blob.py 49 0 100%
tools/binman/etype/blob_dtb.py 46 0 100%
tools/binman/etype/blob_ext.py 9 0 100%
tools/binman/etype/blob_ext_list.py 32 0 100%
tools/binman/etype/blob_named_by_arg.py 9 0 100%
tools/binman/etype/blob_phase.py 22 0 100%
tools/binman/etype/cbfs.py 101 0 100%
tools/binman/etype/collection.py 30 0 100%
tools/binman/etype/cros_ec_rw.py 5 0 100%
tools/binman/etype/efi_capsule.py 59 0 100%
tools/binman/etype/efi_empty_capsule.py 33 0 100%
tools/binman/etype/encrypted.py 34 0 100%
tools/binman/etype/fdtmap.py 62 0 100%
tools/binman/etype/files.py 35 0 100%
tools/binman/etype/fill.py 13 0 100%
tools/binman/etype/fit.py 311 0 100%
tools/binman/etype/fmap.py 37 0 100%
tools/binman/etype/gbb.py 37 0 100%
tools/binman/etype/image_header.py 53 0 100%
tools/binman/etype/intel_cmc.py 4 0 100%
tools/binman/etype/intel_descriptor.py 39 0 100%
tools/binman/etype/intel_fit.py 12 0 100%
tools/binman/etype/intel_fit_ptr.py 17 0 100%
tools/binman/etype/intel_fsp.py 4 0 100%
tools/binman/etype/intel_fsp_m.py 4 0 100%
tools/binman/etype/intel_fsp_s.py 4 0 100%
tools/binman/etype/intel_fsp_t.py 4 0 100%
tools/binman/etype/intel_ifwi.py 67 0 100%
tools/binman/etype/intel_me.py 4 0 100%
tools/binman/etype/intel_mrc.py 6 0 100%
tools/binman/etype/intel_refcode.py 6 0 100%
tools/binman/etype/intel_vbt.py 4 0 100%
tools/binman/etype/intel_vga.py 4 0 100%
tools/binman/etype/mkimage.py 84 0 100%
tools/binman/etype/null.py 9 0 100%
tools/binman/etype/nxp_imx8mcst.py 78 59 24%
tools/binman/etype/nxp_imx8mimage.py 38 6 84%
tools/binman/etype/opensbi.py 5 0 100%
tools/binman/etype/powerpc_mpc85xx_bootpg_resetvec.py 6 0 100%
tools/binman/etype/pre_load.py 76 0 100%
tools/binman/etype/rockchip_tpl.py 5 0 100%
tools/binman/etype/scp.py 5 0 100%
tools/binman/etype/section.py 418 0 100%
tools/binman/etype/tee_os.py 31 0 100%
tools/binman/etype/text.py 21 0 100%
tools/binman/etype/ti_board_config.py 139 0 100%
tools/binman/etype/ti_dm.py 5 0 100%
tools/binman/etype/ti_secure.py 65 0 100%
tools/binman/etype/ti_secure_rom.py 117 0 100%
tools/binman/etype/u_boot.py 7 0 100%
tools/binman/etype/u_boot_dtb.py 9 0 100%
tools/binman/etype/u_boot_dtb_with_ucode.py 51 0 100%
tools/binman/etype/u_boot_elf.py 19 0 100%
tools/binman/etype/u_boot_env.py 27 0 100%
tools/binman/etype/u_boot_expanded.py 4 0 100%
tools/binman/etype/u_boot_img.py 7 0 100%
tools/binman/etype/u_boot_nodtb.py 7 0 100%
tools/binman/etype/u_boot_spl.py 8 0 100%
tools/binman/etype/u_boot_spl_bss_pad.py 14 0 100%
tools/binman/etype/u_boot_spl_dtb.py 9 0 100%
tools/binman/etype/u_boot_spl_elf.py 8 0 100%
tools/binman/etype/u_boot_spl_expanded.py 12 0 100%
tools/binman/etype/u_boot_spl_nodtb.py 8 0 100%
tools/binman/etype/u_boot_spl_pubkey_dtb.py 32 0 100%
tools/binman/etype/u_boot_spl_with_ucode_ptr.py 8 0 100%
tools/binman/etype/u_boot_tpl.py 8 0 100%
tools/binman/etype/u_boot_tpl_bss_pad.py 14 0 100%
tools/binman/etype/u_boot_tpl_dtb.py 9 0 100%
tools/binman/etype/u_boot_tpl_dtb_with_ucode.py 8 0 100%
tools/binman/etype/u_boot_tpl_elf.py 8 0 100%
tools/binman/etype/u_boot_tpl_expanded.py 12 0 100%
tools/binman/etype/u_boot_tpl_nodtb.py 8 0 100%
tools/binman/etype/u_boot_tpl_with_ucode_ptr.py 12 0 100%
tools/binman/etype/u_boot_ucode.py 33 0 100%
tools/binman/etype/u_boot_vpl.py 8 0 100%
tools/binman/etype/u_boot_vpl_bss_pad.py 14 0 100%
tools/binman/etype/u_boot_vpl_dtb.py 9 0 100%
tools/binman/etype/u_boot_vpl_elf.py 8 0 100%
tools/binman/etype/u_boot_vpl_expanded.py 12 0 100%
tools/binman/etype/u_boot_vpl_nodtb.py 8 0 100%
tools/binman/etype/u_boot_with_ucode_ptr.py 42 0 100%
tools/binman/etype/vblock.py 38 0 100%
tools/binman/etype/x86_reset16.py 7 0 100%
tools/binman/etype/x86_reset16_spl.py 7 0 100%
tools/binman/etype/x86_reset16_tpl.py 7 0 100%
tools/binman/etype/x86_start16.py 7 0 100%
tools/binman/etype/x86_start16_spl.py 7 0 100%
tools/binman/etype/x86_start16_tpl.py 7 0 100%
tools/binman/etype/x509_cert.py 71 0 100%
tools/binman/etype/xilinx_bootgen.py 72 0 100%
tools/binman/fip_util.py 202 0 100%
tools/binman/fmap_util.py 49 0 100%
tools/binman/image.py 181 0 100%
tools/binman/state.py 201 0 100%
---------------------------------------------------------------------------
TOTAL 5954 69 99%
To get a report in 'htmlcov/index.html', type: python3-coverage html
Coverage error: 99%, but should be 100%
ValueError: Test coverage failure
Unfortunately the run failed. As it suggests, create a report:
.. code-block:: bash
$ python3-coverage html
Wrote HTML report to htmlcov/index.html
If you open that file in the browser, you can see which files are not reaching
100% and click on them. Here is ``nxp_imx8mimage.py``, for example:
.. code-block:: python
43 # Generate mkimage configuration file similar to imx8mimage.cfg
44 # and pass it to mkimage to generate SPL image for us here.
45 cfg_fname = tools.get_output_filename('nxp.imx8mimage.cfg.%s' % uniq)
46 with open(cfg_fname, 'w') as outf:
47 print('ROM_VERSION v%d' % self.rom_version, file=outf)
48 print('BOOT_FROM %s' % self.boot_from, file=outf)
49 print('LOADER %s %#x' % (input_fname, self.loader_address), file=outf)
50
51 output_fname = tools.get_output_filename(f'cfg-out.{uniq}')
52 args = ['-d', input_fname, '-n', cfg_fname, '-T', 'imx8mimage',
53 output_fname]
54 if self.mkimage.run_cmd(*args) is not None:
55 return tools.read_file(output_fname)
56 else:
57 # Bintool is missing; just use the input data as the output
58 x self.record_missing_bintool(self.mkimage)
59 x return data
60
61 def SetImagePos(self, image_pos):
62 # Customized SoC specific SetImagePos which skips the mkimage etype
63 # implementation and removes the 0x48 offset introduced there. That
64 # offset is only used for uImage/fitImage, which is not the case in
65 # here.
66 upto = 0x00
67 for entry in super().GetEntries().values():
68 x entry.SetOffsetSize(upto, None)
69
70 # Give up if any entries lack a size
71 x if entry.size is None:
72 x return
73 x upto += entry.size
74
75 Entry_section.SetImagePos(self, image_pos)
Most of the file is covered, but the lines marked with ``x`` indicate missing
coverage. The will show up red in your browser.
What is a test?
---------------
A test is a function in ``ftest.py`` which uses an image description in
``tools/binman/test`` to perform some operations and exercise the code. Some
tests are just a few lines; some are more complicated.
Here is a simple test:
.. code-block:: python
def testSimple(self):
"""Test a simple binman with a single file"""
data = self._DoReadFile('005_simple.dts')
self.assertEqual(U_BOOT_DATA, data)
This test tells Binman to build an image using the description. Then it checks
that the resulting image looks correct. The image description is:
.. code-block:: devicetree
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
u-boot {
};
};
};
As you will know from the Binman documentation, this says that there is
one image and it contains the U-Boot binary. So this test builds an image
consisting of a U-Boot binary, then checks that it does indeed have just a
U-Boot binary in it.
Test data
---------
Using real binaries (like ``u-boot.bin``) to test Binman would be quite tedious.
Every output file would be large and it would be hard to tell by looking at the
output (e.g. with a hex dump) if a particular entry contains ``u-boot.bin`` or
``u-boot-spl.bin`` or something else.
Binman gets around this by using simple placeholders. Here is the placeholder
for u-boot.bin:
.. code-block:: python
U_BOOT_DATA = b'1234'
This is just bytes. So the test above checks that the output image contains
these four bytes. This makes verification fast for Binman and very easy for
humans.
Even the devicetree is a placeholder:
.. code-block:: python
U_BOOT_DTB_DATA = b'udtb'
But for some tests you need to use the real devicetree. In that case you can
use ``_DoReadFileRealDtb()``. See ``testUpdateFdtAll()`` for an example of how
to check the devicetree updated by Binman.
Test structure
--------------
Each test is designed to test just one thing. Binman tests are named according
to what they are testing. Individually they don't do very much, but as a whole
they test every line of code in Binman.
So ``testSimple()`` is designed to check that Binman can build the
simplest-possible image that isn't completely empty.
Another type of test is one which checks error-handling, for example:
.. code-block:: python
def testFillNoSize(self):
"""Test for an fill entry type with no size"""
with self.assertRaises(ValueError) as e:
self._DoReadFile('070_fill_no_size.dts')
self.assertIn("'fill' entry is missing properties: size",
str(e.exception))
This test deliberately tries to provoke an error. The image description is:
.. code-block:: devicetree
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
size = <16>;
fill {
fill-byte = [ff];
};
};
};
You can see that there is no size for the 'fill' entry, so we would expect
Binman to complain. The test checks that it actually does. It also checks the
error message produced by Binman. Sometimes you need to add several tests, each
with their own broken image description, in order to check all the error cases.
Sometimes you need to capture the console output of Binman, to check it is
correct. You can to this with ``test_util.capture_sys_output()``, for example:
.. code-block:: python
with test_util.capture_sys_output() as (_, stderr):
self._DoTestFile('071_gbb.dts', force_missing_bintools='futility',
entry_args=entry_args)
err = stderr.getvalue()
self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
The test collects the output and checks it with a regular expression. If you
need to see the test output (e.g. to debug it), you will have to remove that
capture line.
How to add a new test
---------------------
This section explains the process of writing a new test. It uses an example to
help with this, but your code will be different.
Generally you are adding a test because you are adding a new entry type
('etype'). So start by creating the shortest and simplest image-description you
can, which contains the new etype. Put it in a numbered file in
``tool/binman/test`` so that it comes last. All the numbers are unique and there
are no gaps.
Example from ``tools/binman/test/339_nxp_imx8.dts``:
.. code-block:: devicetree
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
nxp-imx8mimage {
args; /* TODO: Needed by mkimage etype superclass */
nxp,boot-from = "sd";
nxp,rom-version = <1>;
nxp,loader-address = <0x10>;
};
};
};
Note that you should use tabs in the file, not spaces. You can see that this has
been cut down to the bare minimum, just enough to include the etype and the
arguments it needs. This is of course not a real image. It will not boot on
anything. But that's fine; we are just trying to test this one etype. Try not
to add any other sections and etypes unless they are absolutely essential for
your test to work. This helps others too: they don't need to understand the full
complexity of your etype just to read your test.
Then create your test by adding a new function at the end of ``ftest.py``:
.. code-block:: python
def testNxpImx8Image(self):
"""Test that binman can produce an iMX8 image"""
self._DoTestFile('339_nxp_imx8.dts')
This uses the test file that you created. It doesn't check anything, it just
runs the image description through binman.
Let's run it:
.. code-block:: bash
$ binman test testNxpImx8Image
======================== Running binman tests ========================
.
----------------------------------------------------------------------
Ran 1 test in 0.242s
OK
So the test passes. It doesn't really do a lot, but it does exercise the etype.
The next step is to update it to actually check the output:
.. code-block:: python
def testNxpImx8Image(self):
"""Test that binman can produce an iMX8 image"""
data = self._DoReadFile('339_nxp_imx8.dts')
print('data', len(data))
The ``_DoReadFile()`` function is documented in the code. It returns the image
contents as the first part of a tuple.
Running this we see:
.. code-block:: bash
data 2200
So it is producing a little over 8K of data. Your etype will be different, but
in any case you can add Python code to check that this data is actually correct,
based on your knowledge of your etype. Note that you should not be checking
whether the external tools (called 'bintools' in Binman) are actually working,
since presumably they have their own tests. You just need to check that the
image seems reasonable, e.g. is not empty, contains the expected sections, etc.
When your etype does use a bintool, it also needs tests, but generally it will
be tested by virtue of the etype test. This is because your etype must call the
bintool to create the image. Sometimes you might need to add a test for a
bintool error-condition, though.
Finishing code coverage
-----------------------
The objective is to have test-coverage for every line of code that you add to
Binman. So how can you tell? First, get a coverage report as described above.
Look through the output for any files which are not at 100%. Add more test cases
(image descriptions and new functions in ``ftest.py``) until you have covered
each line.
In the above example, here are some possible steps:
#. The first red bit is where the ``mkimage`` call returns None. This can be
traced to ``Bintoolmkimage.mkimage()`` which calls
``Bintool.run_cmd_result()`` and ``None`` means that ``mkimage`` is missing.
So the etype has code to handle that case, but it is never used. You can
look for other examples of ``self.mkimage`` returning ``None`` - e.g.
``Entry_mkimage.BuildSectionData()`` does this. The clue for finding this is
that the ``nxp-imx8mimage`` etype is based on ``Entry_mkimage``:
.. code-block:: python
class Entry_nxp_imx8mimage(Entry_mkimage):
It must be tested somewhere...in this case ``testMkimage()`` doesn't do it,
but ``testMkimageMissing()`` immediately below that does. So you can create a
similar test, e.g.:
.. code-block:: python
def testNxpImx8ImageMkimageMissing(self):
"""Test that binman can produce an iMX8 image"""
with test_util.capture_sys_output() as (_, stderr):
self._DoTestFile('339_nxp_imx8.dts',
force_missing_bintools='mkimage')
err = stderr.getvalue()
self.assertRegex(err, "Image 'image'.*missing bintools.*: mkimage")
Note that this uses exactly the same image description as the first test.
It just checks what happens when the tool is missing. Checking the coverage
again, you will see that the first red bit has gone:
.. code-block:: bash
$ binman test -T
$ python3-coverage html
#. The second red bit is for ``SetImagePos()``. You can see that it is iterating
through the sub-entries inside the ``nxp-imx8mimage`` entry. In the case of
the 339 file, there are no such entries, so this code inside the for() loop
isn't used:
.. code-block:: python
def SetImagePos(self, image_pos):
# Customized SoC specific SetImagePos which skips the mkimage etype
# implementation and removes the 0x48 offset introduced there. That
# offset is only used for uImage/fitImage, which is not the case in
# here.
upto = 0x00
for entry in super().GetEntries().values():
entry.SetOffsetSize(upto, None)
# Give up if any entries lack a size
if entry.size is None:
return
upto += entry.size
Entry_section.SetImagePos(self, image_pos)
The solution is to add an entry, e.g. in ``340_nxp_imx8_non_empty.dts``:
.. code-block:: devicetree
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
nxp-imx8mimage {
args; /* TODO: Needed by mkimage etype superclass */
nxp,boot-from = "sd";
nxp,rom-version = <1>;
nxp,loader-address = <0x10>;
u-boot {
};
};
};
};
Now write a little test to use it:
.. code-block:: python
def testNxpImx8ImageNonEmpty(self):
"""Test that binman can produce an iMX8 image with something in it"""
data = self._DoReadFile('340_nxp_imx8_non_empty.dts')
# check data here
With that, the second red bit goes away, because the for() loop is now used.
#. There is one more red bit left, the ``return`` in ``SetImagePos()``. The
above effort got the for() loop to be executed, but it doesn't cover the
``return``. It might have been copied from some other etype, e.g. the mkimage
one. See ``Entry_mkimage.SetImagePos()`` which contains the code:
.. code-block:: python
for entry in self.GetEntries().values():
entry.SetOffsetSize(upto, None)
# Give up if any entries lack a size
if entry.size is None:
return
upto += entry.size
But which test covers that code for mkimage? By figuring that out, you could
use a similar technique. One way to find out is to delete the two lines in
``Entry_mkimage`` which check for entry.size being None and returning, then
see what breaks with ``binman test``:
.. code-block:: bash
ERROR: binman.ftest.TestFunctional.testMkimageCollection (subunit.RemotedTestCase)
binman.ftest.TestFunctional.testMkimageCollection
----------------------------------------------------------------------
testtools.testresult.real._StringException: Traceback (most recent call last):
TypeError: unsupported operand type(s) for +=: 'int' and 'NoneType'
======================================================================
ERROR: binman.ftest.TestFunctional.testMkimageImage (subunit.RemotedTestCase)
binman.ftest.TestFunctional.testMkimageImage
----------------------------------------------------------------------
testtools.testresult.real._StringException: Traceback (most recent call last):
TypeError: unsupported operand type(s) for +=: 'int' and 'NoneType'
======================================================================
ERROR: binman.ftest.TestFunctional.testMkimageSpecial (subunit.RemotedTestCase)
binman.ftest.TestFunctional.testMkimageSpecial
----------------------------------------------------------------------
testtools.testresult.real._StringException: Traceback (most recent call last):
TypeError: unsupported operand type(s) for +=: 'int' and 'NoneType'
We can verify that you got the right test, by putting the lines back in and
getting coverage for just that test:
.. code-block:: bash
binman test -T testMkimageCollection
python3-coverage html
You will see a lot of red since we are seeing test coverage just for one
test, but if you look in ``mkimage.py`` at ``SetImagePos()`` you will see
that the ``return`` is covered (i.e. it is marked green).
Looking at the ``.dts`` files for each of these tests, none jumps out as
being relevant to our case. It seems that this code just isn't needed, so the
best solution is to delete those two lines from the function:
.. code-block:: python
def SetImagePos(self, image_pos):
# Customized SoC specific SetImagePos which skips the mkimage etype
# implementation and removes the 0x48 offset introduced there. That
# offset is only used for uImage/fitImage, which is not the case in
# here.
upto = 0x00
for entry in super().GetEntries().values():
entry.SetOffsetSize(upto, None)
upto += entry.size
Entry_section.SetImagePos(self, image_pos)
We should check the updated code on a real build, to make sure it really
isn't needed, of course.
Now, the test coverage is complete!
If we later discover a case where those lines are needed, we can add the
lines back, along with a test for this case.
Getting help
------------
If you are stuck and cannot work out how to add test coverage for your entry
type, ask on the U-Boot mailing list, cc ``Simon Glass <sjg@chromium.org>`` or
on irc ``sjg1``

View file

@ -94,7 +94,7 @@ them. Expo supports doing this with an enum, where every ID is listed in the
enum:: enum::
enum { enum {
ZERO, ID_PROMPT = EXPOID_BASE_ID,
ID_PROMPT, ID_PROMPT,
@ -130,6 +130,11 @@ that means that something is wrong with your syntax, or perhaps you have an ID
in the `.dts` file that is not mentioned in your enum. Check both files and try in the `.dts` file that is not mentioned in your enum. Check both files and try
again. again.
Note that the first ID in your file must be no less that `EXPOID_BASE_ID` since
IDs before that are reserved. The `expo.py` tool automatically obtains this
value from the `expo.h` header file, but you must set the first ID to this
enum value.
Use the command interface Use the command interface
------------------------- -------------------------

View file

@ -88,8 +88,13 @@ or even the IDs of objects. Programmatic creation of many items in a loop can be
handled by allocating space in the enum for a maximum number of items, then handled by allocating space in the enum for a maximum number of items, then
adding the loop count to the enum values to obtain unique IDs. adding the loop count to the enum values to obtain unique IDs.
Where dynamic IDs are need, use expo_set_dynamic_start() to set the start value, Some standard IDs are reserved for certain purposes. These are defined by
so that they are allocated above the starting (enum) IDs. `enum expo_id_t` and start at 1. `EXPOID_BASE_ID` defines the first ID which
can be used for an expo.
An ID of 0 is invalid. If this is specified in an expo call then a valid
'dynamic IDs is allocated. Use expo_set_dynamic_start() to set the start
value, so that they are allocated above the starting (enum) IDs.
All text strings are stored in a structure attached to the expo, referenced by All text strings are stored in a structure attached to the expo, referenced by
a text ID. This makes it easier at some point to implement multiple languages or a text ID. This makes it easier at some point to implement multiple languages or
@ -176,6 +181,10 @@ menu-inset
menuitem-gap-y menuitem-gap-y
Number of pixels between menu items Number of pixels between menu items
menu-title-margin-x
Number of pixels between right side of menu title to the left size of the
menu labels
Pop-up mode Pop-up mode
----------- -----------
@ -352,6 +361,13 @@ item-id
Specifies the ID for each menu item. These are used for checking which item Specifies the ID for each menu item. These are used for checking which item
has been selected. has been selected.
item-value
type: u32 list, optional
Specifies the value for each menu item. These are used for saving and
loading. If this is omitted the value is its position in the menu (0..n-1).
Valid values are positive and negative integers INT_MIN...(INT_MAX - 1).
item-label / item-label-id item-label / item-label-id
type: string list / u32 list, required type: string list / u32 list, required
@ -413,8 +429,7 @@ strings are provided inline in the nodes where they are used.
/* this comment is parsed by the expo.py tool to insert the values below /* this comment is parsed by the expo.py tool to insert the values below
enum { enum {
ZERO, ID_PROMPT = EXPOID_BASE_ID,
ID_PROMPT,
ID_SCENE1, ID_SCENE1,
ID_SCENE1_TITLE, ID_SCENE1_TITLE,
@ -466,6 +481,9 @@ strings are provided inline in the nodes where they are used.
/* IDs for the menu items */ /* IDs for the menu items */
item-id = <ID_CPU_SPEED_1 ID_CPU_SPEED_2 item-id = <ID_CPU_SPEED_1 ID_CPU_SPEED_2
ID_CPU_SPEED_3>; ID_CPU_SPEED_3>;
/* values for the menu items */
item-value = <(-1) 3 6>;
}; };
power-loss { power-loss {

View file

@ -83,6 +83,7 @@ Testing
py_testing py_testing
tests_writing tests_writing
tests_sandbox tests_sandbox
binman_tests
Refactoring Refactoring
----------- -----------

View file

@ -107,8 +107,10 @@ That results in::
/ { / {
cedit-values { cedit-values {
cpu-speed = <0x00000006>; cpu-speed = <0x00000006>;
cpu-speed-value = <0x00000003>;
cpu-speed-str = "2 GHz"; cpu-speed-str = "2 GHz";
power-loss = <0x0000000a>; power-loss = <0x0000000a>;
power-loss-value = <0x00000000>;
power-loss-str = "Always Off"; power-loss-str = "Always Off";
}; };
} }
@ -118,16 +120,23 @@ That results in::
This shows settings being stored in the environment:: This shows settings being stored in the environment::
=> cedit write_env -v => cedit write_env -v
c.cpu-speed=7 c.cpu-speed=11
c.cpu-speed-str=2.5 GHz c.cpu-speed-str=2.5 GHz
c.power-loss=12 c.cpu-speed-value=3
c.power-loss-str=Memory c.power-loss=14
c.power-loss-str=Always Off
c.power-loss-value=0
c.machine-name=my-machine
c.cpu-speed=11
c.power-loss=14
c.machine-name=my-machine
=> print => print
... ...
c.cpu-speed=6 c.cpu-speed=6
c.cpu-speed-str=2 GHz c.cpu-speed-str=2 GHz
c.power-loss=10 c.power-loss=10
c.power-loss-str=Always Off c.power-loss-str=Always Off
c.machine-name=my-machine
... ...
=> cedit read_env -v => cedit read_env -v

68
doc/usage/cmd/cpuid.rst Normal file
View file

@ -0,0 +1,68 @@
.. SPDX-License-Identifier: GPL-2.0+
.. index::
single: cpuid (command)
cpuid command
=============
Synopsis
--------
::
cpuid <op>
Description
-----------
The cpuid command requests CPU-identification information on x86 CPUs. The
operation <op> selects what information is returned. Up to four 32-bit registers
can be update (eax-edx) depending on the operation.
Configuration
-------------
The cpuid command is only available on x86.
Return value
------------
The return value $? is 0 (true).
Example
-------
::
=> cpuid 1
eax 00060fb1
ebx 00040800
ecx 80002001
edx 178bfbfd
This shows checking for 64-bit 'long' mode::
=> cpuid 80000000
eax 8000000a
ebx 68747541
ecx 444d4163
edx 69746e65
=> cpuid 80000001
eax 00060fb1
ebx 00000000
ecx 00000007
edx 2193fbfd # Bit 29 is set in edx, so long mode is available
On a 32-bit-only CPU::
=> cpuid 80000000
eax 80000004
ebx 756e6547
ecx 6c65746e
edx 49656e69
=> cpuid 80000001
eax 00000663
ebx 00000000
ecx 00000000
edx 00000000 # Bit 29 is not set in edx, so long mode is not available

View file

@ -13,7 +13,7 @@ Synopsis
font list font list
font select <name> [<size>] font select <name> [<size>]
font size <size> font size [<size>]
Description Description
----------- -----------
@ -34,7 +34,7 @@ This selects a new font and optionally changes the size.
font size font size
~~~~~~~~~ ~~~~~~~~~
This changes the font size only. This changes the font size only. With no argument it shows the current size.
Examples Examples
-------- --------
@ -44,6 +44,8 @@ Examples
=> font list => font list
nimbus_sans_l_regular nimbus_sans_l_regular
cantoraone_regular cantoraone_regular
=> font size
30
=> font size 40 => font size 40
=> font select cantoraone_regular 20 => font select cantoraone_regular 20
=> =>

61
doc/usage/cmd/msr.rst Normal file
View file

@ -0,0 +1,61 @@
.. SPDX-License-Identifier: GPL-2.0+
.. index::
single: msr (command)
msr command
===========
Synopsis
--------
::
msr read <op>
msr write <op> <hi> <lo>
Description
-----------
The msr command reads and writes machine-status registers (MSRs) on x86 CPUs.
The information is a 64-bit value split into two parts, <hi> for the top 32
bits and <lo> for the bottom 32 bits.
The operation <op> selects what information is read or written.
msr read
~~~~~~~~
This reads an MSR and displays the value obtained.
msr write
~~~~~~~~~
This writes a value to an MSR.
Configuration
-------------
The msr command is only available on x86.
Return value
------------
The return value $? is 0 (true).
Example
-------
This shows reading msr 0x194 which is MSR_FLEX_RATIO on Intel CPUs::
=> msr read 194
00000000 00011200 # Bits 16 (flex ratio enable) and 20 (lock) are set
This shows adjusting the energy-performance bias on an Intel CPU::
=> msr read 1b0
00000000 00000006 # 6 means 'normal'
=> msr write 1b0 0 f # change to power-save
=> msr read 1b0
00000000 0000000f

View file

@ -52,6 +52,7 @@ Shell commands
cmd/conitrace cmd/conitrace
cmd/cp cmd/cp
cmd/cpu cmd/cpu
cmd/cpuid
cmd/cyclic cmd/cyclic
cmd/dm cmd/dm
cmd/ebtupdate cmd/ebtupdate
@ -86,6 +87,7 @@ Shell commands
cmd/mbr cmd/mbr
cmd/md cmd/md
cmd/mmc cmd/mmc
cmd/msr
cmd/mtest cmd/mtest
cmd/mtrr cmd/mtrr
cmd/panic cmd/panic

View file

@ -83,7 +83,7 @@ static unsigned long cpu_mhz_from_cpuid(void)
if (cpuid_eax(0) < 0x16) if (cpuid_eax(0) < 0x16)
return 0; return 0;
return cpuid_eax(0x16); return cpuid_eax(0x15);
} }
/* /*
@ -299,10 +299,19 @@ static unsigned long __maybe_unused quick_pit_calibrate(void)
if (!pit_expect_msb(0xff-i, &delta, &d2)) if (!pit_expect_msb(0xff-i, &delta, &d2))
break; break;
delta -= tsc;
/*
* Extrapolate the error and fail fast if the error will
* never be below 500 ppm.
*/
if (i == 1 &&
d1 + d2 >= (delta * MAX_QUICK_PIT_ITERATIONS) >> 11)
return 0;
/* /*
* Iterate until the error is less than 500 ppm * Iterate until the error is less than 500 ppm
*/ */
delta -= tsc;
if (d1+d2 >= delta >> 11) if (d1+d2 >= delta >> 11)
continue; continue;
@ -403,6 +412,10 @@ static void tsc_timer_ensure_setup(bool early)
if (!gd->arch.clock_rate) { if (!gd->arch.clock_rate) {
unsigned long fast_calibrate; unsigned long fast_calibrate;
/* deal with this being called before x86_cpu_init_f() */
if (!gd->arch.x86_vendor)
x86_get_identity_for_timer();
/** /**
* There is no obvious way to obtain this information from EFI * There is no obvious way to obtain this information from EFI
* boot services. This value was measured on a Framework Laptop * boot services. This value was measured on a Framework Laptop
@ -438,6 +451,7 @@ static void tsc_timer_ensure_setup(bool early)
return; return;
done: done:
fast_calibrate = min(fast_calibrate, 4000UL);
if (!gd->arch.clock_rate) if (!gd->arch.clock_rate)
gd->arch.clock_rate = fast_calibrate * 1000000; gd->arch.clock_rate = fast_calibrate * 1000000;
} }

View file

@ -94,7 +94,9 @@ static void vidconsole_newline(struct udevice *dev)
priv->ycur += priv->y_charsize; priv->ycur += priv->y_charsize;
/* Check if we need to scroll the terminal */ /* Check if we need to scroll the terminal */
if ((priv->ycur + priv->y_charsize) / priv->y_charsize > priv->rows) { if (vid_priv->rot % 2 ?
priv->ycur + priv->x_charsize > vid_priv->xsize :
priv->ycur + priv->y_charsize > vid_priv->ysize) {
vidconsole_move_rows(dev, 0, rows, priv->rows - rows); vidconsole_move_rows(dev, 0, rows, priv->rows - rows);
for (i = 0; i < rows; i++) for (i = 0; i < rows; i++)
vidconsole_set_row(dev, priv->rows - i - 1, vidconsole_set_row(dev, priv->rows - i - 1,

View file

@ -294,6 +294,9 @@ static const struct vid_rgb colours[VID_COLOUR_COUNT] = {
{ 0xff, 0x00, 0xff }, /* bright magenta */ { 0xff, 0x00, 0xff }, /* bright magenta */
{ 0x00, 0xff, 0xff }, /* bright cyan */ { 0x00, 0xff, 0xff }, /* bright cyan */
{ 0xff, 0xff, 0xff }, /* white */ { 0xff, 0xff, 0xff }, /* white */
/* an extra one for menus */
{ 0x40, 0x40, 0x40 }, /* dark gray */
}; };
u32 video_index_to_colour(struct video_priv *priv, enum colour_idx idx) u32 video_index_to_colour(struct video_priv *priv, enum colour_idx idx)

View file

@ -18,30 +18,82 @@ struct virtio_blk_priv {
struct virtqueue *vq; struct virtqueue *vq;
}; };
static const u32 feature[] = {
VIRTIO_BLK_F_WRITE_ZEROES
};
static void virtio_blk_init_header_sg(struct udevice *dev, u64 sector, u32 type,
struct virtio_blk_outhdr *out_hdr, struct virtio_sg *sg)
{
const bool sector_is_needed = type == VIRTIO_BLK_T_IN ||
type == VIRTIO_BLK_T_OUT;
out_hdr->type = cpu_to_virtio32(dev, type);
out_hdr->sector = cpu_to_virtio64(dev, sector_is_needed ? sector : 0);
sg->addr = out_hdr;
sg->length = sizeof(*out_hdr);
}
static void virtio_blk_init_write_zeroes_sg(struct udevice *dev, u64 sector, lbaint_t blkcnt,
struct virtio_blk_discard_write_zeroes *wz,
struct virtio_sg *sg)
{
wz->sector = cpu_to_virtio64(dev, sector);
wz->num_sectors = cpu_to_virtio32(dev, blkcnt);
wz->flags = cpu_to_virtio32(dev, 0);
sg->addr = wz;
sg->length = sizeof(*wz);
}
static void virtio_blk_init_status_sg(u8 *status, struct virtio_sg *sg)
{
sg->addr = status;
sg->length = sizeof(*status);
}
static void virtio_blk_init_data_sg(void *buffer, lbaint_t blkcnt, struct virtio_sg *sg)
{
sg->addr = buffer;
sg->length = blkcnt * 512;
}
static ulong virtio_blk_do_req(struct udevice *dev, u64 sector, static ulong virtio_blk_do_req(struct udevice *dev, u64 sector,
lbaint_t blkcnt, void *buffer, u32 type) lbaint_t blkcnt, void *buffer, u32 type)
{ {
struct virtio_blk_priv *priv = dev_get_priv(dev); struct virtio_blk_priv *priv = dev_get_priv(dev);
struct virtio_blk_outhdr out_hdr;
struct virtio_blk_discard_write_zeroes wz_hdr;
unsigned int num_out = 0, num_in = 0; unsigned int num_out = 0, num_in = 0;
struct virtio_sg hdr_sg, wz_sg, data_sg, status_sg;
struct virtio_sg *sgs[3]; struct virtio_sg *sgs[3];
u8 status; u8 status;
int ret; int ret;
struct virtio_blk_outhdr out_hdr = { virtio_blk_init_header_sg(dev, sector, type, &out_hdr, &hdr_sg);
.type = cpu_to_virtio32(dev, type),
.sector = cpu_to_virtio64(dev, sector),
};
struct virtio_sg hdr_sg = { &out_hdr, sizeof(out_hdr) };
struct virtio_sg data_sg = { buffer, blkcnt * 512 };
struct virtio_sg status_sg = { &status, sizeof(status) };
sgs[num_out++] = &hdr_sg; sgs[num_out++] = &hdr_sg;
switch (type) {
case VIRTIO_BLK_T_IN:
case VIRTIO_BLK_T_OUT:
virtio_blk_init_data_sg(buffer, blkcnt, &data_sg);
if (type & VIRTIO_BLK_T_OUT) if (type & VIRTIO_BLK_T_OUT)
sgs[num_out++] = &data_sg; sgs[num_out++] = &data_sg;
else else
sgs[num_out + num_in++] = &data_sg; sgs[num_out + num_in++] = &data_sg;
break;
case VIRTIO_BLK_T_WRITE_ZEROES:
virtio_blk_init_write_zeroes_sg(dev, sector, blkcnt, &wz_hdr, &wz_sg);
sgs[num_out++] = &wz_sg;
break;
default:
return -EINVAL;
}
virtio_blk_init_status_sg(&status, &status_sg);
sgs[num_out + num_in++] = &status_sg; sgs[num_out + num_in++] = &status_sg;
log_debug("dev=%s, active=%d, priv=%p, priv->vq=%p\n", dev->name, log_debug("dev=%s, active=%d, priv=%p, priv->vq=%p\n", dev->name,
device_active(dev), priv, priv->vq); device_active(dev), priv, priv->vq);
@ -75,6 +127,15 @@ static ulong virtio_blk_write(struct udevice *dev, lbaint_t start,
VIRTIO_BLK_T_OUT); VIRTIO_BLK_T_OUT);
} }
static ulong virtio_blk_erase(struct udevice *dev, lbaint_t start,
lbaint_t blkcnt)
{
if (!virtio_has_feature(dev, VIRTIO_BLK_F_WRITE_ZEROES))
return -EOPNOTSUPP;
return virtio_blk_do_req(dev, start, blkcnt, NULL, VIRTIO_BLK_T_WRITE_ZEROES);
}
static int virtio_blk_bind(struct udevice *dev) static int virtio_blk_bind(struct udevice *dev)
{ {
struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(dev->parent); struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(dev->parent);
@ -104,7 +165,8 @@ static int virtio_blk_bind(struct udevice *dev)
desc->bdev = dev; desc->bdev = dev;
/* Indicate what driver features we support */ /* Indicate what driver features we support */
virtio_driver_features_init(uc_priv, NULL, 0, NULL, 0); virtio_driver_features_init(uc_priv, feature, ARRAY_SIZE(feature),
NULL, 0);
return 0; return 0;
} }
@ -131,6 +193,7 @@ static int virtio_blk_probe(struct udevice *dev)
static const struct blk_ops virtio_blk_ops = { static const struct blk_ops virtio_blk_ops = {
.read = virtio_blk_read, .read = virtio_blk_read,
.write = virtio_blk_write, .write = virtio_blk_write,
.erase = virtio_blk_erase,
}; };
U_BOOT_DRIVER(virtio_blk) = { U_BOOT_DRIVER(virtio_blk) = {

View file

@ -17,6 +17,8 @@
#define VIRTIO_BLK_F_BLK_SIZE 6 /* Block size of disk is available */ #define VIRTIO_BLK_F_BLK_SIZE 6 /* Block size of disk is available */
#define VIRTIO_BLK_F_TOPOLOGY 10 /* Topology information is available */ #define VIRTIO_BLK_F_TOPOLOGY 10 /* Topology information is available */
#define VIRTIO_BLK_F_MQ 12 /* Support more than one vq */ #define VIRTIO_BLK_F_MQ 12 /* Support more than one vq */
#define VIRTIO_BLK_F_DISCARD 13 /* Discard is supported */
#define VIRTIO_BLK_F_WRITE_ZEROES 14 /* Write zeroes is supported */
/* Legacy feature bits */ /* Legacy feature bits */
#ifndef VIRTIO_BLK_NO_LEGACY #ifndef VIRTIO_BLK_NO_LEGACY
@ -65,6 +67,39 @@ struct __packed virtio_blk_config {
/* number of vqs, only available when VIRTIO_BLK_F_MQ is set */ /* number of vqs, only available when VIRTIO_BLK_F_MQ is set */
__u16 num_queues; __u16 num_queues;
/* the next 3 entries are guarded by VIRTIO_BLK_F_DISCARD */
/*
* The maximum discard sectors (in 512-byte sectors) for
* one segment.
*/
__u32 max_discard_sectors;
/*
* The maximum number of discard segments in a
* discard command.
*/
__u32 max_discard_seg;
/* Discard commands must be aligned to this number of sectors. */
__u32 discard_sector_alignment;
/* the next 3 entries are guarded by VIRTIO_BLK_F_WRITE_ZEROES */
/*
* The maximum number of write zeroes sectors (in 512-byte sectors) in
* one segment.
*/
__u32 max_write_zeroes_sectors;
/*
* The maximum number of segments in a write zeroes
* command.
*/
__u32 max_write_zeroes_seg;
/*
* Set if a VIRTIO_BLK_T_WRITE_ZEROES request may result in the
* deallocation of one or more of the sectors.
*/
__u8 write_zeroes_may_unmap;
__u8 unused1[3];
}; };
/* /*
@ -93,6 +128,9 @@ struct __packed virtio_blk_config {
/* Get device ID command */ /* Get device ID command */
#define VIRTIO_BLK_T_GET_ID 8 #define VIRTIO_BLK_T_GET_ID 8
/* Write zeroes command */
#define VIRTIO_BLK_T_WRITE_ZEROES 13
#ifndef VIRTIO_BLK_NO_LEGACY #ifndef VIRTIO_BLK_NO_LEGACY
/* Barrier before this op */ /* Barrier before this op */
#define VIRTIO_BLK_T_BARRIER 0x80000000 #define VIRTIO_BLK_T_BARRIER 0x80000000
@ -112,6 +150,15 @@ struct virtio_blk_outhdr {
__virtio64 sector; __virtio64 sector;
}; };
struct virtio_blk_discard_write_zeroes {
/* discard/write zeroes start sector */
__virtio64 sector;
/* number of discard/write zeroes sectors */
__virtio32 num_sectors;
/* flags for this range */
__virtio32 flags;
};
#ifndef VIRTIO_BLK_NO_LEGACY #ifndef VIRTIO_BLK_NO_LEGACY
struct virtio_scsi_inhdr { struct virtio_scsi_inhdr {
__virtio32 errors; __virtio32 errors;

View file

@ -197,7 +197,6 @@ static inline void blkcache_free(void) {}
#endif #endif
#if CONFIG_IS_ENABLED(BLK)
struct udevice; struct udevice;
/* Operations on block devices */ /* Operations on block devices */
@ -278,6 +277,8 @@ struct blk_ops {
#endif /* CONFIG_BOUNCE_BUFFER */ #endif /* CONFIG_BOUNCE_BUFFER */
}; };
#if CONFIG_IS_ENABLED(BLK)
/* /*
* These functions should take struct udevice instead of struct blk_desc, * These functions should take struct udevice instead of struct blk_desc,
* but this is convenient for migration to driver model. Add a 'd' prefix * but this is convenient for migration to driver model. Add a 'd' prefix
@ -291,6 +292,8 @@ unsigned long blk_dwrite(struct blk_desc *block_dev, lbaint_t start,
unsigned long blk_derase(struct blk_desc *block_dev, lbaint_t start, unsigned long blk_derase(struct blk_desc *block_dev, lbaint_t start,
lbaint_t blkcnt); lbaint_t blkcnt);
#endif /* BLK */
/** /**
* blk_read() - Read from a block device * blk_read() - Read from a block device
* *
@ -528,8 +531,10 @@ struct blk_desc *blk_get_by_device(struct udevice *dev);
*/ */
int blk_get_desc(enum uclass_id uclass_id, int devnum, struct blk_desc **descp); int blk_get_desc(enum uclass_id uclass_id, int devnum, struct blk_desc **descp);
#else #if !CONFIG_IS_ENABLED(BLK)
#include <errno.h> #include <errno.h>
/* /*
* These functions should take struct udevice instead of struct blk_desc, * These functions should take struct udevice instead of struct blk_desc,
* but this is convenient for migration to driver model. Add a 'd' prefix * but this is convenient for migration to driver model. Add a 'd' prefix

View file

@ -14,6 +14,7 @@
struct abuf; struct abuf;
struct expo; struct expo;
struct scene; struct scene;
struct udevice;
struct video_priv; struct video_priv;
struct udevice; struct udevice;

View file

@ -15,6 +15,26 @@ struct udevice;
#include <cli.h> #include <cli.h>
/**
* enum expo_id_t - standard expo IDs
*
* These are assumed to be in use at all times. Expos should use IDs starting
* from EXPOID_BASE_ID,
*
* @EXPOID_NONE: Not used, invalid ID 0
* @EXPOID_SAVE: User has requested that the expo data be saved
* @EXPOID_DISCARD: User has requested that the expo data be discarded
* @EXPOID_BASE_ID: First ID which can be used for expo objects
*/
enum expo_id_t {
EXPOID_NONE,
EXPOID_SAVE,
EXPOID_DISCARD,
EXPOID_BASE_ID = 5,
};
/** /**
* enum expoact_type - types of actions reported by the expo * enum expoact_type - types of actions reported by the expo
* *
@ -59,11 +79,14 @@ struct expo_action {
* @font_size: Default font size for all text * @font_size: Default font size for all text
* @menu_inset: Inset width (on each side and top/bottom) for menu items * @menu_inset: Inset width (on each side and top/bottom) for menu items
* @menuitem_gap_y: Gap between menu items in pixels * @menuitem_gap_y: Gap between menu items in pixels
* @menu_title_margin_x: Gap between right side of menu title and left size of
* menu label
*/ */
struct expo_theme { struct expo_theme {
u32 font_size; u32 font_size;
u32 menu_inset; u32 menu_inset;
u32 menuitem_gap_y; u32 menuitem_gap_y;
u32 menu_title_margin_x;
}; };
/** /**
@ -307,6 +330,7 @@ enum scene_menuitem_flags_t {
* @desc_id: ID of text object to use as the description text * @desc_id: ID of text object to use as the description text
* @preview_id: ID of the preview object, or 0 if none * @preview_id: ID of the preview object, or 0 if none
* @flags: Flags for this item * @flags: Flags for this item
* @value: Value for this item, or INT_MAX to use sequence
* @sibling: Node to link this item to its siblings * @sibling: Node to link this item to its siblings
*/ */
struct scene_menitem { struct scene_menitem {
@ -317,6 +341,7 @@ struct scene_menitem {
uint desc_id; uint desc_id;
uint preview_id; uint preview_id;
uint flags; uint flags;
int value;
struct list_head sibling; struct list_head sibling;
}; };
@ -341,6 +366,15 @@ struct scene_obj_textline {
uint pos; uint pos;
}; };
/**
* struct expo_arrange_info - Information used when arranging a scene
*
* @label_width: Maximum width of labels in scene
*/
struct expo_arrange_info {
int label_width;
};
/** /**
* expo_new() - create a new expo * expo_new() - create a new expo
* *
@ -506,15 +540,6 @@ void scene_set_highlight_id(struct scene *scn, uint id);
*/ */
int scene_set_open(struct scene *scn, uint id, bool open); int scene_set_open(struct scene *scn, uint id, bool open);
/**
* scene_title_set() - set the scene title
*
* @scn: Scene to update
* @title_id: Title ID to set
* Returns: 0 if OK
*/
int scene_title_set(struct scene *scn, uint title_id);
/** /**
* scene_obj_count() - Count the number of objects in a scene * scene_obj_count() - Count the number of objects in a scene
* *

View file

@ -9,24 +9,24 @@
#ifndef __cedit_test_h #ifndef __cedit_test_h
#define __cedit_test_h #define __cedit_test_h
#define ID_PROMPT 1 #define ID_PROMPT 5
#define ID_SCENE1 2 #define ID_SCENE1 6
#define ID_SCENE1_TITLE 3 #define ID_SCENE1_TITLE 7
#define ID_CPU_SPEED 4 #define ID_CPU_SPEED 8
#define ID_CPU_SPEED_TITLE 5 #define ID_CPU_SPEED_TITLE 9
#define ID_CPU_SPEED_1 6 #define ID_CPU_SPEED_1 10
#define ID_CPU_SPEED_2 7 #define ID_CPU_SPEED_2 11
#define ID_CPU_SPEED_3 8 #define ID_CPU_SPEED_3 12
#define ID_POWER_LOSS 9 #define ID_POWER_LOSS 13
#define ID_AC_OFF 10 #define ID_AC_OFF 14
#define ID_AC_ON 11 #define ID_AC_ON 15
#define ID_AC_MEMORY 12 #define ID_AC_MEMORY 16
#define ID_MACHINE_NAME 13 #define ID_MACHINE_NAME 17
#define ID_MACHINE_NAME_EDIT 14 #define ID_MACHINE_NAME_EDIT 18
#define ID_DYNAMIC_START 15 #define ID_DYNAMIC_START 19
#endif #endif

View file

@ -78,7 +78,8 @@ enum video_format {
* *
* @xsize: Number of pixel columns (e.g. 1366) * @xsize: Number of pixel columns (e.g. 1366)
* @ysize: Number of pixels rows (e.g.. 768) * @ysize: Number of pixels rows (e.g.. 768)
* @rot: Display rotation (0=none, 1=90 degrees clockwise, etc.) * @rot: Display rotation (0=none, 1=90 degrees clockwise, etc.). THis
* does not affect @xsize and @ysize
* @bpix: Encoded bits per pixel (enum video_log2_bpp) * @bpix: Encoded bits per pixel (enum video_log2_bpp)
* @format: Pixel format (enum video_format) * @format: Pixel format (enum video_format)
* @vidconsole_drv_name: Driver to use for the text console, NULL to * @vidconsole_drv_name: Driver to use for the text console, NULL to
@ -181,6 +182,7 @@ enum colour_idx {
VID_LIGHT_MAGENTA, VID_LIGHT_MAGENTA,
VID_LIGHT_CYAN, VID_LIGHT_CYAN,
VID_WHITE, VID_WHITE,
VID_DARK_GREY,
VID_COLOUR_COUNT VID_COLOUR_COUNT
}; };

View file

@ -27,6 +27,14 @@ enum {
* Drivers must set up @rows, @cols, @x_charsize, @y_charsize in their probe() * Drivers must set up @rows, @cols, @x_charsize, @y_charsize in their probe()
* method. Drivers may set up @xstart_frac if desired. * method. Drivers may set up @xstart_frac if desired.
* *
* Note that these values relate to the rotated console, so that an 80x25
* console which is rotated 90 degrees will have rows=80 and cols=25
*
* The xcur_frac and ycur values refer to the unrotated coordinates, that is
* xcur_frac always advances with each character, even if its limit might be
* vid_priv->ysize instead of vid_priv->xsize if the console is rotated 90 or
* 270 degrees.
*
* @sdev: stdio device, acting as an output sink * @sdev: stdio device, acting as an output sink
* @xcur_frac: Current X position, in fractional units (VID_TO_POS(x)) * @xcur_frac: Current X position, in fractional units (VID_TO_POS(x))
* @ycur: Current Y position in pixels (0=top) * @ycur: Current Y position in pixels (0=top)

View file

@ -31,9 +31,11 @@ static int cedit_base(struct unit_test_state *uts)
* ^N Move down to second item * ^N Move down to second item
* ^M Select item * ^M Select item
* \e Quit * \e Quit
*
* cedit_run() returns -EACCESS so this command returns CMD_RET_FAILURE
*/ */
console_in_puts("\x0e\x0d\x0e\x0d\e"); console_in_puts("\x0e\x0d\x0e\x0d\e");
ut_assertok(run_command("cedit run", 0)); ut_asserteq(1, run_command("cedit run", 0));
exp = cur_exp; exp = cur_exp;
scn = expo_lookup_scene_id(exp, exp->scene_id); scn = expo_lookup_scene_id(exp, exp->scene_id);
@ -94,14 +96,16 @@ static int cedit_fdt(struct unit_test_state *uts)
ut_asserteq(ID_CPU_SPEED_2, ut_asserteq(ID_CPU_SPEED_2,
ofnode_read_u32_default(node, "cpu-speed", 0)); ofnode_read_u32_default(node, "cpu-speed", 0));
ut_asserteq(3,
ofnode_read_u32_default(node, "cpu-speed-value", 0));
ut_asserteq_str("2.5 GHz", ofnode_read_string(node, "cpu-speed-str")); ut_asserteq_str("2.5 GHz", ofnode_read_string(node, "cpu-speed-str"));
ut_asserteq_str("my-machine", ofnode_read_string(node, "machine-name")); ut_asserteq_str("my-machine", ofnode_read_string(node, "machine-name"));
/* There should only be 5 properties */ /* There should only be 7 properties */
for (i = 0, ofnode_first_property(node, &prop); ofprop_valid(&prop); for (i = 0, ofnode_first_property(node, &prop); ofprop_valid(&prop);
i++, ofnode_next_property(&prop)) i++, ofnode_next_property(&prop))
; ;
ut_asserteq(5, i); ut_asserteq(7, i);
ut_assert_console_end(); ut_assert_console_end();
@ -147,14 +151,16 @@ static int cedit_env(struct unit_test_state *uts)
strcpy(str, "my-machine"); strcpy(str, "my-machine");
ut_assertok(run_command("cedit write_env -v", 0)); ut_assertok(run_command("cedit write_env -v", 0));
ut_assert_nextlinen("c.cpu-speed=7"); ut_assert_nextlinen("c.cpu-speed=11");
ut_assert_nextlinen("c.cpu-speed-str=2.5 GHz"); ut_assert_nextlinen("c.cpu-speed-str=2.5 GHz");
ut_assert_nextlinen("c.power-loss=10"); ut_assert_nextlinen("c.cpu-speed-value=3");
ut_assert_nextlinen("c.power-loss=14");
ut_assert_nextlinen("c.power-loss-str=Always Off"); ut_assert_nextlinen("c.power-loss-str=Always Off");
ut_assert_nextlinen("c.power-loss-value=0");
ut_assert_nextlinen("c.machine-name=my-machine"); ut_assert_nextlinen("c.machine-name=my-machine");
ut_assert_console_end(); ut_assert_console_end();
ut_asserteq(7, env_get_ulong("c.cpu-speed", 10, 0)); ut_asserteq(11, env_get_ulong("c.cpu-speed", 10, 0));
ut_asserteq_str("2.5 GHz", env_get("c.cpu-speed-str")); ut_asserteq_str("2.5 GHz", env_get("c.cpu-speed-str"));
ut_asserteq_str("my-machine", env_get("c.machine-name")); ut_asserteq_str("my-machine", env_get("c.machine-name"));
@ -163,8 +169,8 @@ static int cedit_env(struct unit_test_state *uts)
*str = '\0'; *str = '\0';
ut_assertok(run_command("cedit read_env -v", 0)); ut_assertok(run_command("cedit read_env -v", 0));
ut_assert_nextlinen("c.cpu-speed=7"); ut_assert_nextlinen("c.cpu-speed=11");
ut_assert_nextlinen("c.power-loss=10"); ut_assert_nextlinen("c.power-loss=14");
ut_assert_nextlinen("c.machine-name=my-machine"); ut_assert_nextlinen("c.machine-name=my-machine");
ut_assert_console_end(); ut_assert_console_end();

View file

@ -91,7 +91,7 @@ static int expo_base(struct unit_test_state *uts)
*name = '\0'; *name = '\0';
ut_assertnonnull(exp); ut_assertnonnull(exp);
ut_asserteq(0, exp->scene_id); ut_asserteq(0, exp->scene_id);
ut_asserteq(0, exp->next_id); ut_asserteq(EXPOID_BASE_ID, exp->next_id);
/* Make sure the name was allocated */ /* Make sure the name was allocated */
ut_assertnonnull(exp->name); ut_assertnonnull(exp->name);
@ -130,7 +130,7 @@ static int expo_scene(struct unit_test_state *uts)
ut_assertok(expo_new(EXPO_NAME, NULL, &exp)); ut_assertok(expo_new(EXPO_NAME, NULL, &exp));
scn = NULL; scn = NULL;
ut_asserteq(0, exp->next_id); ut_asserteq(EXPOID_BASE_ID, exp->next_id);
strcpy(name, SCENE_NAME1); strcpy(name, SCENE_NAME1);
id = scene_new(exp, name, SCENE1, &scn); id = scene_new(exp, name, SCENE1, &scn);
*name = '\0'; *name = '\0';
@ -151,7 +151,7 @@ static int expo_scene(struct unit_test_state *uts)
scn = NULL; scn = NULL;
id = scene_new(exp, SCENE_NAME2, 0, &scn); id = scene_new(exp, SCENE_NAME2, 0, &scn);
ut_assertnonnull(scn); ut_assertnonnull(scn);
ut_assertok(scene_title_set(scn, title_id)); scn->title_id = title_id;
ut_asserteq(STR_SCENE_TITLE + 1, id); ut_asserteq(STR_SCENE_TITLE + 1, id);
ut_asserteq(STR_SCENE_TITLE + 2, exp->next_id); ut_asserteq(STR_SCENE_TITLE + 2, exp->next_id);
ut_asserteq_ptr(exp, scn->expo); ut_asserteq_ptr(exp, scn->expo);
@ -167,6 +167,25 @@ static int expo_scene(struct unit_test_state *uts)
} }
BOOTSTD_TEST(expo_scene, UTF_DM | UTF_SCAN_FDT); BOOTSTD_TEST(expo_scene, UTF_DM | UTF_SCAN_FDT);
/* Check creating a scene with no ID */
static int expo_scene_no_id(struct unit_test_state *uts)
{
struct scene *scn;
struct expo *exp;
char name[100];
int id;
ut_assertok(expo_new(EXPO_NAME, NULL, &exp));
ut_asserteq(EXPOID_BASE_ID, exp->next_id);
strcpy(name, SCENE_NAME1);
id = scene_new(exp, SCENE_NAME1, 0, &scn);
ut_asserteq(EXPOID_BASE_ID, scn->id);
return 0;
}
BOOTSTD_TEST(expo_scene_no_id, UTF_DM | UTF_SCAN_FDT);
/* Check creating a scene with objects */ /* Check creating a scene with objects */
static int expo_object(struct unit_test_state *uts) static int expo_object(struct unit_test_state *uts)
{ {
@ -698,6 +717,7 @@ static int expo_test_build(struct unit_test_state *uts)
ut_asserteq(0, item->desc_id); ut_asserteq(0, item->desc_id);
ut_asserteq(0, item->preview_id); ut_asserteq(0, item->preview_id);
ut_asserteq(0, item->flags); ut_asserteq(0, item->flags);
ut_asserteq(0, item->value);
txt = scene_obj_find(scn, item->label_id, SCENEOBJT_NONE); txt = scene_obj_find(scn, item->label_id, SCENEOBJT_NONE);
ut_asserteq_str("2 GHz", expo_get_str(exp, txt->str_id)); ut_asserteq_str("2 GHz", expo_get_str(exp, txt->str_id));

View file

@ -4,8 +4,7 @@
*/ */
enum { enum {
ZERO, ID_PROMPT = EXPOID_BASE_ID,
ID_PROMPT,
ID_SCENE1, ID_SCENE1,
ID_SCENE1_TITLE, ID_SCENE1_TITLE,

View file

@ -39,8 +39,11 @@
item-id = <ID_CPU_SPEED_1 ID_CPU_SPEED_2 item-id = <ID_CPU_SPEED_1 ID_CPU_SPEED_2
ID_CPU_SPEED_3>; ID_CPU_SPEED_3>;
/* values for the menu items */
item-value = <0 3 6>;
start-bit = <0x400>; start-bit = <0x400>;
bit-length = <2>; bit-length = <3>;
}; };
power-loss { power-loss {

View file

@ -12,6 +12,7 @@ ifdef CONFIG_CONSOLE_RECORD
obj-$(CONFIG_CMD_PAUSE) += test_pause.o obj-$(CONFIG_CMD_PAUSE) += test_pause.o
endif endif
obj-y += exit.o mem.o obj-y += exit.o mem.o
obj-$(CONFIG_X86) += cpuid.o msr.o
obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o
obj-$(CONFIG_CMD_BDI) += bdinfo.o obj-$(CONFIG_CMD_BDI) += bdinfo.o
obj-$(CONFIG_CMD_FDT) += fdt.o obj-$(CONFIG_CMD_FDT) += fdt.o

22
test/cmd/cpuid.c Normal file
View file

@ -0,0 +1,22 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Tests for cpuid command
*
* Copyright 2024 Google LLC
* Written by Simon Glass <sjg@chromium.org>
*/
#include <test/cmd.h>
#include <test/ut.h>
static int cmd_test_cpuid(struct unit_test_state *uts)
{
ut_assertok(run_commandf("cpuid 1"));
ut_assert_nextline("eax 00060fb1");
ut_assert_nextline("ebx 00000800");
ut_assert_nextline("ecx 80002001");
ut_assert_nextline("edx 078bfbfd");
return 0;
}
CMD_TEST(cmd_test_cpuid, UTF_CONSOLE);

View file

@ -27,14 +27,20 @@ static int font_test_base(struct unit_test_state *uts)
ut_assertok(uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev)); ut_assertok(uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev));
ut_assertok(run_command("font list", 0)); ut_assertok(run_command("font list", 0));
if (IS_ENABLED(CONFIG_CONSOLE_TRUETYPE_NIMBUS))
ut_assert_nextline("nimbus_sans_l_regular"); ut_assert_nextline("nimbus_sans_l_regular");
if (IS_ENABLED(CONFIG_CONSOLE_TRUETYPE_ANKACODER))
ut_assert_nextline("ankacoder_c75_r");
if (IS_ENABLED(CONFIG_CONSOLE_TRUETYPE_CANTORAONE)) if (IS_ENABLED(CONFIG_CONSOLE_TRUETYPE_CANTORAONE))
ut_assert_nextline("cantoraone_regular"); ut_assert_nextline("cantoraone_regular");
ut_assert_console_end(); ut_assert_console_end();
ut_assertok(vidconsole_get_font_size(dev, &name, &size)); ut_assertok(vidconsole_get_font_size(dev, &name, &size));
if (IS_ENABLED(CONFIG_CONSOLE_TRUETYPE_ANKACODER))
ut_asserteq_str("ankacoder_c75_r", name);
else
ut_asserteq_str("nimbus_sans_l_regular", name); ut_asserteq_str("nimbus_sans_l_regular", name);
ut_asserteq(18, size); ut_asserteq(CONFIG_CONSOLE_TRUETYPE_SIZE, size);
if (!IS_ENABLED(CONFIG_CONSOLE_TRUETYPE_CANTORAONE)) if (!IS_ENABLED(CONFIG_CONSOLE_TRUETYPE_CANTORAONE))
return 0; return 0;
@ -58,10 +64,19 @@ static int font_test_base(struct unit_test_state *uts)
ut_assertok(vidconsole_get_font_size(dev, &name, &size)); ut_assertok(vidconsole_get_font_size(dev, &name, &size));
ut_asserteq_str("cantoraone_regular", name); ut_asserteq_str("cantoraone_regular", name);
ut_asserteq(40, size); ut_asserteq(40, size);
ut_assertok(ut_check_console_end(uts));
ut_assertok(run_command("font size", 0));
ut_assert_nextline("40");
ut_assertok(ut_check_console_end(uts));
ut_assertok(run_command("font size 30", 0)); ut_assertok(run_command("font size 30", 0));
ut_assert_console_end(); ut_assert_console_end();
ut_assertok(run_command("font size", 0));
ut_assert_nextline("30");
ut_assertok(ut_check_console_end(uts));
ut_assertok(vidconsole_get_font_size(dev, &name, &size)); ut_assertok(vidconsole_get_font_size(dev, &name, &size));
ut_asserteq_str("cantoraone_regular", name); ut_asserteq_str("cantoraone_regular", name);
ut_asserteq(30, size); ut_asserteq(30, size);

38
test/cmd/msr.c Normal file
View file

@ -0,0 +1,38 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Tests for msr command
*
* Copyright 2024 Google LLC
* Written by Simon Glass <sjg@chromium.org>
*/
#include <test/cmd.h>
#include <test/ut.h>
static int cmd_test_msr(struct unit_test_state *uts)
{
ut_assertok(run_commandf("msr read 200"));
ut_assert_nextline("00000000 ffe00006");
ut_assert_console_end();
/* change the first variable msr and see it reflected in the mtrr cmd */
ut_assertok(run_commandf("mtrr"));
ut_assert_nextline("CPU 65537:");
ut_assert_nextlinen("Reg");
ut_assert_nextlinen("0 Y Back 00000000ffe00000");
ut_assertok(console_record_reset_enable());
/* change the type from 6 to 5 */
ut_assertok(run_commandf("msr write 200 0 ffe00005"));
ut_assert_console_end();
/* Now it shows 'Protect' */
ut_assertok(run_commandf("mtrr"));
ut_assert_nextline("CPU 65537:");
ut_assert_nextlinen("Reg");
ut_assert_nextlinen("0 Y Protect 00000000ffe00000");
ut_assertok(console_record_reset_enable());
return 0;
}
CMD_TEST(cmd_test_msr, UTF_CONSOLE);

View file

@ -2099,12 +2099,15 @@ Code coverage
------------- -------------
Binman is a critical tool and is designed to be very testable. Entry Binman is a critical tool and is designed to be very testable. Entry
implementations target 100% test coverage. Run 'binman test -T' to check this. implementations target 100% test coverage. Run ``binman test -T`` to check this.
To enable Python test coverage on Debian-type distributions (e.g. Ubuntu):: To enable Python test coverage on Debian-type distributions (e.g. Ubuntu)::
$ sudo apt-get install python-coverage python3-coverage python-pytest $ sudo apt-get install python-coverage python3-coverage python-pytest
You can also check the coverage provided by a single test, e.g.::
binman test -T testSimple
Exit status Exit status
----------- -----------
@ -2191,6 +2194,11 @@ Use '-P 1' to disable this. It is automatically disabled when code coverage is
being used (-T) since they are incompatible. being used (-T) since they are incompatible.
Writing tests
-------------
See :doc:`../binman_tests`.
Debugging tests Debugging tests
--------------- ---------------

View file

@ -22,7 +22,7 @@ class Bintoolmkimage(bintool.Bintool):
# pylint: disable=R0913 # pylint: disable=R0913
def run(self, reset_timestamp=False, output_fname=None, external=False, def run(self, reset_timestamp=False, output_fname=None, external=False,
pad=None, align=None): pad=None, align=None, priv_keys_dir=None):
"""Run mkimage """Run mkimage
Args: Args:
@ -34,6 +34,7 @@ class Bintoolmkimage(bintool.Bintool):
other things to be easily added later, if required, such as other things to be easily added later, if required, such as
signatures signatures
align: Bytes to use for alignment of the FIT and its external data align: Bytes to use for alignment of the FIT and its external data
priv_keys_dir: Path to directory containing private keys
version: True to get the mkimage version version: True to get the mkimage version
""" """
args = [] args = []
@ -45,6 +46,8 @@ class Bintoolmkimage(bintool.Bintool):
args += ['-B', f'{align:x}'] args += ['-B', f'{align:x}']
if reset_timestamp: if reset_timestamp:
args.append('-t') args.append('-t')
if priv_keys_dir:
args += ['-k', f'{priv_keys_dir}']
if output_fname: if output_fname:
args += ['-F', output_fname] args += ['-F', output_fname]
return self.run_cmd(*args) return self.run_cmd(*args)

View file

@ -864,6 +864,13 @@ The top-level 'fit' node supports the following special properties:
fit,fdt-list-dir = "arch/arm/dts fit,fdt-list-dir = "arch/arm/dts
fit,sign
Enable signing FIT images via mkimage as described in
verified-boot.rst. If the property is found, the private keys path is
detected among binman include directories and passed to mkimage via
-k flag. All the keys required for signing FIT must be available at
time of signing and must be located in single include directory.
Substitutions Substitutions
~~~~~~~~~~~~~ ~~~~~~~~~~~~~

View file

@ -9,6 +9,7 @@ import glob
import os import os
import libfdt import libfdt
import os
from binman.entry import Entry, EntryArg from binman.entry import Entry, EntryArg
from binman.etype.section import Entry_section from binman.etype.section import Entry_section
@ -101,6 +102,14 @@ class Entry_fit(Entry_section):
In this case the input directories are ignored and all devicetree In this case the input directories are ignored and all devicetree
files must be in that directory. files must be in that directory.
fit,sign
Enable signing FIT images via mkimage as described in
verified-boot.rst. If the property is found, the private keys path
is detected among binman include directories and passed to mkimage
via -k flag. All the keys required for signing FIT must be
available at time of signing and must be located in single include
directory.
Substitutions Substitutions
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
@ -426,6 +435,7 @@ class Entry_fit(Entry_section):
self._remove_props = props.split() self._remove_props = props.split()
self.mkimage = None self.mkimage = None
self.fdtgrep = None self.fdtgrep = None
self._fit_sign = None
def ReadNode(self): def ReadNode(self):
super().ReadNode() super().ReadNode()
@ -508,6 +518,45 @@ class Entry_fit(Entry_section):
# are removed from self._entries later. # are removed from self._entries later.
self._priv_entries = dict(self._entries) self._priv_entries = dict(self._entries)
def _get_priv_keys_dir(self, data):
"""Detect private keys path among binman include directories
Args:
data: FIT image in binary format
Returns:
str: Single path containing all private keys found or None
Raises:
ValueError: Filename 'rsa2048.key' not found in input path
ValueError: Multiple key paths found
"""
def _find_keys_dir(node):
for subnode in node.subnodes:
if subnode.name.startswith('signature'):
if subnode.props.get('key-name-hint') is None:
continue
hint = subnode.props['key-name-hint'].value
name = tools.get_input_filename(f"{hint}.key")
path = os.path.dirname(name)
if path not in paths:
paths.append(path)
else:
_find_keys_dir(subnode)
return None
fdt = Fdt.FromData(data)
fdt.Scan()
paths = []
_find_keys_dir(fdt.GetRoot())
if len(paths) > 1:
self.Raise("multiple key paths found (%s)" % ",".join(paths))
return paths[0] if len(paths) else None
def BuildSectionData(self, required): def BuildSectionData(self, required):
"""Build FIT entry contents """Build FIT entry contents
@ -538,6 +587,8 @@ class Entry_fit(Entry_section):
align = self._fit_props.get('fit,align') align = self._fit_props.get('fit,align')
if align is not None: if align is not None:
args.update({'align': fdt_util.fdt32_to_cpu(align.value)}) args.update({'align': fdt_util.fdt32_to_cpu(align.value)})
if self._fit_props.get('fit,sign') is not None:
args.update({'priv_keys_dir': self._get_priv_keys_dir(data)})
if self.mkimage.run(reset_timestamp=True, output_fname=output_fname, if self.mkimage.run(reset_timestamp=True, output_fname=output_fname,
**args) is None: **args) is None:
if not self.GetAllowMissing(): if not self.GetAllowMissing():
@ -637,8 +688,8 @@ class Entry_fit(Entry_section):
""" """
val = fdt_util.GetStringList(node, 'fit,firmware') val = fdt_util.GetStringList(node, 'fit,firmware')
if val is None: if val is None:
return None, self._loadables return None, loadables
valid_entries = list(self._loadables) valid_entries = list(loadables)
for name, entry in self.GetEntries().items(): for name, entry in self.GetEntries().items():
missing = [] missing = []
entry.CheckMissing(missing) entry.CheckMissing(missing)
@ -653,7 +704,7 @@ class Entry_fit(Entry_section):
firmware = name firmware = name
elif name not in result: elif name not in result:
result.append(name) result.append(name)
for name in self._loadables: for name in loadables:
if name != firmware and name not in result: if name != firmware and name not in result:
result.append(name) result.append(name)
return firmware, result return firmware, result

View file

@ -7804,6 +7804,101 @@ fdt fdtmap Extract the devicetree blob from the fdtmap
"""Test that binman can produce an iMX8 image""" """Test that binman can produce an iMX8 image"""
self._DoTestFile('339_nxp_imx8.dts') self._DoTestFile('339_nxp_imx8.dts')
def testFitSignSimple(self):
"""Test that image with FIT and signature nodes can be signed"""
if not elf.ELF_TOOLS:
self.skipTest('Python elftools not available')
entry_args = {
'of-list': 'test-fdt1',
'default-dt': 'test-fdt1',
'atf-bl31-path': 'bl31.elf',
}
data = tools.read_file(self.TestFile("340_rsa2048.key"))
self._MakeInputFile("keys/rsa2048.key", data)
test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
keys_subdir = os.path.join(self._indir, "keys")
data = self._DoReadFileDtb(
'340_fit_signature.dts',
entry_args=entry_args,
extra_indirs=[test_subdir, keys_subdir])[0]
dtb = fdt.Fdt.FromData(data)
dtb.Scan()
conf = dtb.GetNode('/configurations/conf-uboot-1')
self.assertIsNotNone(conf)
signature = conf.FindNode('signature')
self.assertIsNotNone(signature)
self.assertIsNotNone(signature.props.get('value'))
images = dtb.GetNode('/images')
self.assertIsNotNone(images)
for subnode in images.subnodes:
signature = subnode.FindNode('signature')
self.assertIsNotNone(signature)
self.assertIsNotNone(signature.props.get('value'))
def testFitSignKeyNotFound(self):
"""Test that missing keys raise an error"""
if not elf.ELF_TOOLS:
self.skipTest('Python elftools not available')
entry_args = {
'of-list': 'test-fdt1',
'default-dt': 'test-fdt1',
'atf-bl31-path': 'bl31.elf',
}
test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
with self.assertRaises(ValueError) as e:
self._DoReadFileDtb(
'340_fit_signature.dts',
entry_args=entry_args,
extra_indirs=[test_subdir])[0]
self.assertIn(
'Filename \'rsa2048.key\' not found in input path',
str(e.exception))
def testFitSignMultipleKeyPaths(self):
"""Test that keys found in multiple paths raise an error"""
if not elf.ELF_TOOLS:
self.skipTest('Python elftools not available')
entry_args = {
'of-list': 'test-fdt1',
'default-dt': 'test-fdt1',
'atf-bl31-path': 'bl31.elf',
}
data = tools.read_file(self.TestFile("340_rsa2048.key"))
self._MakeInputFile("keys1/rsa2048.key", data)
data = tools.read_file(self.TestFile("340_rsa2048.key"))
self._MakeInputFile("keys2/conf-rsa2048.key", data)
test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
keys_subdir1 = os.path.join(self._indir, "keys1")
keys_subdir2 = os.path.join(self._indir, "keys2")
with self.assertRaises(ValueError) as e:
self._DoReadFileDtb(
'341_fit_signature.dts',
entry_args=entry_args,
extra_indirs=[test_subdir, keys_subdir1, keys_subdir2])[0]
self.assertIn(
'Node \'/binman/fit\': multiple key paths found',
str(e.exception))
def testFitSignNoSingatureNodes(self):
"""Test that fit,sign doens't raise error if no signature nodes found"""
if not elf.ELF_TOOLS:
self.skipTest('Python elftools not available')
entry_args = {
'of-list': 'test-fdt1',
'default-dt': 'test-fdt1',
'atf-bl31-path': 'bl31.elf',
}
test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
self._DoReadFileDtb(
'342_fit_signature.dts',
entry_args=entry_args,
extra_indirs=[test_subdir])[0]
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View file

@ -85,7 +85,7 @@ def RunTests(debug, verbosity, processes, test_preserve_dirs, args, toolpath):
return (0 if result.wasSuccessful() else 1) return (0 if result.wasSuccessful() else 1)
def RunTestCoverage(toolpath, build_dir): def RunTestCoverage(toolpath, build_dir, args):
"""Run the tests and check that we get 100% coverage""" """Run the tests and check that we get 100% coverage"""
glob_list = control.GetEntryModules(False) glob_list = control.GetEntryModules(False)
all_set = set([os.path.splitext(os.path.basename(item))[0] all_set = set([os.path.splitext(os.path.basename(item))[0]
@ -97,7 +97,7 @@ def RunTestCoverage(toolpath, build_dir):
test_util.run_test_coverage('tools/binman/binman', None, test_util.run_test_coverage('tools/binman/binman', None,
['*test*', '*main.py', 'tools/patman/*', 'tools/dtoc/*', ['*test*', '*main.py', 'tools/patman/*', 'tools/dtoc/*',
'tools/u_boot_pylib/*'], 'tools/u_boot_pylib/*'],
build_dir, all_set, extra_args or None) build_dir, all_set, extra_args or None, args=args)
def RunBinman(args): def RunBinman(args):
"""Main entry point to binman once arguments are parsed """Main entry point to binman once arguments are parsed
@ -117,7 +117,7 @@ def RunBinman(args):
if args.cmd == 'test': if args.cmd == 'test':
if args.test_coverage: if args.test_coverage:
RunTestCoverage(args.toolpath, args.build_dir) RunTestCoverage(args.toolpath, args.build_dir, args.tests)
else: else:
ret_code = RunTests(args.debug, args.verbosity, args.processes, ret_code = RunTests(args.debug, args.verbosity, args.processes,
args.test_preserve_dirs, args.tests, args.test_preserve_dirs, args.tests,

View file

@ -0,0 +1,98 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
fit {
description = "test desc";
#address-cells = <1>;
fit,fdt-list = "of-list";
fit,sign;
images {
u-boot {
description = "test u-boot";
type = "standalone";
arch = "arm64";
os = "u-boot";
compression = "none";
load = <0x00000000>;
entry = <0x00000000>;
u-boot-nodtb {
};
hash {
algo = "sha256";
};
signature {
algo = "sha256,rsa2048";
key-name-hint = "rsa2048";
};
};
@atf-SEQ {
fit,operation = "split-elf";
description = "test tf-a";
type = "firmware";
arch = "arm64";
os = "arm-trusted-firmware";
compression = "none";
fit,load;
fit,entry;
fit,data;
atf-bl31 {
};
hash {
algo = "sha256";
};
signature {
algo = "sha256,rsa2048";
key-name-hint = "rsa2048";
};
};
@fdt-SEQ {
description = "test fdt";
type = "flat_dt";
compression = "none";
hash {
algo = "sha256";
};
signature {
algo = "sha256,rsa2048";
key-name-hint = "rsa2048";
};
};
};
configurations {
default = "@conf-uboot-DEFAULT-SEQ";
@conf-uboot-SEQ {
description = "uboot config";
fdt = "fdt-SEQ";
fit,firmware = "u-boot";
fit,loadables;
hash {
algo = "sha256";
};
signature {
algo = "sha256,rsa2048";
key-name-hint = "rsa2048";
sign-images = "firmware", "loadables", "fdt";
};
};
};
};
};
};

View file

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDVUiT2JAF8Ajcx
3XTB5qdGxuPMVFcXKJH+4L66oSt4YUBGi1bClo80U2azu08BTzk2Jzv6hez/mvzL
hBvL3WnPwMl5vdOxb1kvUQyKLSw2bkM8VB0X1jGsKsKjzArg/aI8RknfiaSc5jua
2lqwUFwv2RMF8jvIMN/1GnTLdECeMFVgVFSFkzIocISAHGPoGUOxTf8xK7o0x4RX
NzB+95RtIqTQ5Az/KPVCOcQR5ETrUBXHF1I0rYjJjHHO4dUxxfDqFabt60EzQ/R2
oZu58C4y0TrRI98g4hVPBYapildWjaNQm1Exa4ZaSDVl01OXsFW9Dm80PqfW4tTH
Cm4nuCq5AgMBAAECggEBAIoG5b2SHJfFwzrzpQmVmeTU6i6a3+MvMBAwEZkmkb8J
hhJfNFsiGjTsRgbDiuI5BbbBejCmmWvmN+3jZCzr7fwsLPEl36TufFF+atO5WOM7
Qyv07QIwaOGSpXBgpSVhV6kSfdgy8p1G54hSAt4UkSGwnnt5ei8VWMP6Q1oltW3k
f9DQ/ar4UEVa4jlJU3xqchcUTiKBKSH6pMC/Fqlq8x5JTLmk1Yb6C2UNcgJYez1u
sHkdCA0FG3rFPrpFoQ1LUjMj1uEYNAxM3jOxE7Uvmk4yo9WpQDY7cRb2+Th9YY8a
IKQ2s81Yg2TmkGzr8f5nrZz3WbAmQhQgsKbwlo6snjUCgYEA7kBOt0JlU7bJTfOr
9s51g2VUfIH9lDS2Eh8MY+Bt6Y0Kdw/UK4HR8ZlN/nn0bHuHkc12K8lXEsQpgIEW
DaqHytZJHqFs2egzKu/IvQYZ2WXEMj47LZQxEDHO9gtjE+5qCW9yJGqxW9BJKPVD
F4spus4NqC+yD5OHM+6ESUtL/wMCgYEA5TZj6OHmECeh3efrwHqjDcjrqQbOTozU
KPCNCY3Pv4Cg4xas/L93TE2CY6HJTr6mwEMUM+M4Ujjj15VCmSDQ/YYcGau1jo+f
XdphOEENrPwoe9ATWIyBpT/wDrEz3L6JbE9dWMYY8vKYESt3qhVqDlbpmnYl8Jm+
O3r5Cy2NlJMCgYEAyqzsCZuy5QcesnByvm8dqpxdxdkzJYu9wyakfKZj+gUgfO57
OFOkjFk07yFB27MuPctCFredmfpDr+ygHRoPkG7AHw2Fss2EEaeP5bU18ilPQMqN
vxVMs5EblVVUgJUVoVcsC2yz2f4S7oPOAk5BPoehOIzydauznWrvIAas7I8CgYBr
CFHxLoNq6cbZQ3JACERZrIf2/vmZjoOHtoR1gKYRK7R1NmKDB7lihRMtCSBix/4/
61Lkw+bJ5kzmn4lgzgUpTdWTWy5FquVlQxOA3EfRjlItNsXB5KKpksi7Y53vJ34u
eIUDbkW6NPQzmFOhtaw3k/gzq5Yd2v0M82iWAqiJRwKBgQCl2+e2cjISK31QhKTC
puhwQ0/YuC3zlwMXQgB3nPw8b9RlaDTMrRBCIUFIrrX11tHswGWpyVsxW2AvZ3Zm
jsWpwGkUdpRdXJBhSaisV/PA+x3kYhpibzEI8FrzhU69zNROCb8CTkN4WcdBdq6J
PUh/jRtKoE79qrlnIlNvFoz2gQ==
-----END PRIVATE KEY-----

View file

@ -0,0 +1,98 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
fit {
description = "test desc";
#address-cells = <1>;
fit,fdt-list = "of-list";
fit,sign;
images {
u-boot {
description = "test u-boot";
type = "standalone";
arch = "arm64";
os = "u-boot";
compression = "none";
load = <0x00000000>;
entry = <0x00000000>;
u-boot-nodtb {
};
hash {
algo = "sha256";
};
signature {
algo = "sha256,rsa2048";
key-name-hint = "rsa2048";
};
};
@atf-SEQ {
fit,operation = "split-elf";
description = "test tf-a";
type = "firmware";
arch = "arm64";
os = "arm-trusted-firmware";
compression = "none";
fit,load;
fit,entry;
fit,data;
atf-bl31 {
};
hash {
algo = "sha256";
};
signature {
algo = "sha256,rsa2048";
key-name-hint = "rsa2048";
};
};
@fdt-SEQ {
description = "test fdt";
type = "flat_dt";
compression = "none";
hash {
algo = "sha256";
};
signature {
algo = "sha256,rsa2048";
key-name-hint = "rsa2048";
};
};
};
configurations {
default = "@conf-uboot-DEFAULT-SEQ";
@conf-uboot-SEQ {
description = "uboot config";
fdt = "fdt-SEQ";
fit,firmware = "u-boot";
fit,loadables;
hash {
algo = "sha256";
};
signature {
algo = "sha256,rsa2048";
key-name-hint = "conf-rsa2048";
sign-images = "firmware", "loadables", "fdt";
};
};
};
};
};
};

View file

@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
fit {
description = "test desc";
#address-cells = <1>;
fit,fdt-list = "of-list";
fit,sign;
images {
u-boot {
description = "test u-boot";
type = "standalone";
arch = "arm64";
os = "u-boot";
compression = "none";
load = <0x00000000>;
entry = <0x00000000>;
u-boot-nodtb {
};
};
@atf-SEQ {
fit,operation = "split-elf";
description = "test tf-a";
type = "firmware";
arch = "arm64";
os = "arm-trusted-firmware";
compression = "none";
fit,load;
fit,entry;
fit,data;
atf-bl31 {
};
};
@fdt-SEQ {
description = "test fdt";
type = "flat_dt";
compression = "none";
};
};
configurations {
default = "@conf-uboot-DEFAULT-SEQ";
@conf-uboot-SEQ {
description = "uboot config";
fdt = "fdt-SEQ";
fit,firmware = "u-boot";
fit,loadables;
};
};
};
};
};

View file

@ -34,7 +34,7 @@ from u_boot_pylib.terminal import tprint
# Error in reading or end of file. # Error in reading or end of file.
# << # <<
# which indicates that BREAK_ME has an empty default # which indicates that BREAK_ME has an empty default
RE_NO_DEFAULT = re.compile(b'\((\w+)\) \[] \(NEW\)') RE_NO_DEFAULT = re.compile(br'\((\w+)\) \[] \(NEW\)')
# Symbol types which appear in the bloat feature (-B). Others are silently # Symbol types which appear in the bloat feature (-B). Others are silently
# dropped when reading in the 'nm' output # dropped when reading in the 'nm' output
@ -374,9 +374,9 @@ class Builder:
self._re_function = re.compile('(.*): In function.*') self._re_function = re.compile('(.*): In function.*')
self._re_files = re.compile('In file included from.*') self._re_files = re.compile('In file included from.*')
self._re_warning = re.compile('(.*):(\d*):(\d*): warning: .*') self._re_warning = re.compile(r'(.*):(\d*):(\d*): warning: .*')
self._re_dtb_warning = re.compile('(.*): Warning .*') self._re_dtb_warning = re.compile('(.*): Warning .*')
self._re_note = re.compile('(.*):(\d*):(\d*): note: this is the location of the previous.*') self._re_note = re.compile(r'(.*):(\d*):(\d*): note: this is the location of the previous.*')
self._re_migration_warning = re.compile(r'^={21} WARNING ={22}\n.*\n=+\n', self._re_migration_warning = re.compile(r'^={21} WARNING ={22}\n.*\n=+\n',
re.MULTILINE | re.DOTALL) re.MULTILINE | re.DOTALL)

View file

@ -440,12 +440,12 @@ class Toolchains:
This converts ${blah} within the string to the value of blah. This converts ${blah} within the string to the value of blah.
This function works recursively. This function works recursively.
Resolved string
Args: Args:
var_dict: Dictionary containing variables and their values var_dict: Dictionary containing variables and their values
args: String containing make arguments args: String containing make arguments
Returns: Returns:
Resolved string
>>> bsettings.setup(None) >>> bsettings.setup(None)
>>> tcs = Toolchains() >>> tcs = Toolchains()
>>> tcs.Add('fred', False) >>> tcs.Add('fred', False)
@ -456,7 +456,7 @@ class Toolchains:
>>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd') >>> tcs.ResolveReferences(var_dict, 'this=${oblique}_set${first}nd')
'this=OBLIQUE_setfi2ndrstnd' 'this=OBLIQUE_setfi2ndrstnd'
""" """
re_var = re.compile('(\$\{[-_a-z0-9A-Z]{1,}\})') re_var = re.compile(r'(\$\{[-_a-z0-9A-Z]{1,}\})')
while True: while True:
m = re_var.search(args) m = re_var.search(args)
@ -495,7 +495,7 @@ class Toolchains:
self._make_flags['target'] = brd.target self._make_flags['target'] = brd.target
arg_str = self.ResolveReferences(self._make_flags, arg_str = self.ResolveReferences(self._make_flags,
self._make_flags.get(brd.target, '')) self._make_flags.get(brd.target, ''))
args = re.findall("(?:\".*?\"|\S)+", arg_str) args = re.findall(r"(?:\".*?\"|\S)+", arg_str)
i = 0 i = 0
while i < len(args): while i < len(args):
args[i] = args[i].replace('"', '') args[i] = args[i].replace('"', '')

View file

@ -244,12 +244,15 @@ RUN mkdir /tmp/trace && \
rm -rf /tmp/trace rm -rf /tmp/trace
# Build coreboot # Build coreboot
RUN wget -O - https://coreboot.org/releases/coreboot-4.22.01.tar.xz | tar -C /tmp -xJ && \ RUN wget -O - https://coreboot.org/releases/coreboot-24.08.tar.xz | tar -C /tmp -xJ && \
cd /tmp/coreboot-4.22.01 && \ cd /tmp/coreboot-24.08 && \
make crossgcc-i386 CPUS=$(nproc) && \ make crossgcc-i386 CPUS=$(nproc) && \
make -C payloads/coreinfo olddefconfig && \ make -C payloads/coreinfo olddefconfig && \
make -C payloads/coreinfo && \ make -C payloads/coreinfo && \
make olddefconfig && \ make olddefconfig && \
echo CONFIG_GENERIC_LINEAR_FRAMEBUFFER=y | tee -a .config && \
echo CONFIG_USE_OPTION_TABLE=y | tee -a .config && \
make olddefconfig && \
make -j $(nproc) && \ make -j $(nproc) && \
sudo mkdir /opt/coreboot && \ sudo mkdir /opt/coreboot && \
sudo cp build/coreboot.rom build/cbfstool /opt/coreboot/ sudo cp build/coreboot.rom build/cbfstool /opt/coreboot/

View file

@ -20,17 +20,22 @@ from u_boot_pylib import tools
# Parse: # Parse:
# SCENE1 = 7, # SCENE1 = 7,
# or SCENE1 = EXPOID_BASE_ID,
# or SCENE2, # or SCENE2,
RE_ENUM = re.compile(r'(\S*)(\s*= (\d))?,') RE_ENUM = re.compile(r'(\S*)(\s*= ([0-9A-Z_]+))?,')
# Parse #define <name> "string" # Parse #define <name> "string"
RE_DEF = re.compile(r'#define (\S*)\s*"(.*)"') RE_DEF = re.compile(r'#define (\S*)\s*"(.*)"')
def calc_ids(fname): # Parse EXPOID_BASE_ID = 5,
RE_BASE_ID = re.compile(r'\s*EXPOID_BASE_ID\s*= (\d+),')
def calc_ids(fname, base_id):
"""Figure out the value of the enums in a C file """Figure out the value of the enums in a C file
Args: Args:
fname (str): Filename to parse fname (str): Filename to parse
base_id (int): Base ID (value of EXPOID_BASE_ID)
Returns: Returns:
OrderedDict(): OrderedDict():
@ -55,8 +60,12 @@ def calc_ids(fname):
if not line or line.startswith('/*'): if not line or line.startswith('/*'):
continue continue
m_enum = RE_ENUM.match(line) m_enum = RE_ENUM.match(line)
if m_enum.group(3): enum_name = m_enum.group(3)
cur_id = int(m_enum.group(3)) if enum_name:
if enum_name == 'EXPOID_BASE_ID':
cur_id = base_id
else:
cur_id = int(enum_name)
vals[m_enum.group(1)] = cur_id vals[m_enum.group(1)] = cur_id
cur_id += 1 cur_id += 1
else: else:
@ -67,10 +76,24 @@ def calc_ids(fname):
return vals return vals
def find_base_id():
fname = 'include/expo.h'
base_id = None
with open(fname, 'r', encoding='utf-8') as inf:
for line in inf.readlines():
m_base_id = RE_BASE_ID.match(line)
if m_base_id:
base_id = int(m_base_id.group(1))
if base_id is None:
raise ValueError('EXPOID_BASE_ID not found in expo.h')
#print(f'EXPOID_BASE_ID={base_id}')
return base_id
def run_expo(args): def run_expo(args):
"""Run the expo program""" """Run the expo program"""
base_id = find_base_id()
fname = args.enum_fname or args.layout fname = args.enum_fname or args.layout
ids = calc_ids(fname) ids = calc_ids(fname, base_id)
if not ids: if not ids:
print(f"Warning: No enum ID values found in file '{fname}'") print(f"Warning: No enum ID values found in file '{fname}'")

View file

@ -1333,7 +1333,7 @@ int fit_add_verification_data(const char *keydir, const char *keyfile,
if (ret) { if (ret) {
fprintf(stderr, "Can't add verification data for node '%s' (%s)\n", fprintf(stderr, "Can't add verification data for node '%s' (%s)\n",
fdt_get_name(fit, noffset, NULL), fdt_get_name(fit, noffset, NULL),
fdt_strerror(ret)); strerror(-ret));
return ret; return ret;
} }
} }

View file

@ -23,8 +23,9 @@ except:
use_concurrent = False use_concurrent = False
def run_test_coverage(prog, filter_fname, exclude_list, build_dir, required=None, def run_test_coverage(prog, filter_fname, exclude_list, build_dir,
extra_args=None, single_thread='-P1'): required=None, extra_args=None, single_thread='-P1',
args=None):
"""Run tests and check that we get 100% coverage """Run tests and check that we get 100% coverage
Args: Args:
@ -42,6 +43,7 @@ def run_test_coverage(prog, filter_fname, exclude_list, build_dir, required=None
single_thread (str): Argument string to make the tests run single_thread (str): Argument string to make the tests run
single-threaded. This is necessary to get proper coverage results. single-threaded. This is necessary to get proper coverage results.
The default is '-P0' The default is '-P0'
args (list of str): List of tests to run, or None to run all
Raises: Raises:
ValueError if the code coverage is not 100% ValueError if the code coverage is not 100%
@ -66,9 +68,10 @@ def run_test_coverage(prog, filter_fname, exclude_list, build_dir, required=None
'coverage') 'coverage')
cmd = ('%s%s run ' cmd = ('%s%s run '
'--omit "%s" %s %s %s %s' % (prefix, covtool, ','.join(glob_list), '--omit "%s" %s %s %s %s %s' % (prefix, covtool, ','.join(glob_list),
prog, extra_args or '', test_cmd, prog, extra_args or '', test_cmd,
single_thread or '-P1')) single_thread or '-P1',
' '.join(args) if args else ''))
os.system(cmd) os.system(cmd)
stdout = command.output(covtool, 'report') stdout = command.output(covtool, 'report')
lines = stdout.splitlines() lines = stdout.splitlines()