diff options
100 files changed, 10036 insertions, 455 deletions
@@ -247,6 +247,7 @@ OBJS := $(addprefix $(obj),$(OBJS)) HAVE_VENDOR_COMMON_LIB = $(if $(wildcard board/$(VENDOR)/common/Makefile),y,n) LIBS-y += lib/libgeneric.o +LIBS-y += lib/rsa/librsa.o LIBS-y += lib/lzma/liblzma.o LIBS-y += lib/lzo/liblzo.o LIBS-y += lib/zlib/libz.o @@ -831,7 +832,8 @@ clean: $(obj)tools/mk{smdk5250,}spl \ $(obj)tools/mxsboot \ $(obj)tools/ncb $(obj)tools/ubsha1 \ - $(obj)tools/kernel-doc/docproc + $(obj)tools/kernel-doc/docproc \ + $(obj)tools/proftool @rm -f $(obj)board/cray/L1/{bootscript.c,bootscript.image} \ $(obj)board/matrix_vision/*/bootscript.img \ $(obj)board/voiceblue/eeprom \ @@ -854,7 +854,7 @@ The following options need to be configured: CONFIG_CMD_FDOS * Dos diskette Support CONFIG_CMD_FLASH flinfo, erase, protect CONFIG_CMD_FPGA FPGA device initialization support - CONFIG_CMD_FUSE Device fuse support + CONFIG_CMD_FUSE * Device fuse support CONFIG_CMD_GETTIME * Get time since boot CONFIG_CMD_GO * the 'go' command (exec code) CONFIG_CMD_GREPENV * search environment @@ -864,7 +864,7 @@ The following options need to be configured: CONFIG_CMD_IDE * IDE harddisk support CONFIG_CMD_IMI iminfo CONFIG_CMD_IMLS List all images found in NOR flash - CONFIG_CMD_IMLS_NAND List all images found in NAND flash + CONFIG_CMD_IMLS_NAND * List all images found in NAND flash CONFIG_CMD_IMMAP * IMMR dump support CONFIG_CMD_IMPORTENV * import an environment CONFIG_CMD_INI * import data from an ini file into the env @@ -872,23 +872,24 @@ The following options need to be configured: CONFIG_CMD_ITEST Integer/string test of 2 values CONFIG_CMD_JFFS2 * JFFS2 Support CONFIG_CMD_KGDB * kgdb - CONFIG_CMD_LDRINFO ldrinfo (display Blackfin loader) + CONFIG_CMD_LDRINFO * ldrinfo (display Blackfin loader) CONFIG_CMD_LINK_LOCAL * link-local IP address auto-configuration (169.254.*.*) CONFIG_CMD_LOADB loadb CONFIG_CMD_LOADS loads - CONFIG_CMD_MD5SUM print md5 message digest + CONFIG_CMD_MD5SUM * print md5 message digest (requires CONFIG_CMD_MEMORY and CONFIG_MD5) CONFIG_CMD_MEMINFO * Display detailed memory information CONFIG_CMD_MEMORY md, mm, nm, mw, cp, cmp, crc, base, loop, loopw - CONFIG_CMD_MEMTEST mtest + CONFIG_CMD_MEMTEST * mtest CONFIG_CMD_MISC Misc functions like sleep etc CONFIG_CMD_MMC * MMC memory mapped support CONFIG_CMD_MII * MII utility commands CONFIG_CMD_MTDPARTS * MTD partition support CONFIG_CMD_NAND * NAND support CONFIG_CMD_NET bootp, tftpboot, rarpboot + CONFIG_CMD_NFS NFS support CONFIG_CMD_PCA953X * PCA953x I2C gpio commands CONFIG_CMD_PCA953X_INFO * PCA953x I2C gpio info command CONFIG_CMD_PCI * pciinfo @@ -907,7 +908,7 @@ The following options need to be configured: CONFIG_CMD_SETGETDCR Support for DCR Register access (4xx only) CONFIG_CMD_SF * Read/write/erase SPI NOR flash - CONFIG_CMD_SHA1SUM print sha1 memory digest + CONFIG_CMD_SHA1SUM * print sha1 memory digest (requires CONFIG_CMD_MEMORY) CONFIG_CMD_SOFTSWITCH * Soft switch setting command for BF60x CONFIG_CMD_SOURCE "source" command Support @@ -919,6 +920,7 @@ The following options need to be configured: CONFIG_CMD_USB * USB support CONFIG_CMD_CDP * Cisco Discover Protocol support CONFIG_CMD_MFSL * Microblaze FSL support + CONFIG_CMD_XIMG Load part of Multi Image EXAMPLE: If you want all functions except of network @@ -1443,6 +1445,11 @@ CBFS (Coreboot Filesystem) support Export function i8042_kbd_init, i8042_tstc and i8042_getc for cfb_console. Supports cursor blinking. + CONFIG_CROS_EC_KEYB + Enables a Chrome OS keyboard using the CROS_EC interface. + This uses CROS_EC to communicate with a second microcontroller + which provides key scans on request. + - Video support: CONFIG_VIDEO @@ -2576,6 +2583,16 @@ CBFS (Coreboot Filesystem) support Note: There is also a sha1sum command, which should perhaps be deprecated in favour of 'hash sha1'. +- Signing support: + CONFIG_RSA + + This enables the RSA algorithm used for FIT image verification + in U-Boot. See doc/uImage/signature for more information. + + The signing part is build into mkimage regardless of this + option. + + - Show boot progress: CONFIG_SHOW_BOOT_PROGRESS @@ -2800,6 +2817,11 @@ FIT uImage format: most specific compatibility entry of U-Boot's fdt's root node. The order of entries in the configuration's fdt is ignored. + CONFIG_FIT_SIGNATURE + This option enables signature verification of FIT uImages, + using a hash signed and verified using RSA. See + doc/uImage.FIT/signature.txt for more details. + - Standalone program support: CONFIG_STANDALONE_LOAD_ADDR diff --git a/arch/arm/cpu/armv7/s5p-common/timer.c b/arch/arm/cpu/armv7/s5p-common/timer.c index 4adfaae656..637593c339 100644 --- a/arch/arm/cpu/armv7/s5p-common/timer.c +++ b/arch/arm/cpu/armv7/s5p-common/timer.c @@ -95,7 +95,7 @@ unsigned long get_timer(unsigned long base) return time_ms - base; } -unsigned long timer_get_us(void) +unsigned long __attribute__((no_instrument_function)) timer_get_us(void) { static unsigned long base_time_us; diff --git a/arch/arm/dts/exynos5250.dtsi b/arch/arm/dts/exynos5250.dtsi index cee4fe82cd..2d6dfff591 100644 --- a/arch/arm/dts/exynos5250.dtsi +++ b/arch/arm/dts/exynos5250.dtsi @@ -202,4 +202,6 @@ interrupts = <0 78 0>; }; + gpio: gpio { + }; }; diff --git a/arch/arm/include/asm/arch-exynos/cpu.h b/arch/arm/include/asm/arch-exynos/cpu.h index 36b98c83e1..1ff7642d06 100644 --- a/arch/arm/include/asm/arch-exynos/cpu.h +++ b/arch/arm/include/asm/arch-exynos/cpu.h @@ -178,7 +178,7 @@ static inline char *s5p_get_cpu_name(void) } #define IS_SAMSUNG_TYPE(type, id) \ -static inline int cpu_is_##type(void) \ +static inline int __attribute__((no_instrument_function)) cpu_is_##type(void) \ { \ return (s5p_cpu_id >> 12) == id; \ } @@ -187,7 +187,8 @@ IS_SAMSUNG_TYPE(exynos4, 0x4) IS_SAMSUNG_TYPE(exynos5, 0x5) #define IS_EXYNOS_TYPE(type, id) \ -static inline int proid_is_##type(void) \ +static inline int __attribute__((no_instrument_function)) \ + proid_is_##type(void) \ { \ return s5p_cpu_id == id; \ } @@ -197,9 +198,10 @@ IS_EXYNOS_TYPE(exynos4412, 0x4412) IS_EXYNOS_TYPE(exynos5250, 0x5250) #define SAMSUNG_BASE(device, base) \ -static inline unsigned int samsung_get_base_##device(void) \ +static inline unsigned int __attribute__((no_instrument_function)) \ + samsung_get_base_##device(void) \ { \ - if (cpu_is_exynos4()) { \ + if (cpu_is_exynos4()) { \ if (proid_is_exynos4412()) \ return EXYNOS4X12_##base; \ return EXYNOS4_##base; \ diff --git a/arch/arm/lib/bootm.c b/arch/arm/lib/bootm.c index 1b6e0ace45..b22fbc9982 100644 --- a/arch/arm/lib/bootm.c +++ b/arch/arm/lib/bootm.c @@ -68,12 +68,19 @@ void arch_lmb_reserve(struct lmb *lmb) gd->bd->bi_dram[0].start + gd->bd->bi_dram[0].size - sp); } -static void announce_and_cleanup(void) +/** + * announce_and_cleanup() - Print message and prepare for kernel boot + * + * @fake: non-zero to do everything except actually boot + */ +static void announce_and_cleanup(int fake) { - printf("\nStarting kernel ...\n\n"); + printf("\nStarting kernel ...%s\n\n", fake ? + "(fake run for tracing)" : ""); bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel"); #ifdef CONFIG_BOOTSTAGE_FDT - bootstage_fdt_add_report(); + if (flag == BOOTM_STATE_OS_FAKE_GO) + bootstage_fdt_add_report(); #endif #ifdef CONFIG_BOOTSTAGE_REPORT bootstage_report(); @@ -225,12 +232,13 @@ static void boot_prep_linux(bootm_headers_t *images) } /* Subcommand: GO */ -static void boot_jump_linux(bootm_headers_t *images) +static void boot_jump_linux(bootm_headers_t *images, int flag) { unsigned long machid = gd->bd->bi_arch_number; char *s; void (*kernel_entry)(int zero, int arch, uint params); unsigned long r2; + int fake = (flag & BOOTM_STATE_OS_FAKE_GO); kernel_entry = (void (*)(int, int, uint))images->ep; @@ -243,14 +251,15 @@ static void boot_jump_linux(bootm_headers_t *images) debug("## Transferring control to Linux (at address %08lx)" \ "...\n", (ulong) kernel_entry); bootstage_mark(BOOTSTAGE_ID_RUN_OS); - announce_and_cleanup(); + announce_and_cleanup(fake); if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len) r2 = (unsigned long)images->ft_addr; else r2 = gd->bd->bi_boot_params; - kernel_entry(0, machid, r2); + if (!fake) + kernel_entry(0, machid, r2); } /* Main Entry point for arm bootm implementation @@ -270,13 +279,13 @@ int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images) return 0; } - if (flag & BOOTM_STATE_OS_GO) { - boot_jump_linux(images); + if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) { + boot_jump_linux(images, flag); return 0; } boot_prep_linux(images); - boot_jump_linux(images); + boot_jump_linux(images, flag); return 0; } diff --git a/arch/m68k/cpu/mcf5445x/speed.c b/arch/m68k/cpu/mcf5445x/speed.c index aa73e1f025..0276d4d826 100644 --- a/arch/m68k/cpu/mcf5445x/speed.c +++ b/arch/m68k/cpu/mcf5445x/speed.c @@ -122,17 +122,17 @@ void setup_5441x_clocks(void) vco = ((in_be32(&pll->pcr) & PLL_CR_FBKDIV_BITS) + 1) * CONFIG_SYS_INPUT_CLKSRC; - gd->vco_clk = vco; + gd->arch.vco_clk = vco; - gd->inp_clk = CONFIG_SYS_INPUT_CLKSRC; /* Input clock */ + gd->arch.inp_clk = CONFIG_SYS_INPUT_CLKSRC; /* Input clock */ pdr = in_be32(&pll->pdr); temp = (pdr & PLL_DR_OUTDIV1_BITS) + 1; gd->cpu_clk = vco / temp; /* cpu clock */ - gd->flb_clk = vco / temp; /* FlexBus clock */ - gd->flb_clk >>= 1; + gd->arch.flb_clk = vco / temp; /* FlexBus clock */ + gd->arch.flb_clk >>= 1; if (in_be16(ccm->misccr2) & 2) /* fsys/4 */ - gd->flb_clk >>= 1; + gd->arch.flb_clk >>= 1; temp = ((pdr & PLL_DR_OUTDIV2_BITS) >> 5) + 1; gd->bus_clk = vco / temp; /* bus clock */ diff --git a/arch/m68k/include/asm/m5235.h b/arch/m68k/include/asm/m5235.h index 71a40d33d2..a573f1cf01 100644 --- a/arch/m68k/include/asm/m5235.h +++ b/arch/m68k/include/asm/m5235.h @@ -134,7 +134,7 @@ #define SDRAMC_DCR_RC(x) (((x)&0xFF)<<8) /* Bit definitions and macros for SDRAMC_DARCn */ -#define SDRAMC_DARCn_BA(x) (((x)&0xFFFC)<<18) +#define SDRAMC_DARCn_BA(x) ((x)&0xFFFC0000) #define SDRAMC_DARCn_RE (0x00008000) #define SDRAMC_DARCn_CASL_MASK (0x00003000) #define SDRAMC_DARCn_CASL_C0 (0x00000000) diff --git a/arch/m68k/lib/board.c b/arch/m68k/lib/board.c index b2e3068364..efc9fccc1b 100644 --- a/arch/m68k/lib/board.c +++ b/arch/m68k/lib/board.c @@ -403,14 +403,14 @@ void board_init_r (gd_t *id, ulong dest_addr) gd->flags |= GD_FLG_RELOC; /* tell others: relocation done */ - debug ("Now running in RAM - U-Boot at: %08lx\n", dest_addr); - WATCHDOG_RESET (); gd->reloc_off = dest_addr - CONFIG_SYS_MONITOR_BASE; serial_initialize(); + debug("Now running in RAM - U-Boot at: %08lx\n", dest_addr); + monitor_flash_len = (ulong)&__init_end - dest_addr; #if defined(CONFIG_NEEDS_MANUAL_RELOC) diff --git a/arch/microblaze/lib/bootm.c b/arch/microblaze/lib/bootm.c index 3842709001..b328f94971 100644 --- a/arch/microblaze/lib/bootm.c +++ b/arch/microblaze/lib/bootm.c @@ -62,8 +62,8 @@ int do_bootm_linux(int flag, int argc, char * const argv[], bootstage_mark(BOOTSTAGE_ID_RUN_OS); - if (!of_flat_tree && argc > 3) - of_flat_tree = (char *)simple_strtoul(argv[3], NULL, 16); + if (!of_flat_tree && argc > 1) + of_flat_tree = (char *)simple_strtoul(argv[1], NULL, 16); /* fixup the initrd now that we know where it should be */ if (images->rd_start && images->rd_end && of_flat_tree) diff --git a/arch/nios2/lib/bootm.c b/arch/nios2/lib/bootm.c index f32be52108..114e146d9b 100644 --- a/arch/nios2/lib/bootm.c +++ b/arch/nios2/lib/bootm.c @@ -40,8 +40,8 @@ int do_bootm_linux(int flag, int argc, char * const argv[], bootm_headers_t *ima if (images->ft_len) of_flat_tree = images->ft_addr; #endif - if (!of_flat_tree && argc > 3) - of_flat_tree = (char *)simple_strtoul(argv[3], NULL, 16); + if (!of_flat_tree && argc > 1) + of_flat_tree = (char *)simple_strtoul(argv[1], NULL, 16); if (of_flat_tree) initrd_end = (ulong)of_flat_tree; diff --git a/arch/openrisc/lib/bootm.c b/arch/openrisc/lib/bootm.c index 2c5d9aea99..7f716b8598 100644 --- a/arch/openrisc/lib/bootm.c +++ b/arch/openrisc/lib/bootm.c @@ -63,8 +63,8 @@ int do_bootm_linux(int flag, int argc, char * const argv[], show_boot_progress(15); - if (!of_flat_tree && argc > 3) - of_flat_tree = (char *)simple_strtoul(argv[3], NULL, 16); + if (!of_flat_tree && argc > 1) + of_flat_tree = (char *)simple_strtoul(argv[1], NULL, 16); #ifdef DEBUG printf("## Transferring control to Linux (at address 0x%08lx) " \ "ramdisk 0x%08lx, FDT 0x%08lx...\n", diff --git a/arch/sandbox/cpu/cpu.c b/arch/sandbox/cpu/cpu.c index dd8d495e3f..e9385de2a6 100644 --- a/arch/sandbox/cpu/cpu.c +++ b/arch/sandbox/cpu/cpu.c @@ -37,7 +37,7 @@ void __udelay(unsigned long usec) os_usleep(usec); } -unsigned long timer_get_us(void) +unsigned long __attribute__((no_instrument_function)) timer_get_us(void) { return os_get_nsec() / 1000; } diff --git a/arch/sandbox/cpu/os.c b/arch/sandbox/cpu/os.c index d07540776c..541e450bf6 100644 --- a/arch/sandbox/cpu/os.c +++ b/arch/sandbox/cpu/os.c @@ -152,7 +152,7 @@ void os_usleep(unsigned long usec) usleep(usec); } -u64 os_get_nsec(void) +u64 __attribute__((no_instrument_function)) os_get_nsec(void) { #if defined(CLOCK_MONOTONIC) && defined(_POSIX_MONOTONIC_CLOCK) struct timespec tp; diff --git a/arch/x86/include/asm/global_data.h b/arch/x86/include/asm/global_data.h index 4fdb08090a..9a2056a70f 100644 --- a/arch/x86/include/asm/global_data.h +++ b/arch/x86/include/asm/global_data.h @@ -40,7 +40,7 @@ struct arch_global_data { #include <asm-generic/global_data.h> #ifndef __ASSEMBLY__ -static inline gd_t *get_fs_gd_ptr(void) +static inline __attribute__((no_instrument_function)) gd_t *get_fs_gd_ptr(void) { gd_t *gd_ptr; diff --git a/arch/x86/include/asm/msr.h b/arch/x86/include/asm/msr.h index 6030633d10..b459a63ee6 100644 --- a/arch/x86/include/asm/msr.h +++ b/arch/x86/include/asm/msr.h @@ -85,7 +85,8 @@ static inline unsigned long long native_read_tscp(unsigned int *aux) #define EAX_EDX_RET(val, low, high) "=A" (val) #endif -static inline unsigned long long native_read_msr(unsigned int msr) +static inline __attribute__((no_instrument_function)) + unsigned long long native_read_msr(unsigned int msr) { DECLARE_ARGS(val, low, high); diff --git a/arch/x86/include/asm/u-boot-x86.h b/arch/x86/include/asm/u-boot-x86.h index 22e093427f..709dc8400d 100644 --- a/arch/x86/include/asm/u-boot-x86.h +++ b/arch/x86/include/asm/u-boot-x86.h @@ -64,7 +64,7 @@ void board_init_f_r_trampoline(ulong) __attribute__ ((noreturn)); void board_init_f_r(void) __attribute__ ((noreturn)); /* Read the time stamp counter */ -static inline uint64_t rdtsc(void) +static inline __attribute__((no_instrument_function)) uint64_t rdtsc(void) { uint32_t high, low; __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high)); diff --git a/arch/x86/lib/bootm.c b/arch/x86/lib/bootm.c index 2520228b4c..0d3250cfa3 100644 --- a/arch/x86/lib/bootm.c +++ b/arch/x86/lib/bootm.c @@ -63,6 +63,8 @@ int do_bootm_linux(int flag, int argc, char * const argv[], } #if defined(CONFIG_FIT) } else if (images->fit_uname_os) { + int ret; + ret = fit_image_get_data(images->fit_hdr_os, images->fit_noffset_os, &data, &len); if (ret) { diff --git a/arch/x86/lib/gcc.c b/arch/x86/lib/gcc.c index 4043431eca..497ad75b7a 100644 --- a/arch/x86/lib/gcc.c +++ b/arch/x86/lib/gcc.c @@ -28,7 +28,9 @@ #define WRAP_LIBGCC_CALL(type, name) \ type __normal_##name(type a, type b) __attribute__((regparm(0))); \ type __wrap_##name(type a, type b); \ - type __wrap_##name(type a, type b) { return __normal_##name(a, b); } + type __attribute__((no_instrument_function)) \ + __wrap_##name(type a, type b) \ + { return __normal_##name(a, b); } WRAP_LIBGCC_CALL(long long, __divdi3) WRAP_LIBGCC_CALL(unsigned long long, __udivdi3) diff --git a/arch/x86/lib/tsc_timer.c b/arch/x86/lib/tsc_timer.c index c509801f9e..06889737d1 100644 --- a/arch/x86/lib/tsc_timer.c +++ b/arch/x86/lib/tsc_timer.c @@ -37,7 +37,7 @@ void timer_set_base(u64 base) * restart. This yields a free running counter guaranteed to take almost 6 * years to wrap around even at 100GHz clock rate. */ -u64 get_ticks(void) +u64 __attribute__((no_instrument_function)) get_ticks(void) { u64 now_tick = rdtsc(); @@ -50,7 +50,7 @@ u64 get_ticks(void) #define PLATFORM_INFO_MSR 0xce /* Get the speed of the TSC timer in MHz */ -unsigned long get_tbclk_mhz(void) +unsigned __attribute__((no_instrument_function)) long get_tbclk_mhz(void) { u32 ratio; u64 platform_info = native_read_msr(PLATFORM_INFO_MSR); @@ -75,7 +75,7 @@ ulong get_timer(ulong base) return get_ms_timer() - base; } -ulong timer_get_us(void) +ulong __attribute__((no_instrument_function)) timer_get_us(void) { return get_ticks() / get_tbclk_mhz(); } diff --git a/board/samsung/dts/exynos5250-snow.dts b/board/samsung/dts/exynos5250-snow.dts index 24658c1989..d2ccc6675d 100644 --- a/board/samsung/dts/exynos5250-snow.dts +++ b/board/samsung/dts/exynos5250-snow.dts @@ -32,6 +32,33 @@ spi4 = "/spi@131b0000"; }; + i2c4: i2c@12ca0000 { + cros-ec@1e { + reg = <0x1e>; + compatible = "google,cros-ec"; + i2c-max-frequency = <100000>; + ec-interrupt = <&gpio 782 1>; + }; + + power-regulator@48 { + compatible = "ti,tps65090"; + reg = <0x48>; + }; + }; + + spi@131b0000 { + spi-max-frequency = <1000000>; + spi-deactivate-delay = <100>; + cros-ec@0 { + reg = <0>; + compatible = "google,cros-ec"; + spi-max-frequency = <5000000>; + ec-interrupt = <&gpio 782 1>; + optimise-flash-write; + status = "disabled"; + }; + }; + sound@12d60000 { samsung,i2s-epll-clock-frequency = <192000000>; samsung,i2s-sampling-rate = <48000>; @@ -69,4 +96,58 @@ samsung,dc-value = <25>; }; + cros-ec-keyb { + compatible = "google,cros-ec-keyb"; + google,key-rows = <8>; + google,key-columns = <13>; + google,repeat-delay-ms = <240>; + google,repeat-rate-ms = <30>; + google,ghost-filter; + /* + * Keymap entries take the form of 0xRRCCKKKK where + * RR=Row CC=Column KKKK=Key Code + * The values below are for a US keyboard layout and + * are taken from the Linux driver. Note that the + * 102ND key is not used for US keyboards. + */ + linux,keymap = < + /* CAPSLCK F1 B F10 */ + 0x0001003a 0x0002003b 0x00030030 0x00040044 + /* N = R_ALT ESC */ + 0x00060031 0x0008000d 0x000a0064 0x01010001 + /* F4 G F7 H */ + 0x0102003e 0x01030022 0x01040041 0x01060023 + /* ' F9 BKSPACE L_CTRL */ + 0x01080028 0x01090043 0x010b000e 0x0200001d + /* TAB F3 T F6 */ + 0x0201000f 0x0202003d 0x02030014 0x02040040 + /* ] Y 102ND [ */ + 0x0205001b 0x02060015 0x02070056 0x0208001a + /* F8 GRAVE F2 5 */ + 0x02090042 0x03010029 0x0302003c 0x03030006 + /* F5 6 - \ */ + 0x0304003f 0x03060007 0x0308000c 0x030b002b + /* R_CTRL A D F */ + 0x04000061 0x0401001e 0x04020020 0x04030021 + /* S K J ; */ + 0x0404001f 0x04050025 0x04060024 0x04080027 + /* L ENTER Z C */ + 0x04090026 0x040b001c 0x0501002c 0x0502002e + /* V X , M */ + 0x0503002f 0x0504002d 0x05050033 0x05060032 + /* L_SHIFT / . SPACE */ + 0x0507002a 0x05080035 0x05090034 0x050B0039 + /* 1 3 4 2 */ + 0x06010002 0x06020004 0x06030005 0x06040003 + /* 8 7 0 9 */ + 0x06050009 0x06060008 0x0608000b 0x0609000a + /* L_ALT DOWN RIGHT Q */ + 0x060a0038 0x060b006c 0x060c006a 0x07010010 + /* E R W I */ + 0x07020012 0x07030013 0x07040011 0x07050017 + /* U R_SHIFT P O */ + 0x07060016 0x07070036 0x07080019 0x07090018 + /* UP LEFT */ + 0x070b0067 0x070c0069>; + }; }; diff --git a/board/samsung/smdk5250/exynos5-dt.c b/board/samsung/smdk5250/exynos5-dt.c index 813150586f..aacf43eaa4 100644 --- a/board/samsung/smdk5250/exynos5-dt.c +++ b/board/samsung/smdk5250/exynos5-dt.c @@ -21,6 +21,7 @@ */ #include <common.h> +#include <cros_ec.h> #include <fdtdec.h> #include <asm/io.h> #include <errno.h> @@ -69,6 +70,13 @@ static void boot_temp_check(void) } #endif +struct local_info { + struct cros_ec_dev *cros_ec_dev; /* Pointer to cros_ec device */ + int cros_ec_err; /* Error for cros_ec, 0 if ok */ +}; + +static struct local_info local; + #ifdef CONFIG_USB_EHCI_EXYNOS int board_usb_vbus_init(void) { @@ -97,6 +105,20 @@ static void board_enable_audio_codec(void) } #endif +struct cros_ec_dev *board_get_cros_ec_dev(void) +{ + return local.cros_ec_dev; +} + +static int board_init_cros_ec_devices(const void *blob) +{ + local.cros_ec_err = cros_ec_init(blob, &local.cros_ec_dev); + if (local.cros_ec_err) + return -1; /* Will report in board_late_init() */ + + return 0; +} + int board_init(void) { gd->bd->bi_boot_params = (PHYS_SDRAM_1 + 0x100UL); @@ -112,6 +134,10 @@ int board_init(void) #ifdef CONFIG_EXYNOS_SPI spi_init(); #endif + + if (board_init_cros_ec_devices(gd->fdt_blob)) + return -1; + #ifdef CONFIG_USB_EHCI_EXYNOS board_usb_vbus_init(); #endif @@ -421,3 +447,22 @@ void exynos_set_dp_phy(unsigned int onoff) set_dp_phy_ctrl(onoff); } #endif + +#ifdef CONFIG_BOARD_LATE_INIT +int board_late_init(void) +{ + stdio_print_current_devices(); + + if (local.cros_ec_err) { + /* Force console on */ + gd->flags &= ~GD_FLG_SILENT; + + printf("cros-ec communications failure %d\n", + local.cros_ec_err); + puts("\nPlease reset with Power+Refresh\n\n"); + panic("Cannot init cros-ec device"); + return -1; + } + return 0; +} +#endif diff --git a/common/Makefile b/common/Makefile index 35816037ec..48791b7ffc 100644 --- a/common/Makefile +++ b/common/Makefile @@ -169,6 +169,7 @@ COBJS-$(CONFIG_CMD_SPIBOOTLDR) += cmd_spibootldr.o COBJS-$(CONFIG_CMD_STRINGS) += cmd_strings.o COBJS-$(CONFIG_CMD_TERMINAL) += cmd_terminal.o COBJS-$(CONFIG_CMD_TIME) += cmd_time.o +COBJS-$(CONFIG_CMD_TRACE) += cmd_trace.o COBJS-$(CONFIG_SYS_HUSH_PARSER) += cmd_test.o COBJS-$(CONFIG_CMD_TPM) += cmd_tpm.o COBJS-$(CONFIG_CMD_TSI148) += cmd_tsi148.o @@ -242,6 +243,7 @@ COBJS-y += dlmalloc.o COBJS-y += image.o COBJS-$(CONFIG_OF_LIBFDT) += image-fdt.o COBJS-$(CONFIG_FIT) += image-fit.o +COBJS-$(CONFIG_FIT_SIGNATURE) += image-sig.o COBJS-y += memsize.o COBJS-y += stdio.o diff --git a/common/board_f.c b/common/board_f.c index 8efdb63655..ab4242a77e 100644 --- a/common/board_f.c +++ b/common/board_f.c @@ -53,6 +53,7 @@ #include <os.h> #include <post.h> #include <spi.h> +#include <trace.h> #include <watchdog.h> #include <asm/errno.h> #include <asm/io.h> @@ -500,6 +501,18 @@ static int reserve_lcd(void) } #endif /* CONFIG_LCD */ +static int reserve_trace(void) +{ +#ifdef CONFIG_TRACE + gd->relocaddr -= CONFIG_TRACE_BUFFER_SIZE; + gd->trace_buff = map_sysmem(gd->relocaddr, CONFIG_TRACE_BUFFER_SIZE); + debug("Reserving %dk for trace data at: %08lx\n", + CONFIG_TRACE_BUFFER_SIZE >> 10, gd->relocaddr); +#endif + + return 0; +} + #if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) \ && !defined(CONFIG_ARM) && !defined(CONFIG_X86) static int reserve_video(void) @@ -818,8 +831,9 @@ static init_fnc_t init_sequence_f[] = { #ifdef CONFIG_SANDBOX setup_ram_buf, #endif - setup_fdt, setup_mon_len, + setup_fdt, + trace_early_init, #if defined(CONFIG_MPC85xx) || defined(CONFIG_MPC86xx) /* TODO: can this go into arch_cpu_init()? */ probecpu, @@ -963,6 +977,7 @@ static init_fnc_t init_sequence_f[] = { #ifdef CONFIG_LCD reserve_lcd, #endif + reserve_trace, /* TODO: Why the dependency on CONFIG_8xx? */ #if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) \ && !defined(CONFIG_ARM) && !defined(CONFIG_X86) diff --git a/common/board_r.c b/common/board_r.c index f5649c95f1..f7a036e32f 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -58,6 +58,7 @@ #include <serial.h> #include <spi.h> #include <stdio_dev.h> +#include <trace.h> #include <watchdog.h> #ifdef CONFIG_ADDR_MAP #include <asm/mmu.h> @@ -106,6 +107,15 @@ static int initr_secondary_cpu(void) return 0; } +static int initr_trace(void) +{ +#ifdef CONFIG_TRACE + trace_init(gd->trace_buff, CONFIG_TRACE_BUFFER_SIZE); +#endif + + return 0; +} + static int initr_reloc(void) { gd->flags |= GD_FLG_RELOC; /* tell others: relocation done */ @@ -711,6 +721,7 @@ static int run_main_loop(void) * TODO: perhaps reset the watchdog in the initcall function after each call? */ init_fnc_t init_sequence_r[] = { + initr_trace, initr_reloc, /* TODO: could x86/PPC have this also perhaps? */ #ifdef CONFIG_ARM diff --git a/common/bootstage.c b/common/bootstage.c index c5c69961ac..94a32a9971 100644 --- a/common/bootstage.c +++ b/common/bootstage.c @@ -49,6 +49,7 @@ static int next_id = BOOTSTAGE_ID_USER; enum { BOOTSTAGE_VERSION = 0, BOOTSTAGE_MAGIC = 0xb00757a3, + BOOTSTAGE_DIGITS = 9, }; struct bootstage_hdr { @@ -165,21 +166,6 @@ uint32_t bootstage_accum(enum bootstage_id id) return duration; } -static void print_time(unsigned long us_time) -{ - char str[15], *s; - int grab = 3; - - /* We don't seem to have %'d in U-Boot */ - sprintf(str, "%12lu", us_time); - for (s = str + 3; *s; s += grab) { - if (s != str + 3) - putc(s[-1] != ' ' ? ',' : ' '); - printf("%.*s", grab, s); - grab = 3; - } -} - /** * Get a record name as a printable string * @@ -208,10 +194,10 @@ static uint32_t print_time_record(enum bootstage_id id, if (prev == -1U) { printf("%11s", ""); - print_time(rec->time_us); + print_grouped_ull(rec->time_us, BOOTSTAGE_DIGITS); } else { - print_time(rec->time_us); - print_time(rec->time_us - prev); + print_grouped_ull(rec->time_us, BOOTSTAGE_DIGITS); + print_grouped_ull(rec->time_us - prev, BOOTSTAGE_DIGITS); } printf(" %s\n", get_record_name(buf, sizeof(buf), rec)); @@ -445,9 +431,9 @@ int bootstage_unstash(void *base, int size) } if (hdr->count * sizeof(*rec) > hdr->size) { - debug("%s: Bootstage has %d records needing %d bytes, but " + debug("%s: Bootstage has %d records needing %lu bytes, but " "only %d bytes is available\n", __func__, hdr->count, - hdr->count * sizeof(*rec), hdr->size); + (ulong)hdr->count * sizeof(*rec), hdr->size); return -1; } diff --git a/common/cmd_bootm.c b/common/cmd_bootm.c index e452fcac93..ba0bcd4e32 100644 --- a/common/cmd_bootm.c +++ b/common/cmd_bootm.c @@ -104,9 +104,18 @@ static const void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc, * - verified image architecture (PPC) and type (KERNEL or MULTI), * - loaded (first part of) image to header load address, * - disabled interrupts. + * + * @flag: Flags indicating what to do (BOOTM_STATE_...) + * @argc: Number of arguments. Note that the arguments are shifted down + * so that 0 is the first argument not processed by U-Boot, and + * argc is adjusted accordingly. This avoids confusion as to how + * many arguments are available for the OS. + * @images: Pointers to os/initrd/fdt + * @return 1 on error. On success the OS boots so this function does + * not return. */ typedef int boot_os_fn(int flag, int argc, char * const argv[], - bootm_headers_t *images); /* pointers to os/initrd/fdt */ + bootm_headers_t *images); #ifdef CONFIG_BOOTM_LINUX extern boot_os_fn do_bootm_linux; @@ -199,15 +208,21 @@ static inline void boot_start_lmb(bootm_headers_t *images) { } static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - const void *os_hdr; - int ret; - memset((void *)&images, 0, sizeof(images)); images.verify = getenv_yesno("verify"); boot_start_lmb(&images); bootstage_mark_name(BOOTSTAGE_ID_BOOTM_START, "bootm_start"); + images.state = BOOTM_STATE_START; + + return 0; +} + +static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + const void *os_hdr; /* get kernel image header, start address and length */ os_hdr = boot_get_kernel(cmdtp, flag, argc, argv, @@ -270,6 +285,8 @@ static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[] images.ep = image_get_ep(&images.legacy_hdr_os_copy); #if defined(CONFIG_FIT) } else if (images.fit_uname_os) { + int ret; + ret = fit_image_get_entry(images.fit_hdr_os, images.fit_noffset_os, &images.ep); if (ret) { @@ -287,6 +304,16 @@ static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[] images.ep += images.os.load; } + images.os.start = (ulong)os_hdr; + + return 0; +} + +static int bootm_find_other(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + int ret; + if (((images.os.type == IH_TYPE_KERNEL) || (images.os.type == IH_TYPE_KERNEL_NOLOAD) || (images.os.type == IH_TYPE_MULTI)) && @@ -312,9 +339,6 @@ static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[] #endif } - images.os.start = (ulong)os_hdr; - images.state = BOOTM_STATE_START; - return 0; } @@ -446,7 +470,7 @@ static int bootm_load_os(image_info_t os, ulong *load_end, int boot_progress) return 0; } -static int bootm_start_standalone(ulong iflag, int argc, char * const argv[]) +static int bootm_start_standalone(int argc, char * const argv[]) { char *s; int (*appl)(int, char * const []); @@ -457,7 +481,7 @@ static int bootm_start_standalone(ulong iflag, int argc, char * const argv[]) return 0; } appl = (int (*)(int, char * const []))(ulong)ntohl(images.ep); - (*appl)(argc-1, &argv[1]); + (*appl)(argc, argv); return 0; } @@ -475,108 +499,225 @@ static cmd_tbl_t cmd_bootm_sub[] = { U_BOOT_CMD_MKENT(cmdline, 0, 1, (void *)BOOTM_STATE_OS_CMDLINE, "", ""), U_BOOT_CMD_MKENT(bdt, 0, 1, (void *)BOOTM_STATE_OS_BD_T, "", ""), U_BOOT_CMD_MKENT(prep, 0, 1, (void *)BOOTM_STATE_OS_PREP, "", ""), + U_BOOT_CMD_MKENT(fake, 0, 1, (void *)BOOTM_STATE_OS_FAKE_GO, "", ""), U_BOOT_CMD_MKENT(go, 0, 1, (void *)BOOTM_STATE_OS_GO, "", ""), }; +static int boot_selected_os(int argc, char * const argv[], int state, + bootm_headers_t *images, boot_os_fn *boot_fn, ulong *iflag) +{ + if (images->os.type == IH_TYPE_STANDALONE) { + /* This may return when 'autostart' is 'no' */ + bootm_start_standalone(argc, argv); + return 0; + } + /* + * We have reached the point of no return: we are going to + * overwrite all exception vector code, so we cannot easily + * recover from any failures any more... + */ + *iflag = disable_interrupts(); +#ifdef CONFIG_NETCONSOLE + /* Stop the ethernet stack if NetConsole could have left it up */ + eth_halt(); +#endif + +#if defined(CONFIG_CMD_USB) + /* + * turn off USB to prevent the host controller from writing to the + * SDRAM while Linux is booting. This could happen (at least for OHCI + * controller), because the HCCA (Host Controller Communication Area) + * lies within the SDRAM and the host controller writes continously to + * this area (as busmaster!). The HccaFrameNumber is for example + * updated every 1 ms within the HCCA structure in SDRAM! For more + * details see the OpenHCI specification. + */ + usb_stop(); +#endif +#ifdef CONFIG_SILENT_CONSOLE + if (images->os.os == IH_OS_LINUX) + fixup_silent_linux(); +#endif + arch_preboot_os(); + boot_fn(state, argc, argv, images); + if (state == BOOTM_STATE_OS_FAKE_GO) /* We expect to return */ + return 0; + bootstage_error(BOOTSTAGE_ID_BOOT_OS_RETURNED); +#ifdef DEBUG + puts("\n## Control returned to monitor - resetting...\n"); +#endif + return BOOTM_ERR_RESET; +} + +/** + * Execute selected states of the bootm command. + * + * Note the arguments to this state must be the first argument, Any 'bootm' + * or sub-command arguments must have already been taken. + * + * Note that if states contains more than one flag it MUST contain + * BOOTM_STATE_START, since this handles and consumes the command line args. + * + * @param cmdtp Pointer to bootm command table entry + * @param flag Command flags (CMD_FLAG_...) + * @param argc Number of subcommand arguments (0 = no arguments) + * @param argv Arguments + * @param states Mask containing states to run (BOOTM_STATE_...) + * @param images Image header information + * @param boot_progress 1 to show boot progress, 0 to not do this + * @return 0 if ok, something else on error. Some errors will cause this + * function to perform a reboot! If states contains BOOTM_STATE_OS_GO + * then the intent is to boot an OS, so this function will not return + * unless the image type is standalone. + */ +static int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[], int states, bootm_headers_t *images, + int boot_progress) +{ + boot_os_fn *boot_fn; + ulong iflag = 0; + int ret = 0; + + images->state |= states; + + /* + * Work through the states and see how far we get. We stop on + * any error. + */ + if (states & BOOTM_STATE_START) + ret = bootm_start(cmdtp, flag, argc, argv); + + if (!ret && (states & BOOTM_STATE_FINDOS)) + ret = bootm_find_os(cmdtp, flag, argc, argv); + + if (!ret && (states & BOOTM_STATE_FINDOTHER)) { + ret = bootm_find_other(cmdtp, flag, argc, argv); + argc = 0; /* consume the args */ + } + + /* Load the OS */ + if (!ret && (states & BOOTM_STATE_LOADOS)) { + ulong load_end; + + ret = bootm_load_os(images->os, &load_end, 0); + if (!ret) { + lmb_reserve(&images->lmb, images->os.load, + (load_end - images->os.load)); + } + } + + /* Relocate the ramdisk */ +#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH + if (!ret && (states & BOOTM_STATE_RAMDISK)) { + ulong rd_len = images->rd_end - images->rd_start; + + ret = boot_ramdisk_high(&images->lmb, images->rd_start, + rd_len, &images->initrd_start, &images->initrd_end); + if (!ret) { + setenv_hex("initrd_start", images->initrd_start); + setenv_hex("initrd_end", images->initrd_end); + } + } +#endif +#if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_LMB) + if (!ret && (states & BOOTM_STATE_FDT)) { + boot_fdt_add_mem_rsv_regions(&images->lmb, images->ft_addr); + ret = boot_relocate_fdt(&images->lmb, &images->ft_addr, + &images->ft_len); + } +#endif + + /* From now on, we need the OS boot function */ + if (ret) + return ret; + boot_fn = boot_os[images->os.os]; + if (boot_fn == NULL) { + if (iflag) + enable_interrupts(); + printf("ERROR: booting os '%s' (%d) is not supported\n", + genimg_get_os_name(images->os.os), images->os.os); + bootstage_error(BOOTSTAGE_ID_CHECK_BOOT_OS); + return 1; + } + + /* Call various other states that are not generally used */ + if (!ret && (states & BOOTM_STATE_OS_CMDLINE)) + ret = boot_fn(BOOTM_STATE_OS_CMDLINE, argc, argv, images); + if (!ret && (states & BOOTM_STATE_OS_BD_T)) + ret = boot_fn(BOOTM_STATE_OS_BD_T, argc, argv, images); + if (!ret && (states & BOOTM_STATE_OS_PREP)) + ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images); + +#ifdef CONFIG_TRACE + /* Pretend to run the OS, then run a user command */ + if (!ret && (states & BOOTM_STATE_OS_FAKE_GO)) { + char *cmd_list = getenv("fakegocmd"); + + ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_FAKE_GO, + images, boot_fn, &iflag); + if (!ret && cmd_list) + ret = run_command_list(cmd_list, -1, flag); + } +#endif + /* Now run the OS! We hope this doesn't return */ + if (!ret && (states & BOOTM_STATE_OS_GO)) + ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO, + images, boot_fn, &iflag); + + /* Deal with any fallout */ + if (ret < 0) { + if (ret == BOOTM_ERR_UNIMPLEMENTED) { + if (iflag) + enable_interrupts(); + bootstage_error(BOOTSTAGE_ID_DECOMP_UNIMPL); + return 1; + } else if (ret == BOOTM_ERR_OVERLAP) { + if (images->legacy_hdr_valid) { + if (image_get_type(&images->legacy_hdr_os_copy) + == IH_TYPE_MULTI) + puts("WARNING: legacy format multi component image overwritten\n"); + } else { + puts("ERROR: new format image overwritten - must RESET the board to recover\n"); + bootstage_error(BOOTSTAGE_ID_OVERWRITTEN); + ret = BOOTM_ERR_RESET; + } + } + if (ret == BOOTM_ERR_RESET) + do_reset(cmdtp, flag, argc, argv); + } + if (iflag) + enable_interrupts(); + if (ret) + puts("subcommand not supported\n"); + + return ret; +} + static int do_bootm_subcommand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { int ret = 0; long state; cmd_tbl_t *c; - boot_os_fn *boot_fn; - c = find_cmd_tbl(argv[1], &cmd_bootm_sub[0], ARRAY_SIZE(cmd_bootm_sub)); + c = find_cmd_tbl(argv[0], &cmd_bootm_sub[0], ARRAY_SIZE(cmd_bootm_sub)); + argc--; argv++; if (c) { state = (long)c->cmd; - - /* treat start special since it resets the state machine */ - if (state == BOOTM_STATE_START) { - argc--; - argv++; - return bootm_start(cmdtp, flag, argc, argv); - } + if (state == BOOTM_STATE_START) + state |= BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER; } else { /* Unrecognized command */ return CMD_RET_USAGE; } - if (images.state < BOOTM_STATE_START || - images.state >= state) { + if (state != BOOTM_STATE_START && images.state >= state) { printf("Trying to execute a command out of order\n"); return CMD_RET_USAGE; } - images.state |= state; - boot_fn = boot_os[images.os.os]; - - switch (state) { - ulong load_end; - case BOOTM_STATE_START: - /* should never occur */ - break; - case BOOTM_STATE_LOADOS: - ret = bootm_load_os(images.os, &load_end, 0); - if (ret) - return ret; - - lmb_reserve(&images.lmb, images.os.load, - (load_end - images.os.load)); - break; -#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH - case BOOTM_STATE_RAMDISK: - { - ulong rd_len = images.rd_end - images.rd_start; - - ret = boot_ramdisk_high(&images.lmb, images.rd_start, - rd_len, &images.initrd_start, &images.initrd_end); - if (ret) - return ret; - - setenv_hex("initrd_start", images.initrd_start); - setenv_hex("initrd_end", images.initrd_end); - } - break; -#endif -#if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_LMB) - case BOOTM_STATE_FDT: - { - boot_fdt_add_mem_rsv_regions(&images.lmb, - images.ft_addr); - ret = boot_relocate_fdt(&images.lmb, - &images.ft_addr, &images.ft_len); - break; - } -#endif - case BOOTM_STATE_OS_CMDLINE: - ret = boot_fn(BOOTM_STATE_OS_CMDLINE, argc, argv, &images); - if (ret) - printf("cmdline subcommand not supported\n"); - break; - case BOOTM_STATE_OS_BD_T: - ret = boot_fn(BOOTM_STATE_OS_BD_T, argc, argv, &images); - if (ret) - printf("bdt subcommand not supported\n"); - break; - case BOOTM_STATE_OS_PREP: - ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, &images); - if (ret) - printf("prep subcommand not supported\n"); - break; - case BOOTM_STATE_OS_GO: - disable_interrupts(); -#ifdef CONFIG_NETCONSOLE - /* - * Stop the ethernet stack if NetConsole could have - * left it up - */ - eth_halt(); -#endif - arch_preboot_os(); - boot_fn(BOOTM_STATE_OS_GO, argc, argv, &images); - break; - } + ret = do_bootm_states(cmdtp, flag, argc, argv, state, &images, 0); return ret; } @@ -587,10 +728,6 @@ static int do_bootm_subcommand(cmd_tbl_t *cmdtp, int flag, int argc, int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - ulong iflag; - ulong load_end = 0; - int ret; - boot_os_fn *boot_fn; #ifdef CONFIG_NEEDS_MANUAL_RELOC static int relocated = 0; @@ -611,11 +748,12 @@ int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) #endif /* determine if we have a sub command */ - if (argc > 1) { + argc--; argv++; + if (argc > 0) { char *endp; - simple_strtoul(argv[1], &endp, 16); - /* endp pointing to NULL means that argv[1] was just a + simple_strtoul(argv[0], &endp, 16); + /* endp pointing to NULL means that argv[0] was just a * valid number, pass it along to the normal bootm processing * * If endp is ':' or '#' assume a FIT identifier so pass @@ -627,101 +765,10 @@ int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return do_bootm_subcommand(cmdtp, flag, argc, argv); } - if (bootm_start(cmdtp, flag, argc, argv)) - return 1; - - /* - * We have reached the point of no return: we are going to - * overwrite all exception vector code, so we cannot easily - * recover from any failures any more... - */ - iflag = disable_interrupts(); - -#ifdef CONFIG_NETCONSOLE - /* Stop the ethernet stack if NetConsole could have left it up */ - eth_halt(); -#endif - -#if defined(CONFIG_CMD_USB) - /* - * turn off USB to prevent the host controller from writing to the - * SDRAM while Linux is booting. This could happen (at least for OHCI - * controller), because the HCCA (Host Controller Communication Area) - * lies within the SDRAM and the host controller writes continously to - * this area (as busmaster!). The HccaFrameNumber is for example - * updated every 1 ms within the HCCA structure in SDRAM! For more - * details see the OpenHCI specification. - */ - usb_stop(); -#endif - - ret = bootm_load_os(images.os, &load_end, 1); - - if (ret < 0) { - if (ret == BOOTM_ERR_RESET) - do_reset(cmdtp, flag, argc, argv); - if (ret == BOOTM_ERR_OVERLAP) { - if (images.legacy_hdr_valid) { - image_header_t *hdr; - hdr = &images.legacy_hdr_os_copy; - if (image_get_type(hdr) == IH_TYPE_MULTI) - puts("WARNING: legacy format multi " - "component image " - "overwritten\n"); - } else { - puts("ERROR: new format image overwritten - " - "must RESET the board to recover\n"); - bootstage_error(BOOTSTAGE_ID_OVERWRITTEN); - do_reset(cmdtp, flag, argc, argv); - } - } - if (ret == BOOTM_ERR_UNIMPLEMENTED) { - if (iflag) - enable_interrupts(); - bootstage_error(BOOTSTAGE_ID_DECOMP_UNIMPL); - return 1; - } - } - - lmb_reserve(&images.lmb, images.os.load, (load_end - images.os.load)); - - if (images.os.type == IH_TYPE_STANDALONE) { - if (iflag) - enable_interrupts(); - /* This may return when 'autostart' is 'no' */ - bootm_start_standalone(iflag, argc, argv); - return 0; - } - - bootstage_mark(BOOTSTAGE_ID_CHECK_BOOT_OS); - -#if defined(CONFIG_SILENT_CONSOLE) && !defined(CONFIG_SILENT_U_BOOT_ONLY) - if (images.os.os == IH_OS_LINUX) - fixup_silent_linux(); -#endif - - boot_fn = boot_os[images.os.os]; - - if (boot_fn == NULL) { - if (iflag) - enable_interrupts(); - printf("ERROR: booting os '%s' (%d) is not supported\n", - genimg_get_os_name(images.os.os), images.os.os); - bootstage_error(BOOTSTAGE_ID_CHECK_BOOT_OS); - return 1; - } - - arch_preboot_os(); - - boot_fn(0, argc, argv, &images); - - bootstage_error(BOOTSTAGE_ID_BOOT_OS_RETURNED); -#ifdef DEBUG - puts("\n## Control returned to monitor - resetting...\n"); -#endif - do_reset(cmdtp, flag, argc, argv); - - return 1; + return do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START | + BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER | + BOOTM_STATE_LOADOS | BOOTM_STATE_OS_PREP | + BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO, &images, 1); } int bootm_maybe_autostart(cmd_tbl_t *cmdtp, const char *cmd) @@ -816,22 +863,22 @@ static const void *boot_get_kernel(cmd_tbl_t *cmdtp, int flag, int argc, #endif /* find out kernel image address */ - if (argc < 2) { + if (argc < 1) { img_addr = load_addr; debug("* kernel: default image load address = 0x%08lx\n", load_addr); #if defined(CONFIG_FIT) - } else if (fit_parse_conf(argv[1], load_addr, &img_addr, + } else if (fit_parse_conf(argv[0], load_addr, &img_addr, &fit_uname_config)) { debug("* kernel: config '%s' from image at 0x%08lx\n", fit_uname_config, img_addr); - } else if (fit_parse_subimage(argv[1], load_addr, &img_addr, + } else if (fit_parse_subimage(argv[0], load_addr, &img_addr, &fit_uname_kernel)) { debug("* kernel: subimage '%s' from image at 0x%08lx\n", fit_uname_kernel, img_addr); #endif } else { - img_addr = simple_strtoul(argv[1], NULL, 16); + img_addr = simple_strtoul(argv[0], NULL, 16); debug("* kernel: cmdline image address = 0x%08lx\n", img_addr); } @@ -1346,6 +1393,19 @@ static void fixup_silent_linux(void) } #endif /* CONFIG_SILENT_CONSOLE */ +#if defined(CONFIG_BOOTM_NETBSD) || defined(CONFIG_BOOTM_PLAN9) +static void copy_args(char *dest, int argc, char * const argv[], char delim) +{ + int i; + + for (i = 0; i < argc; i++) { + if (i > 0) + *dest++ = delim; + strcpy(dest, argv[i]); + dest += strlen(argv[i]); + } +} +#endif /*******************************************************************/ /* OS booting routines */ @@ -1401,20 +1461,14 @@ static int do_bootm_netbsd(int flag, int argc, char * const argv[], consdev = "scc3"; #endif - if (argc > 2) { + if (argc > 0) { ulong len; int i; - for (i = 2, len = 0; i < argc; i += 1) + for (i = 0, len = 0; i < argc; i += 1) len += strlen(argv[i]) + 1; cmdline = malloc(len); - - for (i = 2, len = 0; i < argc; i += 1) { - if (i > 2) - cmdline[len++] = ' '; - strcpy(&cmdline[len], argv[i]); - len += strlen(argv[i]); - } + copy_args(cmdline, argc, argv, ' '); } else if ((cmdline = getenv("bootargs")) == NULL) { cmdline = ""; } @@ -1533,6 +1587,7 @@ static int do_bootm_plan9(int flag, int argc, char * const argv[], bootm_headers_t *images) { void (*entry_point)(void); + char *s; if ((flag != 0) && (flag != BOOTM_STATE_OS_GO)) return 1; @@ -1544,6 +1599,20 @@ static int do_bootm_plan9(int flag, int argc, char * const argv[], } #endif + /* See README.plan9 */ + s = getenv("confaddr"); + if (s != NULL) { + char *confaddr = (char *)simple_strtoul(s, NULL, 16); + + if (argc > 0) { + copy_args(confaddr, argc, argv, '\n'); + } else { + s = getenv("bootargs"); + if (s != NULL) + strcpy(confaddr, s); + } + } + entry_point = (void (*)(void))images->ep; printf("## Transferring control to Plan 9 (at address %08lx) ...\n", @@ -1663,9 +1732,8 @@ static int bootz_start(cmd_tbl_t *cmdtp, int flag, int argc, int ret; void *zi_start, *zi_end; - memset(images, 0, sizeof(bootm_headers_t)); - - boot_start_lmb(images); + ret = do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START, + images, 1); /* Setup Linux kernel zImage entry point */ if (argc < 2) { @@ -1684,73 +1752,25 @@ static int bootz_start(cmd_tbl_t *cmdtp, int flag, int argc, lmb_reserve(&images->lmb, images->ep, zi_end - zi_start); - /* Find ramdisk */ - ret = boot_get_ramdisk(argc, argv, images, IH_INITRD_ARCH, - &images->rd_start, &images->rd_end); - if (ret) { - puts("Ramdisk image is corrupt or invalid\n"); - return 1; - } + ret = do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_FINDOTHER, + images, 1); -#if defined(CONFIG_OF_LIBFDT) - /* find flattened device tree */ - ret = boot_get_fdt(flag, argc, argv, IH_ARCH_DEFAULT, images, - &images->ft_addr, &images->ft_len); - if (ret) { - puts("Could not find a valid device tree\n"); - return 1; - } - - set_working_fdt_addr(images->ft_addr); -#endif - - return 0; + return ret; } int do_bootz(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { bootm_headers_t images; + int ret; if (bootz_start(cmdtp, flag, argc, argv, &images)) return 1; - /* - * We have reached the point of no return: we are going to - * overwrite all exception vector code, so we cannot easily - * recover from any failures any more... - */ - disable_interrupts(); + ret = do_bootm_states(cmdtp, flag, argc, argv, + BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO, + &images, 1); -#ifdef CONFIG_NETCONSOLE - /* Stop the ethernet stack if NetConsole could have left it up */ - eth_halt(); -#endif - -#if defined(CONFIG_CMD_USB) - /* - * turn off USB to prevent the host controller from writing to the - * SDRAM while Linux is booting. This could happen (at least for OHCI - * controller), because the HCCA (Host Controller Communication Area) - * lies within the SDRAM and the host controller writes continously to - * this area (as busmaster!). The HccaFrameNumber is for example - * updated every 1 ms within the HCCA structure in SDRAM! For more - * details see the OpenHCI specification. - */ - usb_stop(); -#endif - -#if defined(CONFIG_SILENT_CONSOLE) && !defined(CONFIG_SILENT_U_BOOT_ONLY) - fixup_silent_linux(); -#endif - arch_preboot_os(); - - do_bootm_linux(0, argc, argv, &images); -#ifdef DEBUG - puts("\n## Control returned to monitor - resetting...\n"); -#endif - do_reset(cmdtp, flag, argc, argv); - - return 1; + return ret; } #ifdef CONFIG_SYS_LONGHELP diff --git a/common/cmd_ide.c b/common/cmd_ide.c index 78b4aa70ba..59e95dfa12 100644 --- a/common/cmd_ide.c +++ b/common/cmd_ide.c @@ -830,7 +830,7 @@ static void ide_ident(block_dev_desc_t *dev_desc) /* ------------------------------------------------------------------------- */ -ulong ide_read(int device, ulong blknr, lbaint_t blkcnt, void *buffer) +ulong ide_read(int device, lbaint_t blknr, lbaint_t blkcnt, void *buffer) { ulong n = 0; unsigned char c; @@ -844,7 +844,7 @@ ulong ide_read(int device, ulong blknr, lbaint_t blkcnt, void *buffer) lba48 = 1; } #endif - debug("ide_read dev %d start %lX, blocks " LBAF " buffer at %lX\n", + debug("ide_read dev %d start " LBAF ", blocks " LBAF " buffer at %lX\n", device, blknr, blkcnt, (ulong) buffer); ide_led(DEVICE_LED(device), 1); /* LED on */ @@ -934,8 +934,8 @@ ulong ide_read(int device, ulong blknr, lbaint_t blkcnt, void *buffer) if ((c & (ATA_STAT_DRQ | ATA_STAT_BUSY | ATA_STAT_ERR)) != ATA_STAT_DRQ) { - printf("Error (no IRQ) dev %d blk %ld: status %#02x\n", - device, blknr, c); + printf("Error (no IRQ) dev %d blk " LBAF ": status " + "%#02x\n", device, blknr, c); break; } @@ -954,7 +954,7 @@ IDE_READ_E: /* ------------------------------------------------------------------------- */ -ulong ide_write(int device, ulong blknr, lbaint_t blkcnt, const void *buffer) +ulong ide_write(int device, lbaint_t blknr, lbaint_t blkcnt, const void *buffer) { ulong n = 0; unsigned char c; @@ -1022,8 +1022,8 @@ ulong ide_write(int device, ulong blknr, lbaint_t blkcnt, const void *buffer) if ((c & (ATA_STAT_DRQ | ATA_STAT_BUSY | ATA_STAT_ERR)) != ATA_STAT_DRQ) { - printf("Error (no IRQ) dev %d blk %ld: status %#02x\n", - device, blknr, c); + printf("Error (no IRQ) dev %d blk " LBAF ": status " + "%#02x\n", device, blknr, c); goto WR_OUT; } diff --git a/common/cmd_mem.c b/common/cmd_mem.c index 6df00b15d3..77eafa0b89 100644 --- a/common/cmd_mem.c +++ b/common/cmd_mem.c @@ -551,6 +551,8 @@ static int do_mem_loop(cmd_tbl_t *cmdtp, int flag, int argc, *cp++; } unmap_sysmem(buf); + + return 0; } #ifdef CONFIG_LOOPW diff --git a/common/cmd_trace.c b/common/cmd_trace.c new file mode 100644 index 0000000000..ec3137a8a2 --- /dev/null +++ b/common/cmd_trace.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <command.h> +#include <trace.h> +#include <asm/io.h> + +static int get_args(int argc, char * const argv[], char **buff, + size_t *buff_ptr, size_t *buff_size) +{ + if (argc < 2) + return -1; + if (argc < 4) { + *buff_size = getenv_ulong("profsize", 16, 0); + *buff = map_sysmem(getenv_ulong("profbase", 16, 0), + *buff_size); + *buff_ptr = getenv_ulong("profoffset", 16, 0); + } else { + *buff_size = simple_strtoul(argv[3], NULL, 16); + *buff = map_sysmem(simple_strtoul(argv[2], NULL, 16), + *buff_size); + *buff_ptr = 0; + }; + return 0; +} + +static int create_func_list(int argc, char * const argv[]) +{ + size_t buff_size, avail, buff_ptr, used; + unsigned int needed; + char *buff; + int err; + + if (get_args(argc, argv, &buff, &buff_ptr, &buff_size)) + return -1; + + avail = buff_size - buff_ptr; + err = trace_list_functions(buff + buff_ptr, avail, &needed); + if (err) + printf("Error: truncated (%#x bytes needed)\n", needed); + used = min(avail, needed); + printf("Function trace dumped to %08lx, size %#zx\n", + (ulong)map_to_sysmem(buff + buff_ptr), used); + setenv_hex("profbase", map_to_sysmem(buff)); + setenv_hex("profsize", buff_size); + setenv_hex("profoffset", buff_ptr + used); + + return 0; +} + +static int create_call_list(int argc, char * const argv[]) +{ + size_t buff_size, avail, buff_ptr, used; + unsigned int needed; + char *buff; + int err; + + if (get_args(argc, argv, &buff, &buff_ptr, &buff_size)) + return -1; + + avail = buff_size - buff_ptr; + err = trace_list_calls(buff + buff_ptr, avail, &needed); + if (err) + printf("Error: truncated (%#x bytes needed)\n", needed); + used = min(avail, needed); + printf("Call list dumped to %08lx, size %#zx\n", + (ulong)map_to_sysmem(buff + buff_ptr), used); + + setenv_hex("profbase", map_to_sysmem(buff)); + setenv_hex("profsize", buff_size); + setenv_hex("profoffset", buff_ptr + used); + + return 0; +} + +int do_trace(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + const char *cmd = argc < 2 ? NULL : argv[1]; + + if (!cmd) + return cmd_usage(cmdtp); + switch (*cmd) { + case 'p': + trace_set_enabled(0); + break; + case 'c': + if (create_call_list(argc, argv)) + return cmd_usage(cmdtp); + break; + case 'r': + trace_set_enabled(1); + break; + case 'f': + if (create_func_list(argc, argv)) + return cmd_usage(cmdtp); + break; + case 's': + trace_print_stats(); + break; + default: + return CMD_RET_USAGE; + } + + return 0; +} + +U_BOOT_CMD( + trace, 4, 1, do_trace, + "trace utility commands", + "stats - display tracing statistics\n" + "trace pause - pause tracing\n" + "trace resume - resume tracing\n" + "trace funclist [<addr> <size>] - dump function list into buffer\n" + "trace calls [<addr> <size>] " + "- dump function call trace into buffer" +); diff --git a/common/image-fdt.c b/common/image-fdt.c index 0d421d92fb..d99f444de1 100644 --- a/common/image-fdt.c +++ b/common/image-fdt.c @@ -248,13 +248,16 @@ int boot_get_fdt(int flag, int argc, char * const argv[], uint8_t arch, ulong default_addr; int fdt_noffset; #endif + const char *select = NULL; *of_flat_tree = NULL; *of_size = 0; - if (argc > 3 || genimg_has_config(images)) { + if (argc > 2) + select = argv[2]; + if (select || genimg_has_config(images)) { #if defined(CONFIG_FIT) - if (argc > 3) { + if (select) { /* * If the FDT blob comes from the FIT image and the * FIT image address is omitted in the command line @@ -268,18 +271,18 @@ int boot_get_fdt(int flag, int argc, char * const argv[], uint8_t arch, else default_addr = load_addr; - if (fit_parse_conf(argv[3], default_addr, + if (fit_parse_conf(select, default_addr, &fdt_addr, &fit_uname_config)) { debug("* fdt: config '%s' from image at 0x%08lx\n", fit_uname_config, fdt_addr); - } else if (fit_parse_subimage(argv[3], default_addr, + } else if (fit_parse_subimage(select, default_addr, &fdt_addr, &fit_uname_fdt)) { debug("* fdt: subimage '%s' from image at 0x%08lx\n", fit_uname_fdt, fdt_addr); } else #endif { - fdt_addr = simple_strtoul(argv[3], NULL, 16); + fdt_addr = simple_strtoul(select, NULL, 16); debug("* fdt: cmdline image address = 0x%08lx\n", fdt_addr); } diff --git a/common/image-fit.c b/common/image-fit.c index f40f1603f3..b75e119d93 100644 --- a/common/image-fit.c +++ b/common/image-fit.c @@ -234,42 +234,45 @@ void fit_print_contents(const void *fit) * @fit: pointer to the FIT format image header * @noffset: offset of the hash node * @p: pointer to prefix string + * @type: Type of information to print ("hash" or "sign") * * fit_image_print_data() lists properies for the processed hash node * + * This function avoid using puts() since it prints a newline on the host + * but does not in U-Boot. + * * returns: * no returned results */ -static void fit_image_print_data(const void *fit, int noffset, const char *p) +static void fit_image_print_data(const void *fit, int noffset, const char *p, + const char *type) { - char *algo; + const char *keyname; uint8_t *value; int value_len; - int i, ret; - - /* - * Check subnode name, must be equal to "hash". - * Multiple hash nodes require unique unit node - * names, e.g. hash@1, hash@2, etc. - */ - if (strncmp(fit_get_name(fit, noffset, NULL), - FIT_HASH_NODENAME, - strlen(FIT_HASH_NODENAME)) != 0) - return; + char *algo; + int required; + int ret, i; - debug("%s Hash node: '%s'\n", p, + debug("%s %s node: '%s'\n", p, type, fit_get_name(fit, noffset, NULL)); - - printf("%s Hash algo: ", p); + printf("%s %s algo: ", p, type); if (fit_image_hash_get_algo(fit, noffset, &algo)) { printf("invalid/unsupported\n"); return; } - printf("%s\n", algo); + printf("%s", algo); + keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); + required = fdt_getprop(fit, noffset, "required", NULL) != NULL; + if (keyname) + printf(":%s", keyname); + if (required) + printf(" (required)"); + printf("\n"); ret = fit_image_hash_get_value(fit, noffset, &value, &value_len); - printf("%s Hash value: ", p); + printf("%s %s value: ", p, type); if (ret) { printf("unavailable\n"); } else { @@ -278,7 +281,18 @@ static void fit_image_print_data(const void *fit, int noffset, const char *p) printf("\n"); } - debug("%s Hash len: %d\n", p, value_len); + debug("%s %s len: %d\n", p, type, value_len); + + /* Signatures have a time stamp */ + if (IMAGE_ENABLE_TIMESTAMP && keyname) { + time_t timestamp; + + printf("%s Timestamp: ", p); + if (fit_get_timestamp(fit, noffset, ×tamp)) + printf("unavailable\n"); + else + genimg_print_time(timestamp); + } } /** @@ -303,8 +317,12 @@ static void fit_image_print_verification_data(const void *fit, int noffset, * names, e.g. hash@1, hash@2, signature@1, signature@2, etc. */ name = fit_get_name(fit, noffset, NULL); - if (!strncmp(name, FIT_HASH_NODENAME, strlen(FIT_HASH_NODENAME))) - fit_image_print_data(fit, noffset, p); + if (!strncmp(name, FIT_HASH_NODENAME, strlen(FIT_HASH_NODENAME))) { + fit_image_print_data(fit, noffset, p, "Hash"); + } else if (!strncmp(name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + fit_image_print_data(fit, noffset, p, "Sign"); + } } /** @@ -944,13 +962,23 @@ int fit_image_verify(const void *fit, int image_noffset) { const void *data; size_t size; - int noffset; + int noffset = 0; char *err_msg = ""; + int verify_all = 1; + int ret; /* Get image data and data length */ if (fit_image_get_data(fit, image_noffset, &data, &size)) { err_msg = "Can't get image data/size"; - return 0; + goto error; + } + + /* Verify all required signatures */ + if (IMAGE_ENABLE_VERIFY && + fit_image_verify_required_sigs(fit, image_noffset, data, size, + gd_fdt_blob(), &verify_all)) { + err_msg = "Unable to verify required signature"; + goto error; } /* Process all hash subnodes of the component image node */ @@ -970,6 +998,15 @@ int fit_image_verify(const void *fit, int image_noffset) &err_msg)) goto error; puts("+ "); + } else if (IMAGE_ENABLE_VERIFY && verify_all && + !strncmp(name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + ret = fit_image_check_sig(fit, noffset, data, + size, -1, &err_msg); + if (ret) + puts("- "); + else + puts("+ "); } } diff --git a/common/image-sig.c b/common/image-sig.c new file mode 100644 index 0000000000..5d907cfc42 --- /dev/null +++ b/common/image-sig.c @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2013, Google Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifdef USE_HOSTCC +#include "mkimage.h" +#include <time.h> +#else +#include <common.h> +#include <malloc.h> +DECLARE_GLOBAL_DATA_PTR; +#endif /* !USE_HOSTCC*/ +#include <image.h> +#include <rsa.h> + +#define IMAGE_MAX_HASHED_NODES 100 + +struct image_sig_algo image_sig_algos[] = { + { + "sha1,rsa2048", + rsa_sign, + rsa_add_verify_data, + rsa_verify, + } +}; + +struct image_sig_algo *image_get_sig_algo(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(image_sig_algos); i++) { + if (!strcmp(image_sig_algos[i].name, name)) + return &image_sig_algos[i]; + } + + return NULL; +} + +/** + * fit_region_make_list() - Make a list of image regions + * + * Given a list of fdt_regions, create a list of image_regions. This is a + * simple conversion routine since the FDT and image code use different + * structures. + * + * @fit: FIT image + * @fdt_regions: Pointer to FDT regions + * @count: Number of FDT regions + * @region: Pointer to image regions, which must hold @count records. If + * region is NULL, then (except for an SPL build) the array will be + * allocated. + * @return: Pointer to image regions + */ +struct image_region *fit_region_make_list(const void *fit, + struct fdt_region *fdt_regions, int count, + struct image_region *region) +{ + int i; + + debug("Hash regions:\n"); + debug("%10s %10s\n", "Offset", "Size"); + + /* + * Use malloc() except in SPL (to save code size). In SPL the caller + * must allocate the array. + */ +#ifndef CONFIG_SPL_BUILD + if (!region) + region = calloc(sizeof(*region), count); +#endif + if (!region) + return NULL; + for (i = 0; i < count; i++) { + debug("%10x %10x\n", fdt_regions[i].offset, + fdt_regions[i].size); + region[i].data = fit + fdt_regions[i].offset; + region[i].size = fdt_regions[i].size; + } + + return region; +} + +static int fit_image_setup_verify(struct image_sign_info *info, + const void *fit, int noffset, int required_keynode, + char **err_msgp) +{ + char *algo_name; + + if (fit_image_hash_get_algo(fit, noffset, &algo_name)) { + *err_msgp = "Can't get hash algo property"; + return -1; + } + memset(info, '\0', sizeof(*info)); + info->keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); + info->fit = (void *)fit; + info->node_offset = noffset; + info->algo = image_get_sig_algo(algo_name); + info->fdt_blob = gd_fdt_blob(); + info->required_keynode = required_keynode; + printf("%s:%s", algo_name, info->keyname); + + if (!info->algo) { + *err_msgp = "Unknown signature algorithm"; + return -1; + } + + return 0; +} + +int fit_image_check_sig(const void *fit, int noffset, const void *data, + size_t size, int required_keynode, char **err_msgp) +{ + struct image_sign_info info; + struct image_region region; + uint8_t *fit_value; + int fit_value_len; + + *err_msgp = NULL; + if (fit_image_setup_verify(&info, fit, noffset, required_keynode, + err_msgp)) + return -1; + + if (fit_image_hash_get_value(fit, noffset, &fit_value, + &fit_value_len)) { + *err_msgp = "Can't get hash value property"; + return -1; + } + + region.data = data; + region.size = size; + + if (info.algo->verify(&info, ®ion, 1, fit_value, fit_value_len)) { + *err_msgp = "Verification failed"; + return -1; + } + + return 0; +} + +static int fit_image_verify_sig(const void *fit, int image_noffset, + const char *data, size_t size, const void *sig_blob, + int sig_offset) +{ + int noffset; + char *err_msg = ""; + int verified = 0; + int ret; + + /* Process all hash subnodes of the component image node */ + for (noffset = fdt_first_subnode(fit, image_noffset); + noffset >= 0; + noffset = fdt_next_subnode(fit, noffset)) { + const char *name = fit_get_name(fit, noffset, NULL); + + if (!strncmp(name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + ret = fit_image_check_sig(fit, noffset, data, + size, -1, &err_msg); + if (ret) { + puts("- "); + } else { + puts("+ "); + verified = 1; + break; + } + } + } + + if (noffset == -FDT_ERR_TRUNCATED || noffset == -FDT_ERR_BADSTRUCTURE) { + err_msg = "Corrupted or truncated tree"; + goto error; + } + + return verified ? 0 : -EPERM; + +error: + printf(" error!\n%s for '%s' hash node in '%s' image node\n", + err_msg, fit_get_name(fit, noffset, NULL), + fit_get_name(fit, image_noffset, NULL)); + return -1; +} + +int fit_image_verify_required_sigs(const void *fit, int image_noffset, + const char *data, size_t size, const void *sig_blob, + int *no_sigsp) +{ + int verify_count = 0; + int noffset; + int sig_node; + + /* Work out what we need to verify */ + *no_sigsp = 1; + sig_node = fdt_subnode_offset(sig_blob, 0, FIT_SIG_NODENAME); + if (sig_node < 0) { + debug("%s: No signature node found: %s\n", __func__, + fdt_strerror(sig_node)); + return 0; + } + + for (noffset = fdt_first_subnode(sig_blob, sig_node); + noffset >= 0; + noffset = fdt_next_subnode(sig_blob, noffset)) { + const char *required; + int ret; + + required = fdt_getprop(sig_blob, noffset, "required", NULL); + if (!required || strcmp(required, "image")) + continue; + ret = fit_image_verify_sig(fit, image_noffset, data, size, + sig_blob, noffset); + if (ret) { + printf("Failed to verify required signature '%s'\n", + fit_get_name(sig_blob, noffset, NULL)); + return ret; + } + verify_count++; + } + + if (verify_count) + *no_sigsp = 0; + + return 0; +} + +int fit_config_check_sig(const void *fit, int noffset, int required_keynode, + char **err_msgp) +{ + char * const exc_prop[] = {"data"}; + const char *prop, *end, *name; + struct image_sign_info info; + const uint32_t *strings; + uint8_t *fit_value; + int fit_value_len; + int max_regions; + int i, prop_len; + char path[200]; + int count; + + debug("%s: fdt=%p, conf='%s', sig='%s'\n", __func__, gd_fdt_blob(), + fit_get_name(fit, noffset, NULL), + fit_get_name(gd_fdt_blob(), required_keynode, NULL)); + *err_msgp = NULL; + if (fit_image_setup_verify(&info, fit, noffset, required_keynode, + err_msgp)) + return -1; + + if (fit_image_hash_get_value(fit, noffset, &fit_value, + &fit_value_len)) { + *err_msgp = "Can't get hash value property"; + return -1; + } + + /* Count the number of strings in the property */ + prop = fdt_getprop(fit, noffset, "hashed-nodes", &prop_len); + end = prop ? prop + prop_len : prop; + for (name = prop, count = 0; name < end; name++) + if (!*name) + count++; + if (!count) { + *err_msgp = "Can't get hashed-nodes property"; + return -1; + } + + /* Add a sanity check here since we are using the stack */ + if (count > IMAGE_MAX_HASHED_NODES) { + *err_msgp = "Number of hashed nodes exceeds maximum"; + return -1; + } + + /* Create a list of node names from those strings */ + char *node_inc[count]; + + debug("Hash nodes (%d):\n", count); + for (name = prop, i = 0; name < end; name += strlen(name) + 1, i++) { + debug(" '%s'\n", name); + node_inc[i] = (char *)name; + } + + /* + * Each node can generate one region for each sub-node. Allow for + * 7 sub-nodes (hash@1, signature@1, etc.) and some extra. + */ + max_regions = 20 + count * 7; + struct fdt_region fdt_regions[max_regions]; + + /* Get a list of regions to hash */ + count = fdt_find_regions(fit, node_inc, count, + exc_prop, ARRAY_SIZE(exc_prop), + fdt_regions, max_regions - 1, + path, sizeof(path), 0); + if (count < 0) { + *err_msgp = "Failed to hash configuration"; + return -1; + } + if (count == 0) { + *err_msgp = "No data to hash"; + return -1; + } + if (count >= max_regions - 1) { + *err_msgp = "Too many hash regions"; + return -1; + } + + /* Add the strings */ + strings = fdt_getprop(fit, noffset, "hashed-strings", NULL); + if (strings) { + fdt_regions[count].offset = fdt_off_dt_strings(fit) + + fdt32_to_cpu(strings[0]); + fdt_regions[count].size = fdt32_to_cpu(strings[1]); + count++; + } + + /* Allocate the region list on the stack */ + struct image_region region[count]; + + fit_region_make_list(fit, fdt_regions, count, region); + if (info.algo->verify(&info, region, count, fit_value, + fit_value_len)) { + *err_msgp = "Verification failed"; + return -1; + } + + return 0; +} + +static int fit_config_verify_sig(const void *fit, int conf_noffset, + const void *sig_blob, int sig_offset) +{ + int noffset; + char *err_msg = ""; + int verified = 0; + int ret; + + /* Process all hash subnodes of the component conf node */ + for (noffset = fdt_first_subnode(fit, conf_noffset); + noffset >= 0; + noffset = fdt_next_subnode(fit, noffset)) { + const char *name = fit_get_name(fit, noffset, NULL); + + if (!strncmp(name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + ret = fit_config_check_sig(fit, noffset, sig_offset, + &err_msg); + if (ret) { + puts("- "); + } else { + puts("+ "); + verified = 1; + break; + } + } + } + + if (noffset == -FDT_ERR_TRUNCATED || noffset == -FDT_ERR_BADSTRUCTURE) { + err_msg = "Corrupted or truncated tree"; + goto error; + } + + return verified ? 0 : -EPERM; + +error: + printf(" error!\n%s for '%s' hash node in '%s' config node\n", + err_msg, fit_get_name(fit, noffset, NULL), + fit_get_name(fit, conf_noffset, NULL)); + return -1; +} + +int fit_config_verify_required_sigs(const void *fit, int conf_noffset, + const void *sig_blob) +{ + int noffset; + int sig_node; + + /* Work out what we need to verify */ + sig_node = fdt_subnode_offset(sig_blob, 0, FIT_SIG_NODENAME); + if (sig_node < 0) { + debug("%s: No signature node found: %s\n", __func__, + fdt_strerror(sig_node)); + return 0; + } + + for (noffset = fdt_first_subnode(sig_blob, sig_node); + noffset >= 0; + noffset = fdt_next_subnode(sig_blob, noffset)) { + const char *required; + int ret; + + required = fdt_getprop(sig_blob, noffset, "required", NULL); + if (!required || strcmp(required, "conf")) + continue; + ret = fit_config_verify_sig(fit, conf_noffset, sig_blob, + noffset); + if (ret) { + printf("Failed to verify required signature '%s'\n", + fit_get_name(sig_blob, noffset, NULL)); + return ret; + } + } + + return 0; +} + +int fit_config_verify(const void *fit, int conf_noffset) +{ + return !fit_config_verify_required_sigs(fit, conf_noffset, + gd_fdt_blob()); +} diff --git a/common/image.c b/common/image.c index f863502ab1..1be384f26a 100644 --- a/common/image.c +++ b/common/image.c @@ -816,20 +816,23 @@ int boot_get_ramdisk(int argc, char * const argv[], bootm_headers_t *images, ulong default_addr; int rd_noffset; #endif + const char *select = NULL; *rd_start = 0; *rd_end = 0; + if (argc >= 2) + select = argv[1]; /* * Look for a '-' which indicates to ignore the * ramdisk argument */ - if ((argc >= 3) && (strcmp(argv[2], "-") == 0)) { + if (select && strcmp(select, "-") == 0) { debug("## Skipping init Ramdisk\n"); rd_len = rd_data = 0; - } else if (argc >= 3 || genimg_has_config(images)) { + } else if (select || genimg_has_config(images)) { #if defined(CONFIG_FIT) - if (argc >= 3) { + if (select) { /* * If the init ramdisk comes from the FIT image and * the FIT image address is omitted in the command @@ -841,12 +844,12 @@ int boot_get_ramdisk(int argc, char * const argv[], bootm_headers_t *images, else default_addr = load_addr; - if (fit_parse_conf(argv[2], default_addr, - &rd_addr, &fit_uname_config)) { + if (fit_parse_conf(select, default_addr, + &rd_addr, &fit_uname_config)) { debug("* ramdisk: config '%s' from image at " "0x%08lx\n", fit_uname_config, rd_addr); - } else if (fit_parse_subimage(argv[2], default_addr, + } else if (fit_parse_subimage(select, default_addr, &rd_addr, &fit_uname_ramdisk)) { debug("* ramdisk: subimage '%s' from image at " "0x%08lx\n", @@ -854,7 +857,7 @@ int boot_get_ramdisk(int argc, char * const argv[], bootm_headers_t *images, } else #endif { - rd_addr = simple_strtoul(argv[2], NULL, 16); + rd_addr = simple_strtoul(select, NULL, 16); debug("* ramdisk: cmdline image address = " "0x%08lx\n", rd_addr); @@ -918,7 +921,10 @@ int boot_get_ramdisk(int argc, char * const argv[], bootm_headers_t *images, #endif default: #ifdef CONFIG_SUPPORT_RAW_INITRD - if (argc >= 3 && (end = strchr(argv[2], ':'))) { + end = NULL; + if (select) + end = strchr(select, ':'); + if (end) { rd_len = simple_strtoul(++end, NULL, 16); rd_data = rd_addr; } else diff --git a/common/usb_storage.c b/common/usb_storage.c index 457970f770..4599d03c3a 100644 --- a/common/usb_storage.c +++ b/common/usb_storage.c @@ -170,9 +170,9 @@ int usb_stor_get_info(struct usb_device *dev, struct us_data *us, block_dev_desc_t *dev_desc); int usb_storage_probe(struct usb_device *dev, unsigned int ifnum, struct us_data *ss); -unsigned long usb_stor_read(int device, unsigned long blknr, +unsigned long usb_stor_read(int device, lbaint_t blknr, lbaint_t blkcnt, void *buffer); -unsigned long usb_stor_write(int device, unsigned long blknr, +unsigned long usb_stor_write(int device, lbaint_t blknr, lbaint_t blkcnt, const void *buffer); struct usb_device * usb_get_dev_index(int index); void uhci_show_temp_int_td(void); @@ -1054,7 +1054,7 @@ static void usb_bin_fixup(struct usb_device_descriptor descriptor, } #endif /* CONFIG_USB_BIN_FIXUP */ -unsigned long usb_stor_read(int device, unsigned long blknr, +unsigned long usb_stor_read(int device, lbaint_t blknr, lbaint_t blkcnt, void *buffer) { lbaint_t start, blks; @@ -1127,7 +1127,7 @@ retry_it: return blkcnt; } -unsigned long usb_stor_write(int device, unsigned long blknr, +unsigned long usb_stor_write(int device, lbaint_t blknr, lbaint_t blkcnt, const void *buffer) { lbaint_t start, blks; @@ -96,6 +96,7 @@ HOSTCFLAGS += $(call os_x_before, 10, 4, "-traditional-cpp") HOSTLDFLAGS += $(call os_x_before, 10, 5, "-multiply_defined suppress") else HOSTCC = gcc +HOSTLIBS += -lssl -lcrypto endif ifeq ($(HOSTOS),cygwin) @@ -268,6 +269,16 @@ CFLAGS += $(CFLAGS_WARN) CFLAGS_STACK := $(call cc-option,-fstack-usage) CFLAGS += $(CFLAGS_STACK) +BCURDIR = $(subst $(SRCTREE)/,,$(CURDIR:$(obj)%=%)) + +ifeq ($(findstring examples/,$(BCURDIR)),) +ifeq ($(CONFIG_SPL_BUILD),) +ifdef FTRACE +CFLAGS += -finstrument-functions -DFTRACE +endif +endif +endif + # $(CPPFLAGS) sets -g, which causes gcc to pass a suitable -g<format> # option to the assembler. AFLAGS_DEBUG := @@ -330,7 +341,6 @@ export CONFIG_SYS_TEXT_BASE PLATFORM_CPPFLAGS PLATFORM_RELFLAGS CPPFLAGS CFLAGS ######################################################################### # Allow boards to use custom optimize flags on a per dir/file basis -BCURDIR = $(subst $(SRCTREE)/,,$(CURDIR:$(obj)%=%)) ALL_AFLAGS = $(AFLAGS) $(AFLAGS_$(BCURDIR)/$(@F)) $(AFLAGS_$(BCURDIR)) ALL_CFLAGS = $(CFLAGS) $(CFLAGS_$(BCURDIR)/$(@F)) $(CFLAGS_$(BCURDIR)) EXTRA_CPPFLAGS = $(CPPFLAGS_$(BCURDIR)/$(@F)) $(CPPFLAGS_$(BCURDIR)) diff --git a/doc/README.plan9 b/doc/README.plan9 new file mode 100644 index 0000000000..2d3d0e0cf6 --- /dev/null +++ b/doc/README.plan9 @@ -0,0 +1,18 @@ +Plan 9 from Bell Labs kernel images require additional setup to pass +configuration information to the kernel. An environment variable named +confaddr must be defined with the same value as CONFADDR (see mem.h). +Use of this facility is optional, but should be preferable to manual +configuration. + +When booting an image, arguments supplied to the bootm command will be +copied to CONFADDR. If no arguments are specified, the contents of the +bootargs environment variable will be copied. + +If no command line arguments or bootargs are defined, CONFADDR is left +uninitialized to permit manual configuration. For example, PC-style +configuration could be simulated by issuing a fatload in bootcmd: + + # setenv bootcmd fatload mmc 0 $confaddr plan9.ini; ...; bootm + +Steven Stallion +June 2013 diff --git a/doc/README.trace b/doc/README.trace new file mode 100644 index 0000000000..b535c06566 --- /dev/null +++ b/doc/README.trace @@ -0,0 +1,361 @@ +# +# Copyright (c) 2013 The Chromium OS Authors. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundatio; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +Tracing in U-Boot +================= + +U-Boot supports a simple tracing feature which allows a record of excecution +to be collected and sent to a host machine for analysis. At present the +main use for this is to profile boot time. + + +Overview +-------- + +The trace feature uses GCC's instrument-functions feature to trace all +function entry/exit points. These are then recorded in a memory buffer. +The memory buffer can be saved to the host over a network link using +tftpput or by writing to an attached memory device such as MMC. + +On the host, the file is first converted with a tool called 'proftool', +which extracts useful information from it. The resulting trace output +resembles that emitted by Linux's ftrace feature, so can be visually +displayed by pytimechart. + + +Quick-start using Sandbox +------------------------- + +Sandbox is a build of U-Boot that can run under Linux so it is a convenient +way of trying out tracing before you use it on your actual board. To do +this, follow these steps: + +Add the following to include/configs/sandbox.h (if not already there) + +#define CONFIG_TRACE +#define CONFIG_CMD_TRACE +#define CONFIG_TRACE_BUFFER_SIZE (16 << 20) +#define CONFIG_TRACE_EARLY_SIZE (8 << 20) +#define CONFIG_TRACE_EARLY +#define CONFIG_TRACE_EARLY_ADDR 0x00100000 + +Build sandbox U-Boot with tracing enabled: + +$ make FTRACE=1 O=sandbox sandbox_config +$ make FTRACE=1 O=sandbox + +Run sandbox, wait for a bit of trace information to appear, and then capture +a trace: + +$ ./sandbox/u-boot + + +U-Boot 2013.04-rc2-00100-ga72fcef (Apr 17 2013 - 19:25:24) + +DRAM: 128 MiB +trace: enabled +Using default environment + +In: serial +Out: serial +Err: serial +=>trace stats + 671,406 function sites + 69,712 function calls + 0 untracked function calls + 73,373 traced function calls + 16 maximum observed call depth + 15 call depth limit + 66,491 calls not traced due to depth +=>trace stats + 671,406 function sites + 1,279,450 function calls + 0 untracked function calls + 950,490 traced function calls (333217 dropped due to overflow) + 16 maximum observed call depth + 15 call depth limit + 1,275,767 calls not traced due to depth +=>trace calls 0 e00000 +Call list dumped to 00000000, size 0xae0a40 +=>print +baudrate=115200 +profbase=0 +profoffset=ae0a40 +profsize=e00000 +stderr=serial +stdin=serial +stdout=serial + +Environment size: 117/8188 bytes +=>sb save host 0 trace 0 ${profoffset} +11405888 bytes written in 10 ms (1.1 GiB/s) +=>reset + + +Then run proftool to convert the trace information to ftrace format. + +$ ./sandbox/tools/proftool -m sandbox/System.map -p trace dump-ftrace >trace.txt + +Finally run pytimechart to display it: + +$ pytimechart trace.txt + +Using this tool you can zoom and pan across the trace, with the function +calls on the left and little marks representing the start and end of each +function. + + +CONFIG Options +-------------- + +- CONFIG_TRACE + Enables the trace feature in U-Boot. + +- CONFIG_CMD_TRACE + Enables the trace command. + +- CONFIG_TRACE_BUFFER_SIZE + Size of trace buffer to allocate for U-Boot. This buffer is + used after relocation, as a place to put function tracing + information. The address of the buffer is determined by + the relocation code. + +- CONFIG_TRACE_EARLY + Define this to start tracing early, before relocation. + +- CONFIG_TRACE_EARLY_SIZE + Size of 'early' trace buffer. Before U-Boot has relocated + it doesn't have a proper trace buffer. On many boards + you can define an area of memory to use for the trace + buffer until the 'real' trace buffer is available after + relocation. The contents of this buffer are then copied to + the real buffer. + +- CONFIG_TRACE_EARLY_ADDR + Address of early trace buffer + + +Building U-Boot with Tracing Enabled +------------------------------------ + +Pass 'FTRACE=1' to the U-Boot Makefile to actually instrument the code. +This is kept as a separate option so that it is easy to enable/disable +instrumenting from the command line instead of having to change board +config files. + + +Collecting Trace Data +--------------------- + +When you run U-Boot on your board it will collect trace data up to the +limit of the trace buffer size you have specified. Once that is exhausted +no more data will be collected. + +Collecting trace data has an affect on execution time/performance. You +will notice this particularly with trvial functions - the overhead of +recording their execution may even exceed their normal execution time. +In practice this doesn't matter much so long as you are aware of the +effect. Once you have done your optimisations, turn off tracing before +doing end-to-end timing. + +The best time to start tracing is right at the beginning of U-Boot. The +best time to stop tracing is right at the end. In practice it is hard +to achieve these ideals. + +This implementation enables tracing early in board_init_f(). This means +that it captures most of the board init process, missing only the +early architecture-specific init. However, it also misses the entire +SPL stage if there is one. + +U-Boot typically ends with a 'bootm' command which loads and runs an +OS. There is useful trace data in the execution of that bootm +command. Therefore this implementation provides a way to collect trace +data after bootm has finished processing, but just before it jumps to +the OS. In practical terms, U-Boot runs the 'fakegocmd' environment +variable at this point. This variable should have a short script which +collects the trace data and writes it somewhere. + +Trace data collection relies on a microsecond timer, accesed through +timer_get_us(). So the first think you should do is make sure that +this produces sensible results for your board. Suitable sources for +this timer include high resolution timers, PWMs or profile timers if +available. Most modern SOCs have a suitable timer for this. Make sure +that you mark this timer (and anything it calls) with +__attribute__((no_instrument_function)) so that the trace library can +use it without causing an infinite loop. + + +Commands +-------- + +The trace command has variable sub-commands: + +- stats + Display tracing statistics + +- pause + Pause tracing + +- resume + Resume tracing + +- funclist [<addr> <size>] + Dump a list of functions into the buffer + +- calls [<addr> <size>] + Dump function call trace into buffer + +If the address and size are not given, these are obtained from environment +variables (see below). In any case the environment variables are updated +after the command runs. + + +Environment Variables +--------------------- + +The following are used: + +- profbase + Base address of trace output buffer + +- profoffset + Offset of first unwritten byte in trace output buffer + +- profsize + Size of trace output buffer + +All of these are set by the 'trace calls' command. + +These variables keep track of the amount of data written to the trace +output buffer by the 'trace' command. The trace commands which write data +to the output buffer can use these to specify the buffer to write to, and +update profoffset each time. This allows successive commands to append data +to the same buffer, for example: + + trace funclist 10000 e00000 + trace calls + +(the latter command appends more data to the buffer). + + +- fakegocmd + Specifies commands to run just before booting the OS. This + is a useful time to write the trace data to the host for + processing. + + +Writing Out Trace Data +---------------------- + +Once the trace data is in an output buffer in memory there are various ways +to transmit it to the host. Notably you can use tftput to send the data +over a network link: + +fakegocmd=trace pause; usb start; set autoload n; bootp; + trace calls 10000000 1000000; + tftpput ${profbase} ${profoffset} 192.168.1.4:/tftpboot/calls + +This starts up USB (to talk to an attached USB Ethernet dongle), writes +a trace log to address 10000000 and sends it to a host machine using +TFTP. After this, U-Boot will boot the OS normally, albeit a little +later. + + +Converting Trace Output Data +---------------------------- + +The trace output data is kept in a binary format which is not documented +here. To convert it into something useful, you can use proftool. + +This tool must be given the U-Boot map file and the trace data received +from running that U-Boot. It produces a text output file. + +Options + -m <map_file> + Specify U-Boot map file + + -p <trace_file> + Specifiy profile/trace file + +Commands: + +- dump-ftrace + Write a text dump of the file in Linux ftrace format to stdout + + +Viewing the Trace Data +---------------------- + +You can use pytimechart for this (sudo apt-get pytimechart might work on +your Debian-style machine, and use your favourite search engine to obtain +documentation). It expects the file to have a .txt extension. The program +has terse user interface but is very convenient for viewing U-Boot +profile information. + + +Workflow Suggestions +-------------------- + +The following suggestions may be helpful if you are trying to reduce boot +time: + +1. Enable CONFIG_BOOTSTAGE and CONFIG_BOOTSTAGE_REPORT. This should get +you are helpful overall snapshot of the boot time. + +2. Build U-Boot with tracing and run it. Note the difference in boot time +(it is common for tracing to add 10% to the time) + +3. Collect the trace information as descibed above. Use this to find where +all the time is being spent. + +4. Take a look at that code and see if you can optimise it. Perhaps it is +possible to speed up the initialisation of a device, or remove an unused +feature. + +5. Rebuild, run and collect again. Compare your results. + +6. Keep going until you run out of steam, or your boot is fast enough. + + +Configuring Trace +----------------- + +There are a few parameters in the code that you may want to consider. +There is a function call depth limit (set to 15 by default). When the +stack depth goes above this then no tracing information is recorded. +The maximum depth reached is recorded and displayed by the 'trace stats' +command. + + +Future Work +----------- + +Tracing could be a little tidier in some areas, for example providing +run-time configuration options for trace. + +Some other features that might be useful: + +- Trace filter to select which functions are recorded +- Sample-based profiling using a timer interrupt +- Better control over trace depth +- Compression of trace information + + +Simon Glass <sjg@chromium.org> +April 2013 diff --git a/doc/device-tree-bindings/input/cros-ec-keyb.txt b/doc/device-tree-bindings/input/cros-ec-keyb.txt new file mode 100644 index 0000000000..3118276078 --- /dev/null +++ b/doc/device-tree-bindings/input/cros-ec-keyb.txt @@ -0,0 +1,79 @@ +CROS_EC Keyboard + +The CROS_EC (Matrix Keyboard Protocol) allows communcation with a secondary +micro used for keyboard, and possible other features. + +The CROS_EC keyboard uses this protocol to receive key scans and produce input +in U-Boot. + +Required properties : +- compatible : "google,cros-ec-keyb" +- google,key-rows : Number of key rows +- google,key-columns : Number of key columns + +Optional properties, in addition to those specified by the shared +matrix-keyboard bindings: + +- linux,fn-keymap: a second keymap, same specification as the + matrix-keyboard-controller spec but to be used when the KEY_FN modifier + key is pressed. +- google,repeat-delay-ms : delay in milliseconds before repeat starts +- google,repeat-rate-ms : delay between each subsequent key press +- google,ghost-filter : enable ghost filtering for this device + +Example, taken from daisy: + +cros-ec-keyb { + compatible = "google,cros-ec-keyb"; + google,key-rows = <8>; + google,key-columns = <13>; + google,ghost-filter; + google,repeat-delay-ms = <240>; + google,repeat-rate-ms = <30>; + /* + * Keymap entries take the form of 0xRRCCKKKK where + * RR=Row CC=Column KKKK=Key Code + * The values below are for a US keyboard layout and + * are taken from the Linux driver. Note that the + * 102ND key is not used for US keyboards. + */ + linux,keymap = < + /* CAPSLCK F1 B F10 */ + 0x0001003a 0x0002003c 0x00030030 0x00040044 + /* N = R_ALT ESC */ + 0x00060031 0x0008000d 0x000a0064 0x01010001 + /* F4 G F7 H */ + 0x0102003e 0x01030022 0x01040041 0x01060023 + /* ' F9 BKSPACE L_CTRL */ + 0x01080028 0x01090043 0x010b000e 0x0200001d + /* TAB F3 T F6 */ + 0x0201000f 0x0202003d 0x02030014 0x02040040 + /* ] Y 102ND [ */ + 0x0205001b 0x02060015 0x02070056 0x0208001a + /* F8 GRAVE F2 5 */ + 0x02090042 0x03010029 0x0302003c 0x03030006 + /* F5 6 - \ */ + 0x0304003f 0x03060007 0x0308000c 0x030b002b + /* R_CTRL A D F */ + 0x04000061 0x0401001e 0x04020020 0x04030021 + /* S K J ; */ + 0x0404001f 0x04050025 0x04060024 0x04080027 + /* L ENTER Z C */ + 0x04090026 0x040b001c 0x0501002c 0x0502002e + /* V X , M */ + 0x0503002f 0x0504002d 0x05050033 0x05060032 + /* L_SHIFT / . SPACE */ + 0x0507002a 0x05080035 0x05090034 0x050B0039 + /* 1 3 4 2 */ + 0x06010002 0x06020004 0x06030005 0x06040003 + /* 8 7 0 9 */ + 0x06050009 0x06060008 0x0608000b 0x0609000a + /* L_ALT DOWN RIGHT Q */ + 0x060a0038 0x060b006c 0x060c006a 0x07010010 + /* E R W I */ + 0x07020012 0x07030013 0x07040011 0x07050017 + /* U R_SHIFT P O */ + 0x07060016 0x07070036 0x07080019 0x07090018 + /* UP LEFT */ + 0x070b0067 0x070c0069>; +}; diff --git a/doc/device-tree-bindings/misc/cros-ec.txt b/doc/device-tree-bindings/misc/cros-ec.txt new file mode 100644 index 0000000000..07ea7cdeac --- /dev/null +++ b/doc/device-tree-bindings/misc/cros-ec.txt @@ -0,0 +1,38 @@ +Chrome OS CROS_EC Binding +====================== + +The device tree node which describes the operation of the CROS_EC interface +is as follows: + +Required properties : +- compatible = "google,cros-ec" + +Optional properties : +- spi-max-frequency : Sets the maximum frequency (in Hz) for SPI bus + operation +- i2c-max-frequency : Sets the maximum frequency (in Hz) for I2C bus + operation +- ec-interrupt : Selects the EC interrupt, defined as a GPIO according + to the platform +- optimise-flash-write : Boolean property - if present then flash blocks + containing all 0xff will not be written, since we assume that the EC + uses that pattern for erased blocks + +The CROS_EC node should appear as a subnode of the interrupt that connects it +to the EC (e.g. i2c, spi, lpc). The reg property (as usual) will indicate +the unit address on that bus. + + +Example +======= + + spi@131b0000 { + cros-ec@0 { + reg = <0>; + compatible = "google,cros-ec"; + spi-max-frequency = <5000000>; + ec-interrupt = <&gpio 174 1>; + optimise-flash-write; + status = "disabled"; + }; + }; diff --git a/doc/mkimage.1 b/doc/mkimage.1 index 39652c82d0..14374da88a 100644 --- a/doc/mkimage.1 +++ b/doc/mkimage.1 @@ -4,7 +4,17 @@ mkimage \- Generate image for U-Boot .SH SYNOPSIS .B mkimage -.RB [\fIoptions\fP] +.RB "\-l [" "uimage file name" "]" + +.B mkimage +.RB [\fIoptions\fP] " \-f [" "image tree source file" "]" " [" "uimage file name" "]" + +.B mkimage +.RB [\fIoptions\fP] " \-F [" "uimage file name" "]" + +.B mkimage +.RB [\fIoptions\fP] " (legacy mode)" + .SH "DESCRIPTION" The .B mkimage @@ -26,7 +36,8 @@ etc. The new .I FIT (Flattened Image Tree) format allows for more flexibility in handling images of various types and also -enhances integrity protection of images with stronger checksums. +enhances integrity protection of images with stronger checksums. It also +supports verified boot. .SH "OPTIONS" @@ -67,6 +78,10 @@ Set load address with a hex number. Set entry point with a hex number. .TP +.BI "\-l" +List the contents of an image. + +.TP .BI "\-n [" "image name" "]" Set image name to 'image name'. @@ -82,6 +97,12 @@ Set XIP (execute in place) flag. .B Create FIT image: .TP +.BI "\-c [" "comment" "]" +Specifies a comment to be added when signing. This is typically a useful +message which describes how the image was signed or some other useful +information. + +.TP .BI "\-D [" "dtc options" "]" Provide special options to the device tree compiler that is used to create the image. @@ -91,6 +112,33 @@ create the image. Image tree source file that describes the structure and contents of the FIT image. +.TP +.BI "\-F" +Indicates that an existing FIT image should be modified. No dtc +compilation is performed and the -f flag should not be given. +This can be used to sign images with additional keys after initial image +creation. + +.TP +.BI "\-k [" "key_directory" "]" +Specifies the directory containing keys to use for signing. This directory +should contain a private key file <name>.key for use with signing and a +certificate <name>.crt (containing the public key) for use with verification. + +.TP +.BI "\-K [" "key_destination" "]" +Specifies a compiled device tree binary file (typically .dtb) to write +public key information into. When a private key is used to sign an image, +the corresponding public key is written into this file for for run-time +verification. Typically the file here is the device tree binary used by +CONFIG_OF_CONTROL in U-Boot. + +.TP +.BI "\-r +Specifies that keys used to sign the FIT are required. This means that they +must be verified for the image to boot. Without this option, the verification +will be optional (useful for testing but not for release). + .SH EXAMPLES List image information: @@ -109,10 +157,29 @@ Create FIT image with compressed PowerPC Linux kernel: .nf .B mkimage -f kernel.its kernel.itb .fi +.P +Create FIT image with compressed kernel and sign it with keys in the +/public/signing-keys directory. Add corresponding public keys into u-boot.dtb, +skipping those for which keys cannot be found. Also add a comment. +.nf +.B mkimage -f kernel.its -k /public/signing-keys -K u-boot.dtb \\\\ +-c "Kernel 3.8 image for production devices" kernel.itb +.fi + +.P +Update an existing FIT image, signing it with additional keys. +Add corresponding public keys into u-boot.dtb. This will resign all images +with keys that are available in the new directory. Images that request signing +with unavailable keys are skipped. +.nf +.B mkimage -F -k /secret/signing-keys -K u-boot.dtb \\\\ +-c "Kernel 3.8 image for production devices" kernel.itb +.fi .SH HOMEPAGE http://www.denx.de/wiki/U-Boot/WebHome .PP .SH AUTHOR This manual page was written by Nobuhiro Iwamatsu <iwamatsu@nigauri.org> -and Wolfgang Denk <wd@denx.de> +and Wolfgang Denk <wd@denx.de>. It was updated for image signing by +Simon Glass <sjg@chromium.org>. diff --git a/doc/uImage.FIT/sign-configs.its b/doc/uImage.FIT/sign-configs.its new file mode 100644 index 0000000000..3c17f040de --- /dev/null +++ b/doc/uImage.FIT/sign-configs.its @@ -0,0 +1,45 @@ +/dts-v1/; + +/ { + description = "Chrome OS kernel image with one or more FDT blobs"; + #address-cells = <1>; + + images { + kernel@1 { + data = /incbin/("test-kernel.bin"); + type = "kernel_noload"; + arch = "sandbox"; + os = "linux"; + compression = "lzo"; + load = <0x4>; + entry = <0x8>; + kernel-version = <1>; + hash@1 { + algo = "sha1"; + }; + }; + fdt@1 { + description = "snow"; + data = /incbin/("sandbox-kernel.dtb"); + type = "flat_dt"; + arch = "sandbox"; + compression = "none"; + fdt-version = <1>; + hash@1 { + algo = "sha1"; + }; + }; + }; + configurations { + default = "conf@1"; + conf@1 { + kernel = "kernel@1"; + fdt = "fdt@1"; + signature@1 { + algo = "sha1,rsa2048"; + key-name-hint = "dev"; + sign-images = "fdt", "kernel"; + }; + }; + }; +}; diff --git a/doc/uImage.FIT/sign-images.its b/doc/uImage.FIT/sign-images.its new file mode 100644 index 0000000000..f69326a39b --- /dev/null +++ b/doc/uImage.FIT/sign-images.its @@ -0,0 +1,42 @@ +/dts-v1/; + +/ { + description = "Chrome OS kernel image with one or more FDT blobs"; + #address-cells = <1>; + + images { + kernel@1 { + data = /incbin/("test-kernel.bin"); + type = "kernel_noload"; + arch = "sandbox"; + os = "linux"; + compression = "none"; + load = <0x4>; + entry = <0x8>; + kernel-version = <1>; + signature@1 { + algo = "sha1,rsa2048"; + key-name-hint = "dev"; + }; + }; + fdt@1 { + description = "snow"; + data = /incbin/("sandbox-kernel.dtb"); + type = "flat_dt"; + arch = "sandbox"; + compression = "none"; + fdt-version = <1>; + signature@1 { + algo = "sha1,rsa2048"; + key-name-hint = "dev"; + }; + }; + }; + configurations { + default = "conf@1"; + conf@1 { + kernel = "kernel@1"; + fdt = "fdt@1"; + }; + }; +}; diff --git a/doc/uImage.FIT/signature.txt b/doc/uImage.FIT/signature.txt new file mode 100644 index 0000000000..bc9f3fa6e1 --- /dev/null +++ b/doc/uImage.FIT/signature.txt @@ -0,0 +1,382 @@ +U-Boot FIT Signature Verification +================================= + +Introduction +------------ +FIT supports hashing of images so that these hashes can be checked on +loading. This protects against corruption of the image. However it does not +prevent the substitution of one image for another. + +The signature feature allows the hash to be signed with a private key such +that it can be verified using a public key later. Provided that the private +key is kept secret and the public key is stored in a non-volatile place, +any image can be verified in this way. + +See verified-boot.txt for more general information on verified boot. + + +Concepts +-------- +Some familiarity with public key cryptography is assumed in this section. + +The procedure for signing is as follows: + + - hash an image in the FIT + - sign the hash with a private key to produce a signature + - store the resulting signature in the FIT + +The procedure for verification is: + + - read the FIT + - obtain the public key + - extract the signature from the FIT + - hash the image from the FIT + - verify (with the public key) that the extracted signature matches the + hash + +The signing is generally performed by mkimage, as part of making a firmware +image for the device. The verification is normally done in U-Boot on the +device. + + +Algorithms +---------- +In principle any suitable algorithm can be used to sign and verify a hash. +At present only one class of algorithms is supported: SHA1 hashing with RSA. +This works by hashing the image to produce a 20-byte hash. + +While it is acceptable to bring in large cryptographic libraries such as +openssl on the host side (e.g. mkimage), it is not desirable for U-Boot. +For the run-time verification side, it is important to keep code and data +size as small as possible. + +For this reason the RSA image verification uses pre-processed public keys +which can be used with a very small amount of code - just some extraction +of data from the FDT and exponentiation mod n. Code size impact is a little +under 5KB on Tegra Seaboard, for example. + +It is relatively straightforward to add new algorithms if required. If +another RSA variant is needed, then it can be added to the table in +image-sig.c. If another algorithm is needed (such as DSA) then it can be +placed alongside rsa.c, and its functions added to the table in image-sig.c +also. + + +Creating an RSA key and certificate +----------------------------------- +To create a new public key, size 2048 bits: + +$ openssl genrsa -F4 -out keys/dev.key 2048 + +To create a certificate for this: + +$ openssl req -batch -new -x509 -key keys/dev.key -out keys/dev.crt + +If you like you can look at the public key also: + +$ openssl rsa -in keys/dev.key -pubout + + +Device Tree Bindings +-------------------- +The following properties are required in the FIT's signature node(s) to +allow thes signer to operate. These should be added to the .its file. +Signature nodes sit at the same level as hash nodes and are called +signature@1, signature@2, etc. + +- algo: Algorithm name (e.g. "sha1,rs2048") + +- key-name-hint: Name of key to use for signing. The keys will normally be in +a single directory (parameter -k to mkimage). For a given key <name>, its +private key is stored in <name>.key and the certificate is stored in +<name>.crt. + +When the image is signed, the following properties are added (mandatory): + +- value: The signature data (e.g. 256 bytes for 2048-bit RSA) + +When the image is signed, the following properties are optional: + +- timestamp: Time when image was signed (standard Unix time_t format) + +- signer-name: Name of the signer (e.g. "mkimage") + +- signer-version: Version string of the signer (e.g. "2013.01") + +- comment: Additional information about the signer or image + +For config bindings (see Signed Configurations below), the following +additional properties are optional: + +- sign-images: A list of images to sign, each being a property of the conf +node that contains then. The default is "kernel,fdt" which means that these +two images will be looked up in the config and signed if present. + +For config bindings, these properties are added by the signer: + +- hashed-nodes: A list of nodes which were hashed by the signer. Each is + a string - the full path to node. A typical value might be: + + hashed-nodes = "/", "/configurations/conf@1", "/images/kernel@1", + "/images/kernel@1/hash@1", "/images/fdt@1", + "/images/fdt@1/hash@1"; + +- hashed-strings: The start and size of the string region of the FIT that + was hashed + +Example: See sign-images.its for an example image tree source file and +sign-configs.its for config signing. + + +Public Key Storage +------------------ +In order to verify an image that has been signed with a public key we need to +have a trusted public key. This cannot be stored in the signed image, since +it would be easy to alter. For this implementation we choose to store the +public key in U-Boot's control FDT (using CONFIG_OF_CONTROL). + +Public keys should be stored as sub-nodes in a /signature node. Required +properties are: + +- algo: Algorithm name (e.g. "sha1,rs2048") + +Optional properties are: + +- key-name-hint: Name of key used for signing. This is only a hint since it +is possible for the name to be changed. Verification can proceed by checking +all available signing keys until one matches. + +- required: If present this indicates that the key must be verified for the +image / configuration to be considered valid. Only required keys are +normally verified by the FIT image booting algorithm. Valid values are +"image" to force verification of all images, and "conf" to force verfication +of the selected configuration (which then relies on hashes in the images to +verify those). + +Each signing algorithm has its own additional properties. + +For RSA the following are mandatory: + +- rsa,num-bits: Number of key bits (e.g. 2048) +- rsa,modulus: Modulus (N) as a big-endian multi-word integer +- rsa,r-squared: (2^num-bits)^2 as a big-endian multi-word integer +- rsa,n0-inverse: -1 / modulus[0] mod 2^32 + + +Signed Configurations +--------------------- +While signing images is useful, it does not provide complete protection +against several types of attack. For example, it it possible to create a +FIT with the same signed images, but with the configuration changed such +that a different one is selected (mix and match attack). It is also possible +to substitute a signed image from an older FIT version into a newer FIT +(roll-back attack). + +As an example, consider this FIT: + +/ { + images { + kernel@1 { + data = <data for kernel1> + signature@1 { + algo = "sha1,rsa2048"; + value = <...kernel signature 1...> + }; + }; + kernel@2 { + data = <data for kernel2> + signature@1 { + algo = "sha1,rsa2048"; + value = <...kernel signature 2...> + }; + }; + fdt@1 { + data = <data for fdt1>; + signature@1 { + algo = "sha1,rsa2048"; + vaue = <...fdt signature 1...> + }; + }; + fdt@2 { + data = <data for fdt2>; + signature@1 { + algo = "sha1,rsa2048"; + vaue = <...fdt signature 2...> + }; + }; + }; + configurations { + default = "conf@1"; + conf@1 { + kernel = "kernel@1"; + fdt = "fdt@1"; + }; + conf@1 { + kernel = "kernel@2"; + fdt = "fdt@2"; + }; + }; +}; + +Since both kernels are signed it is easy for an attacker to add a new +configuration 3 with kernel 1 and fdt 2: + + configurations { + default = "conf@1"; + conf@1 { + kernel = "kernel@1"; + fdt = "fdt@1"; + }; + conf@1 { + kernel = "kernel@2"; + fdt = "fdt@2"; + }; + conf@3 { + kernel = "kernel@1"; + fdt = "fdt@2"; + }; + }; + +With signed images, nothing protects against this. Whether it gains an +advantage for the attacker is debatable, but it is not secure. + +To solved this problem, we support signed configurations. In this case it +is the configurations that are signed, not the image. Each image has its +own hash, and we include the hash in the configuration signature. + +So the above example is adjusted to look like this: + +/ { + images { + kernel@1 { + data = <data for kernel1> + hash@1 { + algo = "sha1"; + value = <...kernel hash 1...> + }; + }; + kernel@2 { + data = <data for kernel2> + hash@1 { + algo = "sha1"; + value = <...kernel hash 2...> + }; + }; + fdt@1 { + data = <data for fdt1>; + hash@1 { + algo = "sha1"; + value = <...fdt hash 1...> + }; + }; + fdt@2 { + data = <data for fdt2>; + hash@1 { + algo = "sha1"; + value = <...fdt hash 2...> + }; + }; + }; + configurations { + default = "conf@1"; + conf@1 { + kernel = "kernel@1"; + fdt = "fdt@1"; + signature@1 { + algo = "sha1,rsa2048"; + value = <...conf 1 signature...>; + }; + }; + conf@2 { + kernel = "kernel@2"; + fdt = "fdt@2"; + signature@1 { + algo = "sha1,rsa2048"; + value = <...conf 1 signature...>; + }; + }; + }; +}; + + +You can see that we have added hashes for all images (since they are no +longer signed), and a signature to each configuration. In the above example, +mkimage will sign configurations/conf@1, the kernel and fdt that are +pointed to by the configuration (/images/kernel@1, /images/kernel@1/hash@1, +/images/fdt@1, /images/fdt@1/hash@1) and the root structure of the image +(so that it isn't possible to add or remove root nodes). The signature is +written into /configurations/conf@1/signature@1/value. It can easily be +verified later even if the FIT has been signed with other keys in the +meantime. + + +Verification +------------ +FITs are verified when loaded. After the configuration is selected a list +of required images is produced. If there are 'required' public keys, then +each image must be verified against those keys. This means that every image +that might be used by the target needs to be signed with 'required' keys. + +This happens automatically as part of a bootm command when FITs are used. + + +Enabling FIT Verification +------------------------- +In addition to the options to enable FIT itself, the following CONFIGs must +be enabled: + +CONFIG_FIT_SIGNATURE - enable signing and verfication in FITs +CONFIG_RSA - enable RSA algorithm for signing + + +Testing +------- +An easy way to test signing and verfication is to use the test script +provided in test/vboot/vboot_test.sh. This uses sandbox (a special version +of U-Boot which runs under Linux) to show the operation of a 'bootm' +command loading and verifying images. + +A sample run is show below: + +$ make O=sandbox sandbox_config +$ make O=sandbox +$ O=sandbox ./test/vboot/vboot_test.sh +Simple Verified Boot Test +========================= + +Please see doc/uImage.FIT/verified-boot.txt for more information + +Build keys +Build FIT with signed images +Test Verified Boot Run: unsigned signatures:: OK +Sign images +Test Verified Boot Run: signed images: OK +Build FIT with signed configuration +Test Verified Boot Run: unsigned config: OK +Sign images +Test Verified Boot Run: signed config: OK + +Test passed + + +Future Work +----------- +- Roll-back protection using a TPM is done using the tpm command. This can +be scripted, but we might consider a default way of doing this, built into +bootm. + + +Possible Future Work +-------------------- +- Add support for other RSA/SHA variants, such as rsa4096,sha512. +- Other algorithms besides RSA +- More sandbox tests for failure modes +- Passwords for keys/certificates +- Perhaps implement OAEP +- Enhance bootm to permit scripted signature verification (so that a script +can verify an image but not actually boot it) + + +Simon Glass +sjg@chromium.org +1-1-13 diff --git a/doc/uImage.FIT/verified-boot.txt b/doc/uImage.FIT/verified-boot.txt new file mode 100644 index 0000000000..3c83fbc2c1 --- /dev/null +++ b/doc/uImage.FIT/verified-boot.txt @@ -0,0 +1,104 @@ +U-Boot Verified Boot +==================== + +Introduction +------------ +Verified boot here means the verification of all software loaded into a +machine during the boot process to ensure that it is authorised and correct +for that machine. + +Verified boot extends from the moment of system reset to as far as you wish +into the boot process. An example might be loading U-Boot from read-only +memory, then loading a signed kernel, then using the kernel's dm-verity +driver to mount a signed root filesystem. + +A key point is that it is possible to field-upgrade the software on machines +which use verified boot. Since the machine will only run software that has +been correctly signed, it is safe to read software from an updatable medium. +It is also possible to add a secondary signed firmware image, in read-write +memory, so that firmware can easily be upgraded in a secure manner. + + +Signing +------- +Verified boot uses cryptographic algorithms to 'sign' software images. +Images are signed using a private key known only to the signer, but can +be verified using a public key. As its name suggests the public key can be +made available without risk to the verification process. The private and +public keys are mathematically related. For more information on how this +works look up "public key cryptography" and "RSA" (a particular algorithm). + +The signing and verification process looks something like this: + + + Signing Verification + ======= ============ + + +--------------+ * + | RSA key pair | * +---------------+ + | .key .crt | * | Public key in | + +--------------+ +------> public key ----->| trusted place | + | | * +---------------+ + | | * | + v | * v + +---------+ | * +--------------+ + | |----------+ * | | + | signer | * | U-Boot | + | |----------+ * | signature |--> yes/no + +---------+ | * | verification | + ^ | * | | + | | * +--------------+ + | | * ^ + +----------+ | * | + | Software | +----> signed image -------------+ + | image | * + +----------+ * + + +The signature algorithm relies only on the public key to do its work. Using +this key it checks the signature that it finds in the image. If it verifies +then we know that the image is OK. + +The public key from the signer allows us to verify and therefore trust +software from updatable memory. + +It is critical that the public key be secure and cannot be tampered with. +It can be stored in read-only memory, or perhaps protected by other on-chip +crypto provided by some modern SOCs. If the public key can ben changed, then +the verification is worthless. + + +Chaining Images +--------------- +The above method works for a signer providing images to a run-time U-Boot. +It is also possible to extend this scheme to a second level, like this: + +1. Master private key is used by the signer to sign a first-stage image. +2. Master public key is placed in read-only memory. +2. Secondary private key is created and used to sign second-stage images. +3. Secondary public key is placed in first stage images +4. We use the master public key to verify the first-stage image. We then +use the secondary public key in the first-stage image to verify the second- +state image. +5. This chaining process can go on indefinitely. It is recommended to use a +different key at each stage, so that a compromise in one place will not +affect the whole change. + + +Flattened Image Tree (FIT) +-------------------------- +The FIT format is alreay widely used in U-Boot. It is a flattened device +tree (FDT) in a particular format, with images contained within. FITs +include hashes to verify images, so it is relatively straightforward to +add signatures as well. + +The public key can be stored in U-Boot's CONFIG_OF_CONTROL device tree in +a standard place. Then when a FIT it loaded it can be verified using that +public key. Multiple keys and multiple signatures are supported. + +See signature.txt for more information. + + +Simon Glass +sjg@chromium.org +1-1-13 diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index f3adf64859..bb8e644cd9 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -133,8 +133,7 @@ int dtt_init_one(int sensor) /* * Setup PWM Lookup-Table */ - for (i = 0; i < sizeof(pwm_lookup) / sizeof(struct pwm_lookup_entry); - i++) { + for (i = 0; i < ARRAY_SIZE(pwm_lookup); i++) { int address = DTT_PWM_LOOKUP_BASE + 2 * i; val = pwm_lookup[i].temp; if (is_lm64(sensor)) diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 0805e86678..4331190def 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -27,6 +27,7 @@ LIB := $(obj)libinput.o COBJS-$(CONFIG_I8042_KBD) += i8042.o COBJS-$(CONFIG_TEGRA_KEYBOARD) += tegra-kbc.o +COBJS-$(CONFIG_CROS_EC_KEYB) += cros_ec_keyb.o ifdef CONFIG_PS2KBD COBJS-y += keyboard.o pc_keyb.o COBJS-$(CONFIG_PS2MULT) += ps2mult.o ps2ser.o diff --git a/drivers/input/cros_ec_keyb.c b/drivers/input/cros_ec_keyb.c new file mode 100644 index 0000000000..c197308221 --- /dev/null +++ b/drivers/input/cros_ec_keyb.c @@ -0,0 +1,261 @@ +/* + * Chromium OS Matrix Keyboard + * + * Copyright (c) 2012 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <cros_ec.h> +#include <fdtdec.h> +#include <input.h> +#include <key_matrix.h> +#include <stdio_dev.h> + +DECLARE_GLOBAL_DATA_PTR; + +enum { + KBC_MAX_KEYS = 8, /* Maximum keys held down at once */ +}; + +static struct keyb { + struct cros_ec_dev *dev; /* The CROS_EC device */ + struct input_config input; /* The input layer */ + struct key_matrix matrix; /* The key matrix layer */ + int key_rows; /* Number of keyboard rows */ + int key_cols; /* Number of keyboard columns */ + unsigned int repeat_delay_ms; /* Time before autorepeat starts */ + unsigned int repeat_rate_ms; /* Autorepeat rate in ms */ + int ghost_filter; /* 1 to enable ghost filter, else 0 */ + int inited; /* 1 if keyboard is ready */ +} config; + + +/** + * Check the keyboard controller and return a list of key matrix positions + * for which a key is pressed + * + * @param config Keyboard config + * @param keys List of keys that we have detected + * @param max_count Maximum number of keys to return + * @return number of pressed keys, 0 for none + */ +static int check_for_keys(struct keyb *config, + struct key_matrix_key *keys, int max_count) +{ + struct key_matrix_key *key; + struct mbkp_keyscan scan; + unsigned int row, col, bit, data; + int num_keys; + + if (cros_ec_scan_keyboard(config->dev, &scan)) { + debug("%s: keyboard scan failed\n", __func__); + return -1; + } + + for (col = num_keys = bit = 0; col < config->matrix.num_cols; + col++) { + for (row = 0; row < config->matrix.num_rows; row++) { + unsigned int mask = 1 << (bit & 7); + + data = scan.data[bit / 8]; + if ((data & mask) && num_keys < max_count) { + key = keys + num_keys++; + key->row = row; + key->col = col; + key->valid = 1; + } + bit++; + } + } + + return num_keys; +} + +/** + * Test if keys are available to be read + * + * @return 0 if no keys available, 1 if keys are available + */ +static int kbd_tstc(void) +{ + /* Just get input to do this for us */ + return config.inited ? input_tstc(&config.input) : 0; +} + +/** + * Read a key + * + * @return ASCII key code, or 0 if no key, or -1 if error + */ +static int kbd_getc(void) +{ + /* Just get input to do this for us */ + return config.inited ? input_getc(&config.input) : 0; +} + +/** + * Check the keyboard, and send any keys that are pressed. + * + * This is called by input_tstc() and input_getc() when they need more + * characters + * + * @param input Input configuration + * @return 1, to indicate that we have something to look at + */ +int cros_ec_kbc_check(struct input_config *input) +{ + static struct key_matrix_key last_keys[KBC_MAX_KEYS]; + static int last_num_keys; + struct key_matrix_key keys[KBC_MAX_KEYS]; + int keycodes[KBC_MAX_KEYS]; + int num_keys, num_keycodes; + int irq_pending, sent; + + /* + * Loop until the EC has no more keyscan records, or we have + * received at least one character. This means we know that tstc() + * will always return non-zero if keys have been pressed. + * + * Without this loop, a key release (which generates no new ascii + * characters) will cause us to exit this function, and just tstc() + * may return 0 before all keys have been read from the EC. + */ + do { + irq_pending = cros_ec_interrupt_pending(config.dev); + if (irq_pending) { + num_keys = check_for_keys(&config, keys, KBC_MAX_KEYS); + last_num_keys = num_keys; + memcpy(last_keys, keys, sizeof(keys)); + } else { + /* + * EC doesn't want to be asked, so use keys from last + * time. + */ + num_keys = last_num_keys; + memcpy(keys, last_keys, sizeof(keys)); + } + + if (num_keys < 0) + return -1; + num_keycodes = key_matrix_decode(&config.matrix, keys, + num_keys, keycodes, KBC_MAX_KEYS); + sent = input_send_keycodes(input, keycodes, num_keycodes); + } while (irq_pending && !sent); + + return 1; +} + +/** + * Decode MBKP keyboard details from the device tree + * + * @param blob Device tree blob + * @param node Node to decode from + * @param config Configuration data read from fdt + * @return 0 if ok, -1 on error + */ +static int cros_ec_keyb_decode_fdt(const void *blob, int node, + struct keyb *config) +{ + /* + * Get keyboard rows and columns - at present we are limited to + * 8 columns by the protocol (one byte per row scan) + */ + config->key_rows = fdtdec_get_int(blob, node, "google,key-rows", 0); + config->key_cols = fdtdec_get_int(blob, node, "google,key-columns", 0); + if (!config->key_rows || !config->key_cols || + config->key_rows * config->key_cols / 8 + > CROS_EC_KEYSCAN_COLS) { + debug("%s: Invalid key matrix size %d x %d\n", __func__, + config->key_rows, config->key_cols); + return -1; + } + config->repeat_delay_ms = fdtdec_get_int(blob, node, + "google,repeat-delay-ms", 0); + config->repeat_rate_ms = fdtdec_get_int(blob, node, + "google,repeat-rate-ms", 0); + config->ghost_filter = fdtdec_get_bool(blob, node, + "google,ghost-filter"); + return 0; +} + +/** + * Set up the keyboard. This is called by the stdio device handler. + * + * We want to do this init when the keyboard is actually used rather than + * at start-up, since keyboard input may not currently be selected. + * + * @return 0 if ok, -1 on error + */ +static int cros_ec_init_keyboard(void) +{ + const void *blob = gd->fdt_blob; + int node; + + config.dev = board_get_cros_ec_dev(); + if (!config.dev) { + debug("%s: no cros_ec device: cannot init keyboard\n", + __func__); + return -1; + } + node = fdtdec_next_compatible(blob, 0, COMPAT_GOOGLE_CROS_EC_KEYB); + if (node < 0) { + debug("%s: Node not found\n", __func__); + return -1; + } + if (cros_ec_keyb_decode_fdt(blob, node, &config)) + return -1; + input_set_delays(&config.input, config.repeat_delay_ms, + config.repeat_rate_ms); + if (key_matrix_init(&config.matrix, config.key_rows, + config.key_cols, config.ghost_filter)) { + debug("%s: cannot init key matrix\n", __func__); + return -1; + } + if (key_matrix_decode_fdt(&config.matrix, gd->fdt_blob, node)) { + debug("%s: Could not decode key matrix from fdt\n", __func__); + return -1; + } + config.inited = 1; + debug("%s: Matrix keyboard %dx%d ready\n", __func__, config.key_rows, + config.key_cols); + + return 0; +} + +int drv_keyboard_init(void) +{ + struct stdio_dev dev; + + if (input_init(&config.input, 0)) { + debug("%s: Cannot set up input\n", __func__); + return -1; + } + config.input.read_keys = cros_ec_kbc_check; + + memset(&dev, '\0', sizeof(dev)); + strcpy(dev.name, "cros-ec-keyb"); + dev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; + dev.getc = kbd_getc; + dev.tstc = kbd_tstc; + dev.start = cros_ec_init_keyboard; + + /* Register the device. cros_ec_init_keyboard() will be called soon */ + return input_stdio_register(&dev); +} diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 5d869b47ad..5fbff8ab6b 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -28,6 +28,10 @@ LIB := $(obj)libmisc.o COBJS-$(CONFIG_ALI152X) += ali512x.o COBJS-$(CONFIG_DS4510) += ds4510.o COBJS-$(CONFIG_CBMEM_CONSOLE) += cbmem_console.o +COBJS-$(CONFIG_CROS_EC) += cros_ec.o +COBJS-$(CONFIG_CROS_EC_LPC) += cros_ec_lpc.o +COBJS-$(CONFIG_CROS_EC_I2C) += cros_ec_i2c.o +COBJS-$(CONFIG_CROS_EC_SPI) += cros_ec_spi.o COBJS-$(CONFIG_FSL_IIM) += fsl_iim.o COBJS-$(CONFIG_GPIO_LED) += gpio_led.o COBJS-$(CONFIG_FSL_MC9SDZ60) += mc9sdz60.o diff --git a/drivers/misc/cros_ec.c b/drivers/misc/cros_ec.c new file mode 100644 index 0000000000..6e774d921b --- /dev/null +++ b/drivers/misc/cros_ec.c @@ -0,0 +1,1304 @@ +/* + * Chromium OS cros_ec driver + * + * Copyright (c) 2012 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * The Matrix Keyboard Protocol driver handles talking to the keyboard + * controller chip. Mostly this is for keyboard functions, but some other + * things have slipped in, so we provide generic services to talk to the + * KBC. + */ + +#include <common.h> +#include <command.h> +#include <i2c.h> +#include <cros_ec.h> +#include <fdtdec.h> +#include <malloc.h> +#include <spi.h> +#include <asm/io.h> +#include <asm-generic/gpio.h> + +#ifdef DEBUG_TRACE +#define debug_trace(fmt, b...) debug(fmt, #b) +#else +#define debug_trace(fmt, b...) +#endif + +enum { + /* Timeout waiting for a flash erase command to complete */ + CROS_EC_CMD_TIMEOUT_MS = 5000, + /* Timeout waiting for a synchronous hash to be recomputed */ + CROS_EC_CMD_HASH_TIMEOUT_MS = 2000, +}; + +static struct cros_ec_dev static_dev, *last_dev; + +DECLARE_GLOBAL_DATA_PTR; + +/* Note: depends on enum ec_current_image */ +static const char * const ec_current_image_name[] = {"unknown", "RO", "RW"}; + +void cros_ec_dump_data(const char *name, int cmd, const uint8_t *data, int len) +{ +#ifdef DEBUG + int i; + + printf("%s: ", name); + if (cmd != -1) + printf("cmd=%#x: ", cmd); + for (i = 0; i < len; i++) + printf("%02x ", data[i]); + printf("\n"); +#endif +} + +/* + * Calculate a simple 8-bit checksum of a data block + * + * @param data Data block to checksum + * @param size Size of data block in bytes + * @return checksum value (0 to 255) + */ +int cros_ec_calc_checksum(const uint8_t *data, int size) +{ + int csum, i; + + for (i = csum = 0; i < size; i++) + csum += data[i]; + return csum & 0xff; +} + +static int send_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, + const void *dout, int dout_len, + uint8_t **dinp, int din_len) +{ + int ret; + + switch (dev->interface) { +#ifdef CONFIG_CROS_EC_SPI + case CROS_EC_IF_SPI: + ret = cros_ec_spi_command(dev, cmd, cmd_version, + (const uint8_t *)dout, dout_len, + dinp, din_len); + break; +#endif +#ifdef CONFIG_CROS_EC_I2C + case CROS_EC_IF_I2C: + ret = cros_ec_i2c_command(dev, cmd, cmd_version, + (const uint8_t *)dout, dout_len, + dinp, din_len); + break; +#endif +#ifdef CONFIG_CROS_EC_LPC + case CROS_EC_IF_LPC: + ret = cros_ec_lpc_command(dev, cmd, cmd_version, + (const uint8_t *)dout, dout_len, + dinp, din_len); + break; +#endif + case CROS_EC_IF_NONE: + default: + ret = -1; + } + + return ret; +} + +/** + * Send a command to the CROS-EC device and return the reply. + * + * The device's internal input/output buffers are used. + * + * @param dev CROS-EC device + * @param cmd Command to send (EC_CMD_...) + * @param cmd_version Version of command to send (EC_VER_...) + * @param dout Output data (may be NULL If dout_len=0) + * @param dout_len Size of output data in bytes + * @param dinp Response data (may be NULL If din_len=0). + * If not NULL, it will be updated to point to the data + * and will always be double word aligned (64-bits) + * @param din_len Maximum size of response in bytes + * @return number of bytes in response, or -1 on error + */ +static int ec_command_inptr(struct cros_ec_dev *dev, uint8_t cmd, + int cmd_version, const void *dout, int dout_len, uint8_t **dinp, + int din_len) +{ + uint8_t *din; + int len; + + if (cmd_version != 0 && !dev->cmd_version_is_supported) { + debug("%s: Command version >0 unsupported\n", __func__); + return -1; + } + len = send_command(dev, cmd, cmd_version, dout, dout_len, + &din, din_len); + + /* If the command doesn't complete, wait a while */ + if (len == -EC_RES_IN_PROGRESS) { + struct ec_response_get_comms_status *resp; + ulong start; + + /* Wait for command to complete */ + start = get_timer(0); + do { + int ret; + + mdelay(50); /* Insert some reasonable delay */ + ret = send_command(dev, EC_CMD_GET_COMMS_STATUS, 0, + NULL, 0, + (uint8_t **)&resp, sizeof(*resp)); + if (ret < 0) + return ret; + + if (get_timer(start) > CROS_EC_CMD_TIMEOUT_MS) { + debug("%s: Command %#02x timeout\n", + __func__, cmd); + return -EC_RES_TIMEOUT; + } + } while (resp->flags & EC_COMMS_STATUS_PROCESSING); + + /* OK it completed, so read the status response */ + /* not sure why it was 0 for the last argument */ + len = send_command(dev, EC_CMD_RESEND_RESPONSE, 0, + NULL, 0, &din, din_len); + } + + debug("%s: len=%d, dinp=%p, *dinp=%p\n", __func__, len, dinp, *dinp); + if (dinp) { + /* If we have any data to return, it must be 64bit-aligned */ + assert(len <= 0 || !((uintptr_t)din & 7)); + *dinp = din; + } + + return len; +} + +/** + * Send a command to the CROS-EC device and return the reply. + * + * The device's internal input/output buffers are used. + * + * @param dev CROS-EC device + * @param cmd Command to send (EC_CMD_...) + * @param cmd_version Version of command to send (EC_VER_...) + * @param dout Output data (may be NULL If dout_len=0) + * @param dout_len Size of output data in bytes + * @param din Response data (may be NULL If din_len=0). + * It not NULL, it is a place for ec_command() to copy the + * data to. + * @param din_len Maximum size of response in bytes + * @return number of bytes in response, or -1 on error + */ +static int ec_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, + const void *dout, int dout_len, + void *din, int din_len) +{ + uint8_t *in_buffer; + int len; + + assert((din_len == 0) || din); + len = ec_command_inptr(dev, cmd, cmd_version, dout, dout_len, + &in_buffer, din_len); + if (len > 0) { + /* + * If we were asked to put it somewhere, do so, otherwise just + * disregard the result. + */ + if (din && in_buffer) { + assert(len <= din_len); + memmove(din, in_buffer, len); + } + } + return len; +} + +int cros_ec_scan_keyboard(struct cros_ec_dev *dev, struct mbkp_keyscan *scan) +{ + if (ec_command(dev, EC_CMD_CROS_EC_STATE, 0, NULL, 0, scan, + sizeof(scan->data)) < sizeof(scan->data)) + return -1; + + return 0; +} + +int cros_ec_read_id(struct cros_ec_dev *dev, char *id, int maxlen) +{ + struct ec_response_get_version *r; + + if (ec_command_inptr(dev, EC_CMD_GET_VERSION, 0, NULL, 0, + (uint8_t **)&r, sizeof(*r)) < sizeof(*r)) + return -1; + + if (maxlen > sizeof(r->version_string_ro)) + maxlen = sizeof(r->version_string_ro); + + switch (r->current_image) { + case EC_IMAGE_RO: + memcpy(id, r->version_string_ro, maxlen); + break; + case EC_IMAGE_RW: + memcpy(id, r->version_string_rw, maxlen); + break; + default: + return -1; + } + + id[maxlen - 1] = '\0'; + return 0; +} + +int cros_ec_read_version(struct cros_ec_dev *dev, + struct ec_response_get_version **versionp) +{ + if (ec_command_inptr(dev, EC_CMD_GET_VERSION, 0, NULL, 0, + (uint8_t **)versionp, sizeof(**versionp)) + < sizeof(**versionp)) + return -1; + + return 0; +} + +int cros_ec_read_build_info(struct cros_ec_dev *dev, char **strp) +{ + if (ec_command_inptr(dev, EC_CMD_GET_BUILD_INFO, 0, NULL, 0, + (uint8_t **)strp, EC_HOST_PARAM_SIZE) < 0) + return -1; + + return 0; +} + +int cros_ec_read_current_image(struct cros_ec_dev *dev, + enum ec_current_image *image) +{ + struct ec_response_get_version *r; + + if (ec_command_inptr(dev, EC_CMD_GET_VERSION, 0, NULL, 0, + (uint8_t **)&r, sizeof(*r)) < sizeof(*r)) + return -1; + + *image = r->current_image; + return 0; +} + +static int cros_ec_wait_on_hash_done(struct cros_ec_dev *dev, + struct ec_response_vboot_hash *hash) +{ + struct ec_params_vboot_hash p; + ulong start; + + start = get_timer(0); + while (hash->status == EC_VBOOT_HASH_STATUS_BUSY) { + mdelay(50); /* Insert some reasonable delay */ + + p.cmd = EC_VBOOT_HASH_GET; + if (ec_command(dev, EC_CMD_VBOOT_HASH, 0, &p, sizeof(p), + hash, sizeof(*hash)) < 0) + return -1; + + if (get_timer(start) > CROS_EC_CMD_HASH_TIMEOUT_MS) { + debug("%s: EC_VBOOT_HASH_GET timeout\n", __func__); + return -EC_RES_TIMEOUT; + } + } + return 0; +} + + +int cros_ec_read_hash(struct cros_ec_dev *dev, + struct ec_response_vboot_hash *hash) +{ + struct ec_params_vboot_hash p; + int rv; + + p.cmd = EC_VBOOT_HASH_GET; + if (ec_command(dev, EC_CMD_VBOOT_HASH, 0, &p, sizeof(p), + hash, sizeof(*hash)) < 0) + return -1; + + /* If the EC is busy calculating the hash, fidget until it's done. */ + rv = cros_ec_wait_on_hash_done(dev, hash); + if (rv) + return rv; + + /* If the hash is valid, we're done. Otherwise, we have to kick it off + * again and wait for it to complete. Note that we explicitly assume + * that hashing zero bytes is always wrong, even though that would + * produce a valid hash value. */ + if (hash->status == EC_VBOOT_HASH_STATUS_DONE && hash->size) + return 0; + + debug("%s: No valid hash (status=%d size=%d). Compute one...\n", + __func__, hash->status, hash->size); + + p.cmd = EC_VBOOT_HASH_RECALC; + p.hash_type = EC_VBOOT_HASH_TYPE_SHA256; + p.nonce_size = 0; + p.offset = EC_VBOOT_HASH_OFFSET_RW; + + if (ec_command(dev, EC_CMD_VBOOT_HASH, 0, &p, sizeof(p), + hash, sizeof(*hash)) < 0) + return -1; + + rv = cros_ec_wait_on_hash_done(dev, hash); + if (rv) + return rv; + + debug("%s: hash done\n", __func__); + + return 0; +} + +static int cros_ec_invalidate_hash(struct cros_ec_dev *dev) +{ + struct ec_params_vboot_hash p; + struct ec_response_vboot_hash *hash; + + /* We don't have an explict command for the EC to discard its current + * hash value, so we'll just tell it to calculate one that we know is + * wrong (we claim that hashing zero bytes is always invalid). + */ + p.cmd = EC_VBOOT_HASH_RECALC; + p.hash_type = EC_VBOOT_HASH_TYPE_SHA256; + p.nonce_size = 0; + p.offset = 0; + p.size = 0; + + debug("%s:\n", __func__); + + if (ec_command_inptr(dev, EC_CMD_VBOOT_HASH, 0, &p, sizeof(p), + (uint8_t **)&hash, sizeof(*hash)) < 0) + return -1; + + /* No need to wait for it to finish */ + return 0; +} + +int cros_ec_reboot(struct cros_ec_dev *dev, enum ec_reboot_cmd cmd, + uint8_t flags) +{ + struct ec_params_reboot_ec p; + + p.cmd = cmd; + p.flags = flags; + + if (ec_command_inptr(dev, EC_CMD_REBOOT_EC, 0, &p, sizeof(p), NULL, 0) + < 0) + return -1; + + if (!(flags & EC_REBOOT_FLAG_ON_AP_SHUTDOWN)) { + /* + * EC reboot will take place immediately so delay to allow it + * to complete. Note that some reboot types (EC_REBOOT_COLD) + * will reboot the AP as well, in which case we won't actually + * get to this point. + */ + /* + * TODO(rspangler@chromium.org): Would be nice if we had a + * better way to determine when the reboot is complete. Could + * we poll a memory-mapped LPC value? + */ + udelay(50000); + } + + return 0; +} + +int cros_ec_interrupt_pending(struct cros_ec_dev *dev) +{ + /* no interrupt support : always poll */ + if (!fdt_gpio_isvalid(&dev->ec_int)) + return 1; + + return !gpio_get_value(dev->ec_int.gpio); +} + +int cros_ec_info(struct cros_ec_dev *dev, struct ec_response_cros_ec_info *info) +{ + if (ec_command(dev, EC_CMD_CROS_EC_INFO, 0, NULL, 0, info, + sizeof(*info)) < sizeof(*info)) + return -1; + + return 0; +} + +int cros_ec_get_host_events(struct cros_ec_dev *dev, uint32_t *events_ptr) +{ + struct ec_response_host_event_mask *resp; + + /* + * Use the B copy of the event flags, because the main copy is already + * used by ACPI/SMI. + */ + if (ec_command_inptr(dev, EC_CMD_HOST_EVENT_GET_B, 0, NULL, 0, + (uint8_t **)&resp, sizeof(*resp)) < sizeof(*resp)) + return -1; + + if (resp->mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_INVALID)) + return -1; + + *events_ptr = resp->mask; + return 0; +} + +int cros_ec_clear_host_events(struct cros_ec_dev *dev, uint32_t events) +{ + struct ec_params_host_event_mask params; + + params.mask = events; + + /* + * Use the B copy of the event flags, so it affects the data returned + * by cros_ec_get_host_events(). + */ + if (ec_command_inptr(dev, EC_CMD_HOST_EVENT_CLEAR_B, 0, + ¶ms, sizeof(params), NULL, 0) < 0) + return -1; + + return 0; +} + +int cros_ec_flash_protect(struct cros_ec_dev *dev, + uint32_t set_mask, uint32_t set_flags, + struct ec_response_flash_protect *resp) +{ + struct ec_params_flash_protect params; + + params.mask = set_mask; + params.flags = set_flags; + + if (ec_command(dev, EC_CMD_FLASH_PROTECT, EC_VER_FLASH_PROTECT, + ¶ms, sizeof(params), + resp, sizeof(*resp)) < sizeof(*resp)) + return -1; + + return 0; +} + +static int cros_ec_check_version(struct cros_ec_dev *dev) +{ + struct ec_params_hello req; + struct ec_response_hello *resp; + +#ifdef CONFIG_CROS_EC_LPC + /* LPC has its own way of doing this */ + if (dev->interface == CROS_EC_IF_LPC) + return cros_ec_lpc_check_version(dev); +#endif + + /* + * TODO(sjg@chromium.org). + * There is a strange oddity here with the EC. We could just ignore + * the response, i.e. pass the last two parameters as NULL and 0. + * In this case we won't read back very many bytes from the EC. + * On the I2C bus the EC gets upset about this and will try to send + * the bytes anyway. This means that we will have to wait for that + * to complete before continuing with a new EC command. + * + * This problem is probably unique to the I2C bus. + * + * So for now, just read all the data anyway. + */ + dev->cmd_version_is_supported = 1; + if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, sizeof(req), + (uint8_t **)&resp, sizeof(*resp)) > 0) { + /* It appears to understand new version commands */ + dev->cmd_version_is_supported = 1; + } else { + dev->cmd_version_is_supported = 0; + if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, + sizeof(req), (uint8_t **)&resp, + sizeof(*resp)) < 0) { + debug("%s: Failed both old and new command style\n", + __func__); + return -1; + } + } + + return 0; +} + +int cros_ec_test(struct cros_ec_dev *dev) +{ + struct ec_params_hello req; + struct ec_response_hello *resp; + + req.in_data = 0x12345678; + if (ec_command_inptr(dev, EC_CMD_HELLO, 0, &req, sizeof(req), + (uint8_t **)&resp, sizeof(*resp)) < sizeof(*resp)) { + printf("ec_command_inptr() returned error\n"); + return -1; + } + if (resp->out_data != req.in_data + 0x01020304) { + printf("Received invalid handshake %x\n", resp->out_data); + return -1; + } + + return 0; +} + +int cros_ec_flash_offset(struct cros_ec_dev *dev, enum ec_flash_region region, + uint32_t *offset, uint32_t *size) +{ + struct ec_params_flash_region_info p; + struct ec_response_flash_region_info *r; + int ret; + + p.region = region; + ret = ec_command_inptr(dev, EC_CMD_FLASH_REGION_INFO, + EC_VER_FLASH_REGION_INFO, + &p, sizeof(p), (uint8_t **)&r, sizeof(*r)); + if (ret != sizeof(*r)) + return -1; + + if (offset) + *offset = r->offset; + if (size) + *size = r->size; + + return 0; +} + +int cros_ec_flash_erase(struct cros_ec_dev *dev, uint32_t offset, uint32_t size) +{ + struct ec_params_flash_erase p; + + p.offset = offset; + p.size = size; + return ec_command_inptr(dev, EC_CMD_FLASH_ERASE, 0, &p, sizeof(p), + NULL, 0); +} + +/** + * Write a single block to the flash + * + * Write a block of data to the EC flash. The size must not exceed the flash + * write block size which you can obtain from cros_ec_flash_write_burst_size(). + * + * The offset starts at 0. You can obtain the region information from + * cros_ec_flash_offset() to find out where to write for a particular region. + * + * Attempting to write to the region where the EC is currently running from + * will result in an error. + * + * @param dev CROS-EC device + * @param data Pointer to data buffer to write + * @param offset Offset within flash to write to. + * @param size Number of bytes to write + * @return 0 if ok, -1 on error + */ +static int cros_ec_flash_write_block(struct cros_ec_dev *dev, + const uint8_t *data, uint32_t offset, uint32_t size) +{ + struct ec_params_flash_write p; + + p.offset = offset; + p.size = size; + assert(data && p.size <= sizeof(p.data)); + memcpy(p.data, data, p.size); + + return ec_command_inptr(dev, EC_CMD_FLASH_WRITE, 0, + &p, sizeof(p), NULL, 0) >= 0 ? 0 : -1; +} + +/** + * Return optimal flash write burst size + */ +static int cros_ec_flash_write_burst_size(struct cros_ec_dev *dev) +{ + struct ec_params_flash_write p; + return sizeof(p.data); +} + +/** + * Check if a block of data is erased (all 0xff) + * + * This function is useful when dealing with flash, for checking whether a + * data block is erased and thus does not need to be programmed. + * + * @param data Pointer to data to check (must be word-aligned) + * @param size Number of bytes to check (must be word-aligned) + * @return 0 if erased, non-zero if any word is not erased + */ +static int cros_ec_data_is_erased(const uint32_t *data, int size) +{ + assert(!(size & 3)); + size /= sizeof(uint32_t); + for (; size > 0; size -= 4, data++) + if (*data != -1U) + return 0; + + return 1; +} + +int cros_ec_flash_write(struct cros_ec_dev *dev, const uint8_t *data, + uint32_t offset, uint32_t size) +{ + uint32_t burst = cros_ec_flash_write_burst_size(dev); + uint32_t end, off; + int ret; + + /* + * TODO: round up to the nearest multiple of write size. Can get away + * without that on link right now because its write size is 4 bytes. + */ + end = offset + size; + for (off = offset; off < end; off += burst, data += burst) { + uint32_t todo; + + /* If the data is empty, there is no point in programming it */ + todo = min(end - off, burst); + if (dev->optimise_flash_write && + cros_ec_data_is_erased((uint32_t *)data, todo)) + continue; + + ret = cros_ec_flash_write_block(dev, data, off, todo); + if (ret) + return ret; + } + + return 0; +} + +/** + * Read a single block from the flash + * + * Read a block of data from the EC flash. The size must not exceed the flash + * write block size which you can obtain from cros_ec_flash_write_burst_size(). + * + * The offset starts at 0. You can obtain the region information from + * cros_ec_flash_offset() to find out where to read for a particular region. + * + * @param dev CROS-EC device + * @param data Pointer to data buffer to read into + * @param offset Offset within flash to read from + * @param size Number of bytes to read + * @return 0 if ok, -1 on error + */ +static int cros_ec_flash_read_block(struct cros_ec_dev *dev, uint8_t *data, + uint32_t offset, uint32_t size) +{ + struct ec_params_flash_read p; + + p.offset = offset; + p.size = size; + + return ec_command(dev, EC_CMD_FLASH_READ, 0, + &p, sizeof(p), data, size) >= 0 ? 0 : -1; +} + +int cros_ec_flash_read(struct cros_ec_dev *dev, uint8_t *data, uint32_t offset, + uint32_t size) +{ + uint32_t burst = cros_ec_flash_write_burst_size(dev); + uint32_t end, off; + int ret; + + end = offset + size; + for (off = offset; off < end; off += burst, data += burst) { + ret = cros_ec_flash_read_block(dev, data, off, + min(end - off, burst)); + if (ret) + return ret; + } + + return 0; +} + +int cros_ec_flash_update_rw(struct cros_ec_dev *dev, + const uint8_t *image, int image_size) +{ + uint32_t rw_offset, rw_size; + int ret; + + if (cros_ec_flash_offset(dev, EC_FLASH_REGION_RW, &rw_offset, &rw_size)) + return -1; + if (image_size > rw_size) + return -1; + + /* Invalidate the existing hash, just in case the AP reboots + * unexpectedly during the update. If that happened, the EC RW firmware + * would be invalid, but the EC would still have the original hash. + */ + ret = cros_ec_invalidate_hash(dev); + if (ret) + return ret; + + /* + * Erase the entire RW section, so that the EC doesn't see any garbage + * past the new image if it's smaller than the current image. + * + * TODO: could optimize this to erase just the current image, since + * presumably everything past that is 0xff's. But would still need to + * round up to the nearest multiple of erase size. + */ + ret = cros_ec_flash_erase(dev, rw_offset, rw_size); + if (ret) + return ret; + + /* Write the image */ + ret = cros_ec_flash_write(dev, image, rw_offset, image_size); + if (ret) + return ret; + + return 0; +} + +int cros_ec_read_vbnvcontext(struct cros_ec_dev *dev, uint8_t *block) +{ + struct ec_params_vbnvcontext p; + int len; + + p.op = EC_VBNV_CONTEXT_OP_READ; + + len = ec_command(dev, EC_CMD_VBNV_CONTEXT, EC_VER_VBNV_CONTEXT, + &p, sizeof(p), block, EC_VBNV_BLOCK_SIZE); + if (len < EC_VBNV_BLOCK_SIZE) + return -1; + + return 0; +} + +int cros_ec_write_vbnvcontext(struct cros_ec_dev *dev, const uint8_t *block) +{ + struct ec_params_vbnvcontext p; + int len; + + p.op = EC_VBNV_CONTEXT_OP_WRITE; + memcpy(p.block, block, sizeof(p.block)); + + len = ec_command_inptr(dev, EC_CMD_VBNV_CONTEXT, EC_VER_VBNV_CONTEXT, + &p, sizeof(p), NULL, 0); + if (len < 0) + return -1; + + return 0; +} + +int cros_ec_set_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t state) +{ + struct ec_params_ldo_set params; + + params.index = index; + params.state = state; + + if (ec_command_inptr(dev, EC_CMD_LDO_SET, 0, + ¶ms, sizeof(params), + NULL, 0)) + return -1; + + return 0; +} + +int cros_ec_get_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t *state) +{ + struct ec_params_ldo_get params; + struct ec_response_ldo_get *resp; + + params.index = index; + + if (ec_command_inptr(dev, EC_CMD_LDO_GET, 0, + ¶ms, sizeof(params), + (uint8_t **)&resp, sizeof(*resp)) < sizeof(*resp)) + return -1; + + *state = resp->state; + + return 0; +} + +/** + * Decode MBKP details from the device tree and allocate a suitable device. + * + * @param blob Device tree blob + * @param node Node to decode from + * @param devp Returns a pointer to the new allocated device + * @return 0 if ok, -1 on error + */ +static int cros_ec_decode_fdt(const void *blob, int node, + struct cros_ec_dev **devp) +{ + enum fdt_compat_id compat; + struct cros_ec_dev *dev; + int parent; + + /* See what type of parent we are inside (this is expensive) */ + parent = fdt_parent_offset(blob, node); + if (parent < 0) { + debug("%s: Cannot find node parent\n", __func__); + return -1; + } + + dev = &static_dev; + dev->node = node; + dev->parent_node = parent; + + compat = fdtdec_lookup(blob, parent); + switch (compat) { +#ifdef CONFIG_CROS_EC_SPI + case COMPAT_SAMSUNG_EXYNOS_SPI: + dev->interface = CROS_EC_IF_SPI; + if (cros_ec_spi_decode_fdt(dev, blob)) + return -1; + break; +#endif +#ifdef CONFIG_CROS_EC_I2C + case COMPAT_SAMSUNG_S3C2440_I2C: + dev->interface = CROS_EC_IF_I2C; + if (cros_ec_i2c_decode_fdt(dev, blob)) + return -1; + break; +#endif +#ifdef CONFIG_CROS_EC_LPC + case COMPAT_INTEL_LPC: + dev->interface = CROS_EC_IF_LPC; + break; +#endif + default: + debug("%s: Unknown compat id %d\n", __func__, compat); + return -1; + } + + fdtdec_decode_gpio(blob, node, "ec-interrupt", &dev->ec_int); + dev->optimise_flash_write = fdtdec_get_bool(blob, node, + "optimise-flash-write"); + *devp = dev; + + return 0; +} + +int cros_ec_init(const void *blob, struct cros_ec_dev **cros_ecp) +{ + char id[MSG_BYTES]; + struct cros_ec_dev *dev; + int node = 0; + + *cros_ecp = NULL; + do { + node = fdtdec_next_compatible(blob, node, + COMPAT_GOOGLE_CROS_EC); + if (node < 0) { + debug("%s: Node not found\n", __func__); + return 0; + } + } while (!fdtdec_get_is_enabled(blob, node)); + + if (cros_ec_decode_fdt(blob, node, &dev)) { + debug("%s: Failed to decode device.\n", __func__); + return -CROS_EC_ERR_FDT_DECODE; + } + + switch (dev->interface) { +#ifdef CONFIG_CROS_EC_SPI + case CROS_EC_IF_SPI: + if (cros_ec_spi_init(dev, blob)) { + debug("%s: Could not setup SPI interface\n", __func__); + return -CROS_EC_ERR_DEV_INIT; + } + break; +#endif +#ifdef CONFIG_CROS_EC_I2C + case CROS_EC_IF_I2C: + if (cros_ec_i2c_init(dev, blob)) + return -CROS_EC_ERR_DEV_INIT; + break; +#endif +#ifdef CONFIG_CROS_EC_LPC + case CROS_EC_IF_LPC: + if (cros_ec_lpc_init(dev, blob)) + return -CROS_EC_ERR_DEV_INIT; + break; +#endif + case CROS_EC_IF_NONE: + default: + return 0; + } + + /* we will poll the EC interrupt line */ + fdtdec_setup_gpio(&dev->ec_int); + if (fdt_gpio_isvalid(&dev->ec_int)) + gpio_direction_input(dev->ec_int.gpio); + + if (cros_ec_check_version(dev)) { + debug("%s: Could not detect CROS-EC version\n", __func__); + return -CROS_EC_ERR_CHECK_VERSION; + } + + if (cros_ec_read_id(dev, id, sizeof(id))) { + debug("%s: Could not read KBC ID\n", __func__); + return -CROS_EC_ERR_READ_ID; + } + + /* Remember this device for use by the cros_ec command */ + last_dev = *cros_ecp = dev; + debug("Google Chrome EC CROS-EC driver ready, id '%s'\n", id); + + return 0; +} + +#ifdef CONFIG_CMD_CROS_EC +int cros_ec_decode_region(int argc, char * const argv[]) +{ + if (argc > 0) { + if (0 == strcmp(*argv, "rw")) + return EC_FLASH_REGION_RW; + else if (0 == strcmp(*argv, "ro")) + return EC_FLASH_REGION_RO; + + debug("%s: Invalid region '%s'\n", __func__, *argv); + } else { + debug("%s: Missing region parameter\n", __func__); + } + + return -1; +} + +/** + * Perform a flash read or write command + * + * @param dev CROS-EC device to read/write + * @param is_write 1 do to a write, 0 to do a read + * @param argc Number of arguments + * @param argv Arguments (2 is region, 3 is address) + * @return 0 for ok, 1 for a usage error or -ve for ec command error + * (negative EC_RES_...) + */ +static int do_read_write(struct cros_ec_dev *dev, int is_write, int argc, + char * const argv[]) +{ + uint32_t offset, size = -1U, region_size; + unsigned long addr; + char *endp; + int region; + int ret; + + region = cros_ec_decode_region(argc - 2, argv + 2); + if (region == -1) + return 1; + if (argc < 4) + return 1; + addr = simple_strtoul(argv[3], &endp, 16); + if (*argv[3] == 0 || *endp != 0) + return 1; + if (argc > 4) { + size = simple_strtoul(argv[4], &endp, 16); + if (*argv[4] == 0 || *endp != 0) + return 1; + } + + ret = cros_ec_flash_offset(dev, region, &offset, ®ion_size); + if (ret) { + debug("%s: Could not read region info\n", __func__); + return ret; + } + if (size == -1U) + size = region_size; + + ret = is_write ? + cros_ec_flash_write(dev, (uint8_t *)addr, offset, size) : + cros_ec_flash_read(dev, (uint8_t *)addr, offset, size); + if (ret) { + debug("%s: Could not %s region\n", __func__, + is_write ? "write" : "read"); + return ret; + } + + return 0; +} + +static int do_cros_ec(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + struct cros_ec_dev *dev = last_dev; + const char *cmd; + int ret = 0; + + if (argc < 2) + return CMD_RET_USAGE; + + cmd = argv[1]; + if (0 == strcmp("init", cmd)) { + ret = cros_ec_init(gd->fdt_blob, &dev); + if (ret) { + printf("Could not init cros_ec device (err %d)\n", ret); + return 1; + } + return 0; + } + + /* Just use the last allocated device; there should be only one */ + if (!last_dev) { + printf("No CROS-EC device available\n"); + return 1; + } + if (0 == strcmp("id", cmd)) { + char id[MSG_BYTES]; + + if (cros_ec_read_id(dev, id, sizeof(id))) { + debug("%s: Could not read KBC ID\n", __func__); + return 1; + } + printf("%s\n", id); + } else if (0 == strcmp("info", cmd)) { + struct ec_response_cros_ec_info info; + + if (cros_ec_info(dev, &info)) { + debug("%s: Could not read KBC info\n", __func__); + return 1; + } + printf("rows = %u\n", info.rows); + printf("cols = %u\n", info.cols); + printf("switches = %#x\n", info.switches); + } else if (0 == strcmp("curimage", cmd)) { + enum ec_current_image image; + + if (cros_ec_read_current_image(dev, &image)) { + debug("%s: Could not read KBC image\n", __func__); + return 1; + } + printf("%d\n", image); + } else if (0 == strcmp("hash", cmd)) { + struct ec_response_vboot_hash hash; + int i; + + if (cros_ec_read_hash(dev, &hash)) { + debug("%s: Could not read KBC hash\n", __func__); + return 1; + } + + if (hash.hash_type == EC_VBOOT_HASH_TYPE_SHA256) + printf("type: SHA-256\n"); + else + printf("type: %d\n", hash.hash_type); + + printf("offset: 0x%08x\n", hash.offset); + printf("size: 0x%08x\n", hash.size); + + printf("digest: "); + for (i = 0; i < hash.digest_size; i++) + printf("%02x", hash.hash_digest[i]); + printf("\n"); + } else if (0 == strcmp("reboot", cmd)) { + int region; + enum ec_reboot_cmd cmd; + + if (argc >= 3 && !strcmp(argv[2], "cold")) + cmd = EC_REBOOT_COLD; + else { + region = cros_ec_decode_region(argc - 2, argv + 2); + if (region == EC_FLASH_REGION_RO) + cmd = EC_REBOOT_JUMP_RO; + else if (region == EC_FLASH_REGION_RW) + cmd = EC_REBOOT_JUMP_RW; + else + return CMD_RET_USAGE; + } + + if (cros_ec_reboot(dev, cmd, 0)) { + debug("%s: Could not reboot KBC\n", __func__); + return 1; + } + } else if (0 == strcmp("events", cmd)) { + uint32_t events; + + if (cros_ec_get_host_events(dev, &events)) { + debug("%s: Could not read host events\n", __func__); + return 1; + } + printf("0x%08x\n", events); + } else if (0 == strcmp("clrevents", cmd)) { + uint32_t events = 0x7fffffff; + + if (argc >= 3) + events = simple_strtol(argv[2], NULL, 0); + + if (cros_ec_clear_host_events(dev, events)) { + debug("%s: Could not clear host events\n", __func__); + return 1; + } + } else if (0 == strcmp("read", cmd)) { + ret = do_read_write(dev, 0, argc, argv); + if (ret > 0) + return CMD_RET_USAGE; + } else if (0 == strcmp("write", cmd)) { + ret = do_read_write(dev, 1, argc, argv); + if (ret > 0) + return CMD_RET_USAGE; + } else if (0 == strcmp("erase", cmd)) { + int region = cros_ec_decode_region(argc - 2, argv + 2); + uint32_t offset, size; + + if (region == -1) + return CMD_RET_USAGE; + if (cros_ec_flash_offset(dev, region, &offset, &size)) { + debug("%s: Could not read region info\n", __func__); + ret = -1; + } else { + ret = cros_ec_flash_erase(dev, offset, size); + if (ret) { + debug("%s: Could not erase region\n", + __func__); + } + } + } else if (0 == strcmp("regioninfo", cmd)) { + int region = cros_ec_decode_region(argc - 2, argv + 2); + uint32_t offset, size; + + if (region == -1) + return CMD_RET_USAGE; + ret = cros_ec_flash_offset(dev, region, &offset, &size); + if (ret) { + debug("%s: Could not read region info\n", __func__); + } else { + printf("Region: %s\n", region == EC_FLASH_REGION_RO ? + "RO" : "RW"); + printf("Offset: %x\n", offset); + printf("Size: %x\n", size); + } + } else if (0 == strcmp("vbnvcontext", cmd)) { + uint8_t block[EC_VBNV_BLOCK_SIZE]; + char buf[3]; + int i, len; + unsigned long result; + + if (argc <= 2) { + ret = cros_ec_read_vbnvcontext(dev, block); + if (!ret) { + printf("vbnv_block: "); + for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++) + printf("%02x", block[i]); + putc('\n'); + } + } else { + /* + * TODO(clchiou): Move this to a utility function as + * cmd_spi might want to call it. + */ + memset(block, 0, EC_VBNV_BLOCK_SIZE); + len = strlen(argv[2]); + buf[2] = '\0'; + for (i = 0; i < EC_VBNV_BLOCK_SIZE; i++) { + if (i * 2 >= len) + break; + buf[0] = argv[2][i * 2]; + if (i * 2 + 1 >= len) + buf[1] = '0'; + else + buf[1] = argv[2][i * 2 + 1]; + strict_strtoul(buf, 16, &result); + block[i] = result; + } + ret = cros_ec_write_vbnvcontext(dev, block); + } + if (ret) { + debug("%s: Could not %s VbNvContext\n", __func__, + argc <= 2 ? "read" : "write"); + } + } else if (0 == strcmp("test", cmd)) { + int result = cros_ec_test(dev); + + if (result) + printf("Test failed with error %d\n", result); + else + puts("Test passed\n"); + } else if (0 == strcmp("version", cmd)) { + struct ec_response_get_version *p; + char *build_string; + + ret = cros_ec_read_version(dev, &p); + if (!ret) { + /* Print versions */ + printf("RO version: %1.*s\n", + sizeof(p->version_string_ro), + p->version_string_ro); + printf("RW version: %1.*s\n", + sizeof(p->version_string_rw), + p->version_string_rw); + printf("Firmware copy: %s\n", + (p->current_image < + ARRAY_SIZE(ec_current_image_name) ? + ec_current_image_name[p->current_image] : + "?")); + ret = cros_ec_read_build_info(dev, &build_string); + if (!ret) + printf("Build info: %s\n", build_string); + } + } else if (0 == strcmp("ldo", cmd)) { + uint8_t index, state; + char *endp; + + if (argc < 3) + return CMD_RET_USAGE; + index = simple_strtoul(argv[2], &endp, 10); + if (*argv[2] == 0 || *endp != 0) + return CMD_RET_USAGE; + if (argc > 3) { + state = simple_strtoul(argv[3], &endp, 10); + if (*argv[3] == 0 || *endp != 0) + return CMD_RET_USAGE; + ret = cros_ec_set_ldo(dev, index, state); + } else { + ret = cros_ec_get_ldo(dev, index, &state); + if (!ret) { + printf("LDO%d: %s\n", index, + state == EC_LDO_STATE_ON ? + "on" : "off"); + } + } + + if (ret) { + debug("%s: Could not access LDO%d\n", __func__, index); + return ret; + } + } else { + return CMD_RET_USAGE; + } + + if (ret < 0) { + printf("Error: CROS-EC command failed (error %d)\n", ret); + ret = 1; + } + + return ret; +} + +U_BOOT_CMD( + crosec, 5, 1, do_cros_ec, + "CROS-EC utility command", + "init Re-init CROS-EC (done on startup automatically)\n" + "crosec id Read CROS-EC ID\n" + "crosec info Read CROS-EC info\n" + "crosec curimage Read CROS-EC current image\n" + "crosec hash Read CROS-EC hash\n" + "crosec reboot [rw | ro | cold] Reboot CROS-EC\n" + "crosec events Read CROS-EC host events\n" + "crosec clrevents [mask] Clear CROS-EC host events\n" + "crosec regioninfo <ro|rw> Read image info\n" + "crosec erase <ro|rw> Erase EC image\n" + "crosec read <ro|rw> <addr> [<size>] Read EC image\n" + "crosec write <ro|rw> <addr> [<size>] Write EC image\n" + "crosec vbnvcontext [hexstring] Read [write] VbNvContext from EC\n" + "crosec ldo <idx> [<state>] Switch/Read LDO state\n" + "crosec test run tests on cros_ec\n" + "crosec version Read CROS-EC version" +); +#endif diff --git a/drivers/misc/cros_ec_i2c.c b/drivers/misc/cros_ec_i2c.c new file mode 100644 index 0000000000..b0060ac197 --- /dev/null +++ b/drivers/misc/cros_ec_i2c.c @@ -0,0 +1,199 @@ +/* + * Chromium OS cros_ec driver - I2C interface + * + * Copyright (c) 2012 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * The Matrix Keyboard Protocol driver handles talking to the keyboard + * controller chip. Mostly this is for keyboard functions, but some other + * things have slipped in, so we provide generic services to talk to the + * KBC. + */ + +#include <common.h> +#include <i2c.h> +#include <cros_ec.h> + +#ifdef DEBUG_TRACE +#define debug_trace(fmt, b...) debug(fmt, #b) +#else +#define debug_trace(fmt, b...) +#endif + +int cros_ec_i2c_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, + const uint8_t *dout, int dout_len, + uint8_t **dinp, int din_len) +{ + int old_bus = 0; + /* version8, cmd8, arglen8, out8[dout_len], csum8 */ + int out_bytes = dout_len + 4; + /* response8, arglen8, in8[din_len], checksum8 */ + int in_bytes = din_len + 3; + uint8_t *ptr; + /* Receive input data, so that args will be dword aligned */ + uint8_t *in_ptr; + int ret; + + old_bus = i2c_get_bus_num(); + + /* + * Sanity-check I/O sizes given transaction overhead in internal + * buffers. + */ + if (out_bytes > sizeof(dev->dout)) { + debug("%s: Cannot send %d bytes\n", __func__, dout_len); + return -1; + } + if (in_bytes > sizeof(dev->din)) { + debug("%s: Cannot receive %d bytes\n", __func__, din_len); + return -1; + } + assert(dout_len >= 0); + assert(dinp); + + /* + * Copy command and data into output buffer so we can do a single I2C + * burst transaction. + */ + ptr = dev->dout; + + /* + * in_ptr starts of pointing to a dword-aligned input data buffer. + * We decrement it back by the number of header bytes we expect to + * receive, so that the first parameter of the resulting input data + * will be dword aligned. + */ + in_ptr = dev->din + sizeof(int64_t); + if (!dev->cmd_version_is_supported) { + /* Send an old-style command */ + *ptr++ = cmd; + out_bytes = dout_len + 1; + in_bytes = din_len + 2; + in_ptr--; /* Expect just a status byte */ + } else { + *ptr++ = EC_CMD_VERSION0 + cmd_version; + *ptr++ = cmd; + *ptr++ = dout_len; + in_ptr -= 2; /* Expect status, length bytes */ + } + memcpy(ptr, dout, dout_len); + ptr += dout_len; + + if (dev->cmd_version_is_supported) + *ptr++ = (uint8_t) + cros_ec_calc_checksum(dev->dout, dout_len + 3); + + /* Set to the proper i2c bus */ + if (i2c_set_bus_num(dev->bus_num)) { + debug("%s: Cannot change to I2C bus %d\n", __func__, + dev->bus_num); + return -1; + } + + /* Send output data */ + cros_ec_dump_data("out", -1, dev->dout, out_bytes); + ret = i2c_write(dev->addr, 0, 0, dev->dout, out_bytes); + if (ret) { + debug("%s: Cannot complete I2C write to 0x%x\n", + __func__, dev->addr); + ret = -1; + } + + if (!ret) { + ret = i2c_read(dev->addr, 0, 0, in_ptr, in_bytes); + if (ret) { + debug("%s: Cannot complete I2C read from 0x%x\n", + __func__, dev->addr); + ret = -1; + } + } + + /* Return to original bus number */ + i2c_set_bus_num(old_bus); + if (ret) + return ret; + + if (*in_ptr != EC_RES_SUCCESS) { + debug("%s: Received bad result code %d\n", __func__, *in_ptr); + return -(int)*in_ptr; + } + + if (dev->cmd_version_is_supported) { + int len, csum; + + len = in_ptr[1]; + if (len + 3 > sizeof(dev->din)) { + debug("%s: Received length %#02x too large\n", + __func__, len); + return -1; + } + csum = cros_ec_calc_checksum(in_ptr, 2 + len); + if (csum != in_ptr[2 + len]) { + debug("%s: Invalid checksum rx %#02x, calced %#02x\n", + __func__, in_ptr[2 + din_len], csum); + return -1; + } + din_len = min(din_len, len); + cros_ec_dump_data("in", -1, in_ptr, din_len + 3); + } else { + cros_ec_dump_data("in (old)", -1, in_ptr, in_bytes); + } + + /* Return pointer to dword-aligned input data, if any */ + *dinp = dev->din + sizeof(int64_t); + + return din_len; +} + +int cros_ec_i2c_decode_fdt(struct cros_ec_dev *dev, const void *blob) +{ + /* Decode interface-specific FDT params */ + dev->max_frequency = fdtdec_get_int(blob, dev->node, + "i2c-max-frequency", 100000); + dev->bus_num = i2c_get_bus_num_fdt(dev->parent_node); + if (dev->bus_num == -1) { + debug("%s: Failed to read bus number\n", __func__); + return -1; + } + dev->addr = fdtdec_get_int(blob, dev->node, "reg", -1); + if (dev->addr == -1) { + debug("%s: Failed to read device address\n", __func__); + return -1; + } + + return 0; +} + +/** + * Initialize I2C protocol. + * + * @param dev CROS_EC device + * @param blob Device tree blob + * @return 0 if ok, -1 on error + */ +int cros_ec_i2c_init(struct cros_ec_dev *dev, const void *blob) +{ + i2c_init(dev->max_frequency, dev->addr); + + dev->cmd_version_is_supported = 0; + + return 0; +} diff --git a/drivers/misc/cros_ec_lpc.c b/drivers/misc/cros_ec_lpc.c new file mode 100644 index 0000000000..cf0435b8dd --- /dev/null +++ b/drivers/misc/cros_ec_lpc.c @@ -0,0 +1,283 @@ +/* + * Chromium OS cros_ec driver - LPC interface + * + * Copyright (c) 2012 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * The Matrix Keyboard Protocol driver handles talking to the keyboard + * controller chip. Mostly this is for keyboard functions, but some other + * things have slipped in, so we provide generic services to talk to the + * KBC. + */ + +#include <common.h> +#include <command.h> +#include <cros_ec.h> +#include <asm/io.h> + +#ifdef DEBUG_TRACE +#define debug_trace(fmt, b...) debug(fmt, ##b) +#else +#define debug_trace(fmt, b...) +#endif + +static int wait_for_sync(struct cros_ec_dev *dev) +{ + unsigned long start; + + start = get_timer(0); + while (inb(EC_LPC_ADDR_HOST_CMD) & EC_LPC_STATUS_BUSY_MASK) { + if (get_timer(start) > 1000) { + debug("%s: Timeout waiting for CROS_EC sync\n", + __func__); + return -1; + } + } + + return 0; +} + +/** + * Send a command to a LPC CROS_EC device and return the reply. + * + * The device's internal input/output buffers are used. + * + * @param dev CROS_EC device + * @param cmd Command to send (EC_CMD_...) + * @param cmd_version Version of command to send (EC_VER_...) + * @param dout Output data (may be NULL If dout_len=0) + * @param dout_len Size of output data in bytes + * @param dinp Place to put pointer to response data + * @param din_len Maximum size of response in bytes + * @return number of bytes in response, or -1 on error + */ +static int old_lpc_command(struct cros_ec_dev *dev, uint8_t cmd, + const uint8_t *dout, int dout_len, + uint8_t **dinp, int din_len) +{ + int ret, i; + + if (dout_len > EC_OLD_PARAM_SIZE) { + debug("%s: Cannot send %d bytes\n", __func__, dout_len); + return -1; + } + + if (din_len > EC_OLD_PARAM_SIZE) { + debug("%s: Cannot receive %d bytes\n", __func__, din_len); + return -1; + } + + if (wait_for_sync(dev)) { + debug("%s: Timeout waiting ready\n", __func__); + return -1; + } + + debug_trace("cmd: %02x, ", cmd); + for (i = 0; i < dout_len; i++) { + debug_trace("%02x ", dout[i]); + outb(dout[i], EC_LPC_ADDR_OLD_PARAM + i); + } + outb(cmd, EC_LPC_ADDR_HOST_CMD); + debug_trace("\n"); + + if (wait_for_sync(dev)) { + debug("%s: Timeout waiting ready\n", __func__); + return -1; + } + + ret = inb(EC_LPC_ADDR_HOST_DATA); + if (ret) { + debug("%s: CROS_EC result code %d\n", __func__, ret); + return -ret; + } + + debug_trace("resp: %02x, ", ret); + for (i = 0; i < din_len; i++) { + dev->din[i] = inb(EC_LPC_ADDR_OLD_PARAM + i); + debug_trace("%02x ", dev->din[i]); + } + debug_trace("\n"); + *dinp = dev->din; + + return din_len; +} + +int cros_ec_lpc_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, + const uint8_t *dout, int dout_len, + uint8_t **dinp, int din_len) +{ + const int cmd_addr = EC_LPC_ADDR_HOST_CMD; + const int data_addr = EC_LPC_ADDR_HOST_DATA; + const int args_addr = EC_LPC_ADDR_HOST_ARGS; + const int param_addr = EC_LPC_ADDR_HOST_PARAM; + + struct ec_lpc_host_args args; + uint8_t *d; + int csum; + int i; + + /* Fall back to old-style command interface if args aren't supported */ + if (!dev->cmd_version_is_supported) + return old_lpc_command(dev, cmd, dout, dout_len, dinp, + din_len); + + if (dout_len > EC_HOST_PARAM_SIZE) { + debug("%s: Cannot send %d bytes\n", __func__, dout_len); + return -1; + } + + /* Fill in args */ + args.flags = EC_HOST_ARGS_FLAG_FROM_HOST; + args.command_version = cmd_version; + args.data_size = dout_len; + + /* Calculate checksum */ + csum = cmd + args.flags + args.command_version + args.data_size; + for (i = 0, d = (uint8_t *)dout; i < dout_len; i++, d++) + csum += *d; + + args.checksum = (uint8_t)csum; + + if (wait_for_sync(dev)) { + debug("%s: Timeout waiting ready\n", __func__); + return -1; + } + + /* Write args */ + for (i = 0, d = (uint8_t *)&args; i < sizeof(args); i++, d++) + outb(*d, args_addr + i); + + /* Write data, if any */ + debug_trace("cmd: %02x, ver: %02x", cmd, cmd_version); + for (i = 0, d = (uint8_t *)dout; i < dout_len; i++, d++) { + outb(*d, param_addr + i); + debug_trace("%02x ", *d); + } + + outb(cmd, cmd_addr); + debug_trace("\n"); + + if (wait_for_sync(dev)) { + debug("%s: Timeout waiting for response\n", __func__); + return -1; + } + + /* Check result */ + i = inb(data_addr); + if (i) { + debug("%s: CROS_EC result code %d\n", __func__, i); + return -i; + } + + /* Read back args */ + for (i = 0, d = (uint8_t *)&args; i < sizeof(args); i++, d++) + *d = inb(args_addr + i); + + /* + * If EC didn't modify args flags, then somehow we sent a new-style + * command to an old EC, which means it would have read its params + * from the wrong place. + */ + if (!(args.flags & EC_HOST_ARGS_FLAG_TO_HOST)) { + debug("%s: CROS_EC protocol mismatch\n", __func__); + return -EC_RES_INVALID_RESPONSE; + } + + if (args.data_size > din_len) { + debug("%s: CROS_EC returned too much data %d > %d\n", + __func__, args.data_size, din_len); + return -EC_RES_INVALID_RESPONSE; + } + + /* Read data, if any */ + for (i = 0, d = (uint8_t *)dev->din; i < args.data_size; i++, d++) { + *d = inb(param_addr + i); + debug_trace("%02x ", *d); + } + debug_trace("\n"); + + /* Verify checksum */ + csum = cmd + args.flags + args.command_version + args.data_size; + for (i = 0, d = (uint8_t *)dev->din; i < args.data_size; i++, d++) + csum += *d; + + if (args.checksum != (uint8_t)csum) { + debug("%s: CROS_EC response has invalid checksum\n", __func__); + return -EC_RES_INVALID_CHECKSUM; + } + *dinp = dev->din; + + /* Return actual amount of data received */ + return args.data_size; +} + +/** + * Initialize LPC protocol. + * + * @param dev CROS_EC device + * @param blob Device tree blob + * @return 0 if ok, -1 on error + */ +int cros_ec_lpc_init(struct cros_ec_dev *dev, const void *blob) +{ + int byte, i; + + /* See if we can find an EC at the other end */ + byte = 0xff; + byte &= inb(EC_LPC_ADDR_HOST_CMD); + byte &= inb(EC_LPC_ADDR_HOST_DATA); + for (i = 0; i < EC_HOST_PARAM_SIZE && (byte == 0xff); i++) + byte &= inb(EC_LPC_ADDR_HOST_PARAM + i); + if (byte == 0xff) { + debug("%s: CROS_EC device not found on LPC bus\n", + __func__); + return -1; + } + + return 0; +} + +/* + * Test if LPC command args are supported. + * + * The cheapest way to do this is by looking for the memory-mapped + * flag. This is faster than sending a new-style 'hello' command and + * seeing whether the EC sets the EC_HOST_ARGS_FLAG_FROM_HOST flag + * in args when it responds. + */ +int cros_ec_lpc_check_version(struct cros_ec_dev *dev) +{ + if (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID) == 'E' && + inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1) + == 'C' && + (inb(EC_LPC_ADDR_MEMMAP + + EC_MEMMAP_HOST_CMD_FLAGS) & + EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED)) { + dev->cmd_version_is_supported = 1; + } else { + /* We are going to use the old IO ports */ + dev->cmd_version_is_supported = 0; + } + debug("lpc: version %s\n", dev->cmd_version_is_supported ? + "new" : "old"); + + return 0; +} diff --git a/drivers/misc/cros_ec_spi.c b/drivers/misc/cros_ec_spi.c new file mode 100644 index 0000000000..e15c83341c --- /dev/null +++ b/drivers/misc/cros_ec_spi.c @@ -0,0 +1,161 @@ +/* + * Chromium OS cros_ec driver - SPI interface + * + * Copyright (c) 2012 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* + * The Matrix Keyboard Protocol driver handles talking to the keyboard + * controller chip. Mostly this is for keyboard functions, but some other + * things have slipped in, so we provide generic services to talk to the + * KBC. + */ + +#include <common.h> +#include <cros_ec.h> +#include <spi.h> + +/** + * Send a command to a LPC CROS_EC device and return the reply. + * + * The device's internal input/output buffers are used. + * + * @param dev CROS_EC device + * @param cmd Command to send (EC_CMD_...) + * @param cmd_version Version of command to send (EC_VER_...) + * @param dout Output data (may be NULL If dout_len=0) + * @param dout_len Size of output data in bytes + * @param dinp Returns pointer to response data. This will be + * untouched unless we return a value > 0. + * @param din_len Maximum size of response in bytes + * @return number of bytes in response, or -1 on error + */ +int cros_ec_spi_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, + const uint8_t *dout, int dout_len, + uint8_t **dinp, int din_len) +{ + int in_bytes = din_len + 4; /* status, length, checksum, trailer */ + uint8_t *out; + uint8_t *p; + int csum, len; + int rv; + + /* + * Sanity-check input size to make sure it plus transaction overhead + * fits in the internal device buffer. + */ + if (in_bytes > sizeof(dev->din)) { + debug("%s: Cannot receive %d bytes\n", __func__, din_len); + return -1; + } + + /* We represent message length as a byte */ + if (dout_len > 0xff) { + debug("%s: Cannot send %d bytes\n", __func__, dout_len); + return -1; + } + + /* + * Clear input buffer so we don't get false hits for MSG_HEADER + */ + memset(dev->din, '\0', in_bytes); + + if (spi_claim_bus(dev->spi)) { + debug("%s: Cannot claim SPI bus\n", __func__); + return -1; + } + + out = dev->dout; + out[0] = cmd_version; + out[1] = cmd; + out[2] = (uint8_t)dout_len; + memcpy(out + 3, dout, dout_len); + csum = cros_ec_calc_checksum(out, 3) + + cros_ec_calc_checksum(dout, dout_len); + out[3 + dout_len] = (uint8_t)csum; + + /* + * Send output data and receive input data starting such that the + * message body will be dword aligned. + */ + p = dev->din + sizeof(int64_t) - 2; + len = dout_len + 4; + cros_ec_dump_data("out", cmd, out, len); + rv = spi_xfer(dev->spi, max(len, in_bytes) * 8, out, p, + SPI_XFER_BEGIN | SPI_XFER_END); + + spi_release_bus(dev->spi); + + if (rv) { + debug("%s: Cannot complete SPI transfer\n", __func__); + return -1; + } + + len = min(p[1], din_len); + cros_ec_dump_data("in", -1, p, len + 3); + + /* Response code is first byte of message */ + if (p[0] != EC_RES_SUCCESS) { + printf("%s: Returned status %d\n", __func__, p[0]); + return -(int)(p[0]); + } + + /* Check checksum */ + csum = cros_ec_calc_checksum(p, len + 2); + if (csum != p[len + 2]) { + debug("%s: Invalid checksum rx %#02x, calced %#02x\n", __func__, + p[2 + len], csum); + return -1; + } + + /* Anything else is the response data */ + *dinp = p + 2; + + return len; +} + +int cros_ec_spi_decode_fdt(struct cros_ec_dev *dev, const void *blob) +{ + /* Decode interface-specific FDT params */ + dev->max_frequency = fdtdec_get_int(blob, dev->node, + "spi-max-frequency", 500000); + dev->cs = fdtdec_get_int(blob, dev->node, "reg", 0); + + return 0; +} + +/** + * Initialize SPI protocol. + * + * @param dev CROS_EC device + * @param blob Device tree blob + * @return 0 if ok, -1 on error + */ +int cros_ec_spi_init(struct cros_ec_dev *dev, const void *blob) +{ + dev->spi = spi_setup_slave_fdt(blob, dev->parent_node, + dev->cs, dev->max_frequency, 0); + if (!dev->spi) { + debug("%s: Could not setup SPI slave\n", __func__); + return -1; + } + + return 0; +} diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 83d2df7744..73f7195792 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -254,7 +254,7 @@ err_out: } static unsigned long -mmc_berase(int dev_num, unsigned long start, lbaint_t blkcnt) +mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt) { int err = 0; struct mmc *mmc = find_mmc_device(dev_num); @@ -266,7 +266,8 @@ mmc_berase(int dev_num, unsigned long start, lbaint_t blkcnt) if ((start % mmc->erase_grp_size) || (blkcnt % mmc->erase_grp_size)) printf("\n\nCaution! Your devices Erase group is 0x%x\n" - "The erase range would be change to 0x%lx~0x%lx\n\n", + "The erase range would be change to " + "0x" LBAF "~0x" LBAF "\n\n", mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1), ((start + blkcnt + mmc->erase_grp_size) & ~(mmc->erase_grp_size - 1)) - 1); @@ -289,14 +290,14 @@ mmc_berase(int dev_num, unsigned long start, lbaint_t blkcnt) } static ulong -mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src) +mmc_write_blocks(struct mmc *mmc, lbaint_t start, lbaint_t blkcnt, const void*src) { struct mmc_cmd cmd; struct mmc_data data; int timeout = 1000; if ((start + blkcnt) > mmc->block_dev.lba) { - printf("MMC: block number 0x%lx exceeds max(0x%lx)\n", + printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", start + blkcnt, mmc->block_dev.lba); return 0; } @@ -346,7 +347,7 @@ mmc_write_blocks(struct mmc *mmc, ulong start, lbaint_t blkcnt, const void*src) } static ulong -mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void*src) +mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, const void*src) { lbaint_t cur, blocks_todo = blkcnt; @@ -369,7 +370,7 @@ mmc_bwrite(int dev_num, ulong start, lbaint_t blkcnt, const void*src) return blkcnt; } -static int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start, +static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start, lbaint_t blkcnt) { struct mmc_cmd cmd; @@ -408,7 +409,7 @@ static int mmc_read_blocks(struct mmc *mmc, void *dst, ulong start, return blkcnt; } -static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst) +static ulong mmc_bread(int dev_num, lbaint_t start, lbaint_t blkcnt, void *dst) { lbaint_t cur, blocks_todo = blkcnt; @@ -420,7 +421,7 @@ static ulong mmc_bread(int dev_num, ulong start, lbaint_t blkcnt, void *dst) return 0; if ((start + blkcnt) > mmc->block_dev.lba) { - printf("MMC: block number 0x%lx exceeds max(0x%lx)\n", + printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", start + blkcnt, mmc->block_dev.lba); return 0; } diff --git a/drivers/mtd/cfi_flash.c b/drivers/mtd/cfi_flash.c index 25f875202c..25a5710758 100644 --- a/drivers/mtd/cfi_flash.c +++ b/drivers/mtd/cfi_flash.c @@ -1797,7 +1797,7 @@ static int flash_detect_legacy(phys_addr_t base, int banknum) }; int i; - for (i = 0; i < sizeof(modes) / sizeof(modes[0]); i++) { + for (i = 0; i < ARRAY_SIZE(modes); i++) { info->vendor = modes[i]; info->start[0] = (ulong)map_physmem(base, @@ -1883,8 +1883,7 @@ static int __flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry) /* Issue FLASH reset command */ flash_cmd_reset(info); - for (cfi_offset=0; - cfi_offset < sizeof(flash_offset_cfi) / sizeof(uint); + for (cfi_offset = 0; cfi_offset < ARRAY_SIZE(flash_offset_cfi); cfi_offset++) { flash_write_cmd (info, 0, flash_offset_cfi[cfi_offset], FLASH_CMD_CFI); @@ -2336,7 +2335,7 @@ void flash_protect_default(void) #endif #if defined(CONFIG_SYS_FLASH_AUTOPROTECT_LIST) - for (i = 0; i < (sizeof(apl) / sizeof(struct apl_s)); i++) { + for (i = 0; i < ARRAY_SIZE(apl); i++) { debug("autoprotecting from %08lx to %08lx\n", apl[i].start, apl[i].start + apl[i].size - 1); flash_protect(FLAG_PROTECT_SET, diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c index daa80038a7..a19cec5ace 100644 --- a/drivers/serial/serial.c +++ b/drivers/serial/serial.c @@ -37,7 +37,6 @@ static struct serial_device *serial_current; * Table with supported baudrates (defined in config_xyz.h) */ static const unsigned long baudrate_table[] = CONFIG_SYS_BAUDRATE_TABLE; -#define N_BAUDRATES (sizeof(baudrate_table) / sizeof(baudrate_table[0])) /** * serial_null() - Void registration routine of a serial driver @@ -74,11 +73,11 @@ static int on_baudrate(const char *name, const char *value, enum env_op op, if (gd->baudrate == baudrate) return 0; - for (i = 0; i < N_BAUDRATES; ++i) { + for (i = 0; i < ARRAY_SIZE(baudrate_table); ++i) { if (baudrate == baudrate_table[i]) break; } - if (i == N_BAUDRATES) { + if (i == ARRAY_SIZE(baudrate_table)) { if ((flags & H_FORCE) == 0) printf("## Baudrate %d bps not supported\n", baudrate); diff --git a/drivers/spi/exynos_spi.c b/drivers/spi/exynos_spi.c index 01378d098e..7a25a35aa0 100644 --- a/drivers/spi/exynos_spi.c +++ b/drivers/spi/exynos_spi.c @@ -465,6 +465,28 @@ static int process_nodes(const void *blob, int node_list[], int count) } #endif +/** + * Set up a new SPI slave for an fdt node + * + * @param blob Device tree blob + * @param node SPI peripheral node to use + * @return 0 if ok, -1 on error + */ +struct spi_slave *spi_setup_slave_fdt(const void *blob, int node, + unsigned int cs, unsigned int max_hz, unsigned int mode) +{ + struct spi_bus *bus; + unsigned int i; + + for (i = 0, bus = spi_bus; i < bus_count; i++, bus++) { + if (bus->node == node) + return spi_setup_slave(i, cs, max_hz, mode); + } + + debug("%s: Failed to find bus node %d\n", __func__, node); + return NULL; +} + /* Sadly there is no error return from this function */ void spi_init(void) { diff --git a/drivers/usb/musb/musb_hcd.c b/drivers/usb/musb/musb_hcd.c index 60e03a4bf7..7bb91e5abf 100644 --- a/drivers/usb/musb/musb_hcd.c +++ b/drivers/usb/musb/musb_hcd.c @@ -1105,8 +1105,7 @@ int usb_lowlevel_init(int index, void **controller) /* Configure all the endpoint FIFO's and start usb controller */ musbr = musb_cfg.regs; - musb_configure_ep(&epinfo[0], - sizeof(epinfo) / sizeof(struct musb_epinfo)); + musb_configure_ep(&epinfo[0], ARRAY_SIZE(epinfo)); musb_start(); /* diff --git a/drivers/usb/musb/musb_udc.c b/drivers/usb/musb/musb_udc.c index e0b4217dc3..e8a2ce0419 100644 --- a/drivers/usb/musb/musb_udc.c +++ b/drivers/usb/musb/musb_udc.c @@ -894,8 +894,7 @@ void udc_setup_ep(struct usb_device_instance *device, unsigned int id, epinfo[id * 2].epsize = endpoint->rcv_packetSize; } - musb_configure_ep(&epinfo[0], - sizeof(epinfo) / sizeof(struct musb_epinfo)); + musb_configure_ep(&epinfo[0], ARRAY_SIZE(epinfo)); } else { if (debug_level > 0) serial_printf("ERROR : %s endpoint request %d " diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h index 3e9ca1182d..8cfc3fa775 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -82,6 +82,9 @@ typedef struct global_data { unsigned long fdt_size; /* Space reserved for relocated FDT */ void **jt; /* jump table */ char env_buf[32]; /* buffer for getenv() before reloc. */ +#ifdef CONFIG_TRACE + void *trace_buff; /* The trace buffer */ +#endif struct arch_global_data arch; /* architecture-specific data */ } gd_t; #endif diff --git a/include/common.h b/include/common.h index 126891d658..e5220cf671 100644 --- a/include/common.h +++ b/include/common.h @@ -750,6 +750,10 @@ void irq_install_handler(int, interrupt_handler_t *, void *); void irq_free_handler (int); void reset_timer (void); ulong get_timer (ulong base); + +/* Return value of monotonic microsecond timer */ +unsigned long timer_get_us(void); + void enable_interrupts (void); int disable_interrupts (void); diff --git a/include/configs/coreboot.h b/include/configs/coreboot.h index 2fefdc80db..288ef8df74 100644 --- a/include/configs/coreboot.h +++ b/include/configs/coreboot.h @@ -168,6 +168,13 @@ */ #include <config_cmd_default.h> +#define CONFIG_TRACE +#define CONFIG_CMD_TRACE +#define CONFIG_TRACE_BUFFER_SIZE (16 << 20) +#define CONFIG_TRACE_EARLY_SIZE (8 << 20) +#define CONFIG_TRACE_EARLY +#define CONFIG_TRACE_EARLY_ADDR 0x01400000 + #define CONFIG_CMD_BDI #define CONFIG_CMD_BOOTD #define CONFIG_CMD_CONSOLE diff --git a/include/configs/exynos5250-dt.h b/include/configs/exynos5250-dt.h index 163243572f..e2a096b643 100644 --- a/include/configs/exynos5250-dt.h +++ b/include/configs/exynos5250-dt.h @@ -43,6 +43,14 @@ #define CONFIG_OF_CONTROL #define CONFIG_OF_SEPARATE +/* Allow tracing to be enabled */ +#define CONFIG_TRACE +#define CONFIG_CMD_TRACE +#define CONFIG_TRACE_BUFFER_SIZE (16 << 20) +#define CONFIG_TRACE_EARLY_SIZE (8 << 20) +#define CONFIG_TRACE_EARLY +#define CONFIG_TRACE_EARLY_ADDR 0x50000000 + /* Keep L2 Cache Disabled */ #define CONFIG_SYS_DCACHE_OFF @@ -82,11 +90,19 @@ #define CONFIG_BAUDRATE 115200 #define EXYNOS5_DEFAULT_UART_OFFSET 0x010000 +/* Enable keyboard */ +#define CONFIG_CROS_EC /* CROS_EC protocol */ +#define CONFIG_CROS_EC_SPI /* Support CROS_EC over SPI */ +#define CONFIG_CROS_EC_I2C /* Support CROS_EC over I2C */ +#define CONFIG_CROS_EC_KEYB /* CROS_EC keyboard input */ +#define CONFIG_CMD_CROS_EC +#define CONFIG_KEYBOARD + /* Console configuration */ #define CONFIG_CONSOLE_MUX #define CONFIG_SYS_CONSOLE_IS_IN_ENV #define EXYNOS_DEVICE_SETTINGS \ - "stdin=serial\0" \ + "stdin=serial,cros-ec-keyb\0" \ "stdout=serial,lcd\0" \ "stderr=serial,lcd\0" diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h index 788207d007..98dd083302 100644 --- a/include/configs/sandbox.h +++ b/include/configs/sandbox.h @@ -22,6 +22,19 @@ #ifndef __CONFIG_H #define __CONFIG_H +#ifdef FTRACE +#define CONFIG_TRACE +#define CONFIG_CMD_TRACE +#define CONFIG_TRACE_BUFFER_SIZE (16 << 20) +#define CONFIG_TRACE_EARLY_SIZE (8 << 20) +#define CONFIG_TRACE_EARLY +#define CONFIG_TRACE_EARLY_ADDR 0x00100000 + +#endif + +#define CONFIG_BOOTSTAGE +#define CONFIG_BOOTSTAGE_REPORT + /* Number of bits in a C 'long' on this architecture */ #define CONFIG_SANDBOX_BITS_PER_LONG 64 @@ -30,6 +43,8 @@ #define CONFIG_OF_LIBFDT #define CONFIG_LMB #define CONFIG_FIT +#define CONFIG_FIT_SIGNATURE +#define CONFIG_RSA #define CONFIG_CMD_FDT #define CONFIG_FS_FAT diff --git a/include/cros_ec.h b/include/cros_ec.h new file mode 100644 index 0000000000..335d5b4e63 --- /dev/null +++ b/include/cros_ec.h @@ -0,0 +1,449 @@ +/* + * Chromium OS cros_ec driver + * + * Copyright (c) 2012 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _CROS_EC_H +#define _CROS_EC_H + +#include <linux/compiler.h> +#include <ec_commands.h> +#include <fdtdec.h> +#include <cros_ec_message.h> + +/* Which interface is the device on? */ +enum cros_ec_interface_t { + CROS_EC_IF_NONE, + CROS_EC_IF_SPI, + CROS_EC_IF_I2C, + CROS_EC_IF_LPC, /* Intel Low Pin Count interface */ +}; + +/* Our configuration information */ +struct cros_ec_dev { + enum cros_ec_interface_t interface; + struct spi_slave *spi; /* Our SPI slave, if using SPI */ + int node; /* Our node */ + int parent_node; /* Our parent node (interface) */ + unsigned int cs; /* Our chip select */ + unsigned int addr; /* Device address (for I2C) */ + unsigned int bus_num; /* Bus number (for I2C) */ + unsigned int max_frequency; /* Maximum interface frequency */ + struct fdt_gpio_state ec_int; /* GPIO used as EC interrupt line */ + int cmd_version_is_supported; /* Device supports command versions */ + int optimise_flash_write; /* Don't write erased flash blocks */ + + /* + * These two buffers will always be dword-aligned and include enough + * space for up to 7 word-alignment bytes also, so we can ensure that + * the body of the message is always dword-aligned (64-bit). + * + * We use this alignment to keep ARM and x86 happy. Probably word + * alignment would be OK, there might be a small performance advantage + * to using dword. + */ + uint8_t din[ALIGN(MSG_BYTES + sizeof(int64_t), sizeof(int64_t))] + __aligned(sizeof(int64_t)); + uint8_t dout[ALIGN(MSG_BYTES + sizeof(int64_t), sizeof(int64_t))] + __aligned(sizeof(int64_t)); +}; + +/* + * Hard-code the number of columns we happen to know we have right now. It + * would be more correct to call cros_ec_info() at startup and determine the + * actual number of keyboard cols from there. + */ +#define CROS_EC_KEYSCAN_COLS 13 + +/* Information returned by a key scan */ +struct mbkp_keyscan { + uint8_t data[CROS_EC_KEYSCAN_COLS]; +}; + +/** + * Read the ID of the CROS-EC device + * + * The ID is a string identifying the CROS-EC device. + * + * @param dev CROS-EC device + * @param id Place to put the ID + * @param maxlen Maximum length of the ID field + * @return 0 if ok, -1 on error + */ +int cros_ec_read_id(struct cros_ec_dev *dev, char *id, int maxlen); + +/** + * Read a keyboard scan from the CROS-EC device + * + * Send a message requesting a keyboard scan and return the result + * + * @param dev CROS-EC device + * @param scan Place to put the scan results + * @return 0 if ok, -1 on error + */ +int cros_ec_scan_keyboard(struct cros_ec_dev *dev, struct mbkp_keyscan *scan); + +/** + * Read which image is currently running on the CROS-EC device. + * + * @param dev CROS-EC device + * @param image Destination for image identifier + * @return 0 if ok, <0 on error + */ +int cros_ec_read_current_image(struct cros_ec_dev *dev, + enum ec_current_image *image); + +/** + * Read the hash of the CROS-EC device firmware. + * + * @param dev CROS-EC device + * @param hash Destination for hash information + * @return 0 if ok, <0 on error + */ +int cros_ec_read_hash(struct cros_ec_dev *dev, + struct ec_response_vboot_hash *hash); + +/** + * Send a reboot command to the CROS-EC device. + * + * Note that some reboot commands (such as EC_REBOOT_COLD) also reboot the AP. + * + * @param dev CROS-EC device + * @param cmd Reboot command + * @param flags Flags for reboot command (EC_REBOOT_FLAG_*) + * @return 0 if ok, <0 on error + */ +int cros_ec_reboot(struct cros_ec_dev *dev, enum ec_reboot_cmd cmd, + uint8_t flags); + +/** + * Check if the CROS-EC device has an interrupt pending. + * + * Read the status of the external interrupt connected to the CROS-EC device. + * If no external interrupt is configured, this always returns 1. + * + * @param dev CROS-EC device + * @return 0 if no interrupt is pending + */ +int cros_ec_interrupt_pending(struct cros_ec_dev *dev); + +enum { + CROS_EC_OK, + CROS_EC_ERR = 1, + CROS_EC_ERR_FDT_DECODE, + CROS_EC_ERR_CHECK_VERSION, + CROS_EC_ERR_READ_ID, + CROS_EC_ERR_DEV_INIT, +}; + +/** + * Set up the Chromium OS matrix keyboard protocol + * + * @param blob Device tree blob containing setup information + * @param cros_ecp Returns pointer to the cros_ec device, or NULL if none + * @return 0 if we got an cros_ec device and all is well (or no cros_ec is + * expected), -ve if we should have an cros_ec device but failed to find + * one, or init failed (-CROS_EC_ERR_...). + */ +int cros_ec_init(const void *blob, struct cros_ec_dev **cros_ecp); + +/** + * Read information about the keyboard matrix + * + * @param dev CROS-EC device + * @param info Place to put the info structure + */ +int cros_ec_info(struct cros_ec_dev *dev, + struct ec_response_cros_ec_info *info); + +/** + * Read the host event flags + * + * @param dev CROS-EC device + * @param events_ptr Destination for event flags. Not changed on error. + * @return 0 if ok, <0 on error + */ +int cros_ec_get_host_events(struct cros_ec_dev *dev, uint32_t *events_ptr); + +/** + * Clear the specified host event flags + * + * @param dev CROS-EC device + * @param events Event flags to clear + * @return 0 if ok, <0 on error + */ +int cros_ec_clear_host_events(struct cros_ec_dev *dev, uint32_t events); + +/** + * Get/set flash protection + * + * @param dev CROS-EC device + * @param set_mask Mask of flags to set; if 0, just retrieves existing + * protection state without changing it. + * @param set_flags New flag values; only bits in set_mask are applied; + * ignored if set_mask=0. + * @param prot Destination for updated protection state from EC. + * @return 0 if ok, <0 on error + */ +int cros_ec_flash_protect(struct cros_ec_dev *dev, + uint32_t set_mask, uint32_t set_flags, + struct ec_response_flash_protect *resp); + + +/** + * Run internal tests on the cros_ec interface. + * + * @param dev CROS-EC device + * @return 0 if ok, <0 if the test failed + */ +int cros_ec_test(struct cros_ec_dev *dev); + +/** + * Update the EC RW copy. + * + * @param dev CROS-EC device + * @param image the content to write + * @param imafge_size content length + * @return 0 if ok, <0 if the test failed + */ +int cros_ec_flash_update_rw(struct cros_ec_dev *dev, + const uint8_t *image, int image_size); + +/** + * Return a pointer to the board's CROS-EC device + * + * This should be implemented by board files. + * + * @return pointer to CROS-EC device, or NULL if none is available + */ +struct cros_ec_dev *board_get_cros_ec_dev(void); + + +/* Internal interfaces */ +int cros_ec_i2c_init(struct cros_ec_dev *dev, const void *blob); +int cros_ec_spi_init(struct cros_ec_dev *dev, const void *blob); +int cros_ec_lpc_init(struct cros_ec_dev *dev, const void *blob); + +/** + * Read information from the fdt for the i2c cros_ec interface + * + * @param dev CROS-EC device + * @param blob Device tree blob + * @return 0 if ok, -1 if we failed to read all required information + */ +int cros_ec_i2c_decode_fdt(struct cros_ec_dev *dev, const void *blob); + +/** + * Read information from the fdt for the spi cros_ec interface + * + * @param dev CROS-EC device + * @param blob Device tree blob + * @return 0 if ok, -1 if we failed to read all required information + */ +int cros_ec_spi_decode_fdt(struct cros_ec_dev *dev, const void *blob); + +/** + * Check whether the LPC interface supports new-style commands. + * + * LPC has its own way of doing this, which involves checking LPC values + * visible to the host. Do this, and update dev->cmd_version_is_supported + * accordingly. + * + * @param dev CROS-EC device to check + */ +int cros_ec_lpc_check_version(struct cros_ec_dev *dev); + +/** + * Send a command to an I2C CROS-EC device and return the reply. + * + * This rather complicated function deals with sending both old-style and + * new-style commands. The old ones have just a command byte and arguments. + * The new ones have version, command, arg-len, [args], chksum so are 3 bytes + * longer. + * + * The device's internal input/output buffers are used. + * + * @param dev CROS-EC device + * @param cmd Command to send (EC_CMD_...) + * @param cmd_version Version of command to send (EC_VER_...) + * @param dout Output data (may be NULL If dout_len=0) + * @param dout_len Size of output data in bytes + * @param dinp Returns pointer to response data + * @param din_len Maximum size of response in bytes + * @return number of bytes in response, or -1 on error + */ +int cros_ec_i2c_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, + const uint8_t *dout, int dout_len, + uint8_t **dinp, int din_len); + +/** + * Send a command to a LPC CROS-EC device and return the reply. + * + * The device's internal input/output buffers are used. + * + * @param dev CROS-EC device + * @param cmd Command to send (EC_CMD_...) + * @param cmd_version Version of command to send (EC_VER_...) + * @param dout Output data (may be NULL If dout_len=0) + * @param dout_len Size of output data in bytes + * @param dinp Returns pointer to response data + * @param din_len Maximum size of response in bytes + * @return number of bytes in response, or -1 on error + */ +int cros_ec_lpc_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, + const uint8_t *dout, int dout_len, + uint8_t **dinp, int din_len); + +int cros_ec_spi_command(struct cros_ec_dev *dev, uint8_t cmd, int cmd_version, + const uint8_t *dout, int dout_len, + uint8_t **dinp, int din_len); + +/** + * Dump a block of data for a command. + * + * @param name Name for data (e.g. 'in', 'out') + * @param cmd Command number associated with data, or -1 for none + * @param data Data block to dump + * @param len Length of data block to dump + */ +void cros_ec_dump_data(const char *name, int cmd, const uint8_t *data, int len); + +/** + * Calculate a simple 8-bit checksum of a data block + * + * @param data Data block to checksum + * @param size Size of data block in bytes + * @return checksum value (0 to 255) + */ +int cros_ec_calc_checksum(const uint8_t *data, int size); + +/** + * Decode a flash region parameter + * + * @param argc Number of params remaining + * @param argv List of remaining parameters + * @return flash region (EC_FLASH_REGION_...) or -1 on error + */ +int cros_ec_decode_region(int argc, char * const argv[]); + +int cros_ec_flash_erase(struct cros_ec_dev *dev, uint32_t offset, + uint32_t size); + +/** + * Read data from the flash + * + * Read an arbitrary amount of data from the EC flash, by repeatedly reading + * small blocks. + * + * The offset starts at 0. You can obtain the region information from + * cros_ec_flash_offset() to find out where to read for a particular region. + * + * @param dev CROS-EC device + * @param data Pointer to data buffer to read into + * @param offset Offset within flash to read from + * @param size Number of bytes to read + * @return 0 if ok, -1 on error + */ +int cros_ec_flash_read(struct cros_ec_dev *dev, uint8_t *data, uint32_t offset, + uint32_t size); + +/** + * Write data to the flash + * + * Write an arbitrary amount of data to the EC flash, by repeatedly writing + * small blocks. + * + * The offset starts at 0. You can obtain the region information from + * cros_ec_flash_offset() to find out where to write for a particular region. + * + * Attempting to write to the region where the EC is currently running from + * will result in an error. + * + * @param dev CROS-EC device + * @param data Pointer to data buffer to write + * @param offset Offset within flash to write to. + * @param size Number of bytes to write + * @return 0 if ok, -1 on error + */ +int cros_ec_flash_write(struct cros_ec_dev *dev, const uint8_t *data, + uint32_t offset, uint32_t size); + +/** + * Obtain position and size of a flash region + * + * @param dev CROS-EC device + * @param region Flash region to query + * @param offset Returns offset of flash region in EC flash + * @param size Returns size of flash region + * @return 0 if ok, -1 on error + */ +int cros_ec_flash_offset(struct cros_ec_dev *dev, enum ec_flash_region region, + uint32_t *offset, uint32_t *size); + +/** + * Read/write VbNvContext from/to a CROS-EC device. + * + * @param dev CROS-EC device + * @param block Buffer of VbNvContext to be read/write + * @return 0 if ok, -1 on error + */ +int cros_ec_read_vbnvcontext(struct cros_ec_dev *dev, uint8_t *block); +int cros_ec_write_vbnvcontext(struct cros_ec_dev *dev, const uint8_t *block); + +/** + * Read the version information for the EC images + * + * @param dev CROS-EC device + * @param versionp This is set to point to the version information + * @return 0 if ok, -1 on error + */ +int cros_ec_read_version(struct cros_ec_dev *dev, + struct ec_response_get_version **versionp); + +/** + * Read the build information for the EC + * + * @param dev CROS-EC device + * @param versionp This is set to point to the build string + * @return 0 if ok, -1 on error + */ +int cros_ec_read_build_info(struct cros_ec_dev *dev, char **strp); + +/** + * Switch on/off a LDO / FET. + * + * @param dev CROS-EC device + * @param index index of the LDO/FET to switch + * @param state new state of the LDO/FET : EC_LDO_STATE_ON|OFF + * @return 0 if ok, -1 on error + */ +int cros_ec_set_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t state); + +/** + * Read back a LDO / FET current state. + * + * @param dev CROS-EC device + * @param index index of the LDO/FET to switch + * @param state current state of the LDO/FET : EC_LDO_STATE_ON|OFF + * @return 0 if ok, -1 on error + */ +int cros_ec_get_ldo(struct cros_ec_dev *dev, uint8_t index, uint8_t *state); +#endif diff --git a/include/cros_ec_message.h b/include/cros_ec_message.h new file mode 100644 index 0000000000..a2421c7ba4 --- /dev/null +++ b/include/cros_ec_message.h @@ -0,0 +1,44 @@ +/* + * Chromium OS Matrix Keyboard Message Protocol definitions + * + * Copyright (c) 2012 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _CROS_MESSAGE_H +#define _CROS_MESSAGE_H + +/* + * Command interface between EC and AP, for LPC, I2C and SPI interfaces. + * + * This is copied from the Chromium OS Open Source Embedded Controller code. + */ +enum { + /* The header byte, which follows the preamble */ + MSG_HEADER = 0xec, + + MSG_HEADER_BYTES = 3, + MSG_TRAILER_BYTES = 2, + MSG_PROTO_BYTES = MSG_HEADER_BYTES + MSG_TRAILER_BYTES, + + /* Max length of messages */ + MSG_BYTES = EC_HOST_PARAM_SIZE + MSG_PROTO_BYTES, +}; + +#endif diff --git a/include/ec_commands.h b/include/ec_commands.h new file mode 100644 index 0000000000..12811cc070 --- /dev/null +++ b/include/ec_commands.h @@ -0,0 +1,1440 @@ +/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* Host communication command constants for Chrome EC */ + +#ifndef __CROS_EC_COMMANDS_H +#define __CROS_EC_COMMANDS_H + +/* + * Protocol overview + * + * request: CMD [ P0 P1 P2 ... Pn S ] + * response: ERR [ P0 P1 P2 ... Pn S ] + * + * where the bytes are defined as follow : + * - CMD is the command code. (defined by EC_CMD_ constants) + * - ERR is the error code. (defined by EC_RES_ constants) + * - Px is the optional payload. + * it is not sent if the error code is not success. + * (defined by ec_params_ and ec_response_ structures) + * - S is the checksum which is the sum of all payload bytes. + * + * On LPC, CMD and ERR are sent/received at EC_LPC_ADDR_KERNEL|USER_CMD + * and the payloads are sent/received at EC_LPC_ADDR_KERNEL|USER_PARAM. + * On I2C, all bytes are sent serially in the same message. + */ + +/* Current version of this protocol */ +#define EC_PROTO_VERSION 0x00000002 + +/* Command version mask */ +#define EC_VER_MASK(version) (1UL << (version)) + +/* I/O addresses for ACPI commands */ +#define EC_LPC_ADDR_ACPI_DATA 0x62 +#define EC_LPC_ADDR_ACPI_CMD 0x66 + +/* I/O addresses for host command */ +#define EC_LPC_ADDR_HOST_DATA 0x200 +#define EC_LPC_ADDR_HOST_CMD 0x204 + +/* I/O addresses for host command args and params */ +#define EC_LPC_ADDR_HOST_ARGS 0x800 +#define EC_LPC_ADDR_HOST_PARAM 0x804 +#define EC_HOST_PARAM_SIZE 0x0fc /* Size of param area in bytes */ + +/* I/O addresses for host command params, old interface */ +#define EC_LPC_ADDR_OLD_PARAM 0x880 +#define EC_OLD_PARAM_SIZE 0x080 /* Size of param area in bytes */ + +/* EC command register bit functions */ +#define EC_LPC_CMDR_DATA (1 << 0) /* Data ready for host to read */ +#define EC_LPC_CMDR_PENDING (1 << 1) /* Write pending to EC */ +#define EC_LPC_CMDR_BUSY (1 << 2) /* EC is busy processing a command */ +#define EC_LPC_CMDR_CMD (1 << 3) /* Last host write was a command */ +#define EC_LPC_CMDR_ACPI_BRST (1 << 4) /* Burst mode (not used) */ +#define EC_LPC_CMDR_SCI (1 << 5) /* SCI event is pending */ +#define EC_LPC_CMDR_SMI (1 << 6) /* SMI event is pending */ + +#define EC_LPC_ADDR_MEMMAP 0x900 +#define EC_MEMMAP_SIZE 255 /* ACPI IO buffer max is 255 bytes */ +#define EC_MEMMAP_TEXT_MAX 8 /* Size of a string in the memory map */ + +/* The offset address of each type of data in mapped memory. */ +#define EC_MEMMAP_TEMP_SENSOR 0x00 /* Temp sensors */ +#define EC_MEMMAP_FAN 0x10 /* Fan speeds */ +#define EC_MEMMAP_TEMP_SENSOR_B 0x18 /* Temp sensors (second set) */ +#define EC_MEMMAP_ID 0x20 /* 'E' 'C' */ +#define EC_MEMMAP_ID_VERSION 0x22 /* Version of data in 0x20 - 0x2f */ +#define EC_MEMMAP_THERMAL_VERSION 0x23 /* Version of data in 0x00 - 0x1f */ +#define EC_MEMMAP_BATTERY_VERSION 0x24 /* Version of data in 0x40 - 0x7f */ +#define EC_MEMMAP_SWITCHES_VERSION 0x25 /* Version of data in 0x30 - 0x33 */ +#define EC_MEMMAP_EVENTS_VERSION 0x26 /* Version of data in 0x34 - 0x3f */ +#define EC_MEMMAP_HOST_CMD_FLAGS 0x27 /* Host command interface flags */ +#define EC_MEMMAP_SWITCHES 0x30 +#define EC_MEMMAP_HOST_EVENTS 0x34 +#define EC_MEMMAP_BATT_VOLT 0x40 /* Battery Present Voltage */ +#define EC_MEMMAP_BATT_RATE 0x44 /* Battery Present Rate */ +#define EC_MEMMAP_BATT_CAP 0x48 /* Battery Remaining Capacity */ +#define EC_MEMMAP_BATT_FLAG 0x4c /* Battery State, defined below */ +#define EC_MEMMAP_BATT_DCAP 0x50 /* Battery Design Capacity */ +#define EC_MEMMAP_BATT_DVLT 0x54 /* Battery Design Voltage */ +#define EC_MEMMAP_BATT_LFCC 0x58 /* Battery Last Full Charge Capacity */ +#define EC_MEMMAP_BATT_CCNT 0x5c /* Battery Cycle Count */ +#define EC_MEMMAP_BATT_MFGR 0x60 /* Battery Manufacturer String */ +#define EC_MEMMAP_BATT_MODEL 0x68 /* Battery Model Number String */ +#define EC_MEMMAP_BATT_SERIAL 0x70 /* Battery Serial Number String */ +#define EC_MEMMAP_BATT_TYPE 0x78 /* Battery Type String */ + +/* Number of temp sensors at EC_MEMMAP_TEMP_SENSOR */ +#define EC_TEMP_SENSOR_ENTRIES 16 +/* + * Number of temp sensors at EC_MEMMAP_TEMP_SENSOR_B. + * + * Valid only if EC_MEMMAP_THERMAL_VERSION returns >= 2. + */ +#define EC_TEMP_SENSOR_B_ENTRIES 8 +#define EC_TEMP_SENSOR_NOT_PRESENT 0xff +#define EC_TEMP_SENSOR_ERROR 0xfe +#define EC_TEMP_SENSOR_NOT_POWERED 0xfd +#define EC_TEMP_SENSOR_NOT_CALIBRATED 0xfc +/* + * The offset of temperature value stored in mapped memory. This allows + * reporting a temperature range of 200K to 454K = -73C to 181C. + */ +#define EC_TEMP_SENSOR_OFFSET 200 + +#define EC_FAN_SPEED_ENTRIES 4 /* Number of fans at EC_MEMMAP_FAN */ +#define EC_FAN_SPEED_NOT_PRESENT 0xffff /* Entry not present */ +#define EC_FAN_SPEED_STALLED 0xfffe /* Fan stalled */ + +/* Battery bit flags at EC_MEMMAP_BATT_FLAG. */ +#define EC_BATT_FLAG_AC_PRESENT 0x01 +#define EC_BATT_FLAG_BATT_PRESENT 0x02 +#define EC_BATT_FLAG_DISCHARGING 0x04 +#define EC_BATT_FLAG_CHARGING 0x08 +#define EC_BATT_FLAG_LEVEL_CRITICAL 0x10 + +/* Switch flags at EC_MEMMAP_SWITCHES */ +#define EC_SWITCH_LID_OPEN 0x01 +#define EC_SWITCH_POWER_BUTTON_PRESSED 0x02 +#define EC_SWITCH_WRITE_PROTECT_DISABLED 0x04 +/* Recovery requested via keyboard */ +#define EC_SWITCH_KEYBOARD_RECOVERY 0x08 +/* Recovery requested via dedicated signal (from servo board) */ +#define EC_SWITCH_DEDICATED_RECOVERY 0x10 +/* Was fake developer mode switch; now unused. Remove in next refactor. */ +#define EC_SWITCH_IGNORE0 0x20 + +/* Host command interface flags */ +/* Host command interface supports LPC args (LPC interface only) */ +#define EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED 0x01 + +/* Wireless switch flags */ +#define EC_WIRELESS_SWITCH_WLAN 0x01 +#define EC_WIRELESS_SWITCH_BLUETOOTH 0x02 + +/* + * This header file is used in coreboot both in C and ACPI code. The ACPI code + * is pre-processed to handle constants but the ASL compiler is unable to + * handle actual C code so keep it separate. + */ +#ifndef __ACPI__ + +/* + * Define __packed if someone hasn't beat us to it. Linux kernel style + * checking prefers __packed over __attribute__((packed)). + */ +#ifndef __packed +#define __packed __attribute__((packed)) +#endif + +/* LPC command status byte masks */ +/* EC has written a byte in the data register and host hasn't read it yet */ +#define EC_LPC_STATUS_TO_HOST 0x01 +/* Host has written a command/data byte and the EC hasn't read it yet */ +#define EC_LPC_STATUS_FROM_HOST 0x02 +/* EC is processing a command */ +#define EC_LPC_STATUS_PROCESSING 0x04 +/* Last write to EC was a command, not data */ +#define EC_LPC_STATUS_LAST_CMD 0x08 +/* EC is in burst mode. Unsupported by Chrome EC, so this bit is never set */ +#define EC_LPC_STATUS_BURST_MODE 0x10 +/* SCI event is pending (requesting SCI query) */ +#define EC_LPC_STATUS_SCI_PENDING 0x20 +/* SMI event is pending (requesting SMI query) */ +#define EC_LPC_STATUS_SMI_PENDING 0x40 +/* (reserved) */ +#define EC_LPC_STATUS_RESERVED 0x80 + +/* + * EC is busy. This covers both the EC processing a command, and the host has + * written a new command but the EC hasn't picked it up yet. + */ +#define EC_LPC_STATUS_BUSY_MASK \ + (EC_LPC_STATUS_FROM_HOST | EC_LPC_STATUS_PROCESSING) + +/* Host command response codes */ +enum ec_status { + EC_RES_SUCCESS = 0, + EC_RES_INVALID_COMMAND = 1, + EC_RES_ERROR = 2, + EC_RES_INVALID_PARAM = 3, + EC_RES_ACCESS_DENIED = 4, + EC_RES_INVALID_RESPONSE = 5, + EC_RES_INVALID_VERSION = 6, + EC_RES_INVALID_CHECKSUM = 7, + EC_RES_IN_PROGRESS = 8, /* Accepted, command in progress */ + EC_RES_UNAVAILABLE = 9, /* No response available */ + EC_RES_TIMEOUT = 10, /* We got a timeout */ + EC_RES_OVERFLOW = 11, /* Table / data overflow */ +}; + +/* + * Host event codes. Note these are 1-based, not 0-based, because ACPI query + * EC command uses code 0 to mean "no event pending". We explicitly specify + * each value in the enum listing so they won't change if we delete/insert an + * item or rearrange the list (it needs to be stable across platforms, not + * just within a single compiled instance). + */ +enum host_event_code { + EC_HOST_EVENT_LID_CLOSED = 1, + EC_HOST_EVENT_LID_OPEN = 2, + EC_HOST_EVENT_POWER_BUTTON = 3, + EC_HOST_EVENT_AC_CONNECTED = 4, + EC_HOST_EVENT_AC_DISCONNECTED = 5, + EC_HOST_EVENT_BATTERY_LOW = 6, + EC_HOST_EVENT_BATTERY_CRITICAL = 7, + EC_HOST_EVENT_BATTERY = 8, + EC_HOST_EVENT_THERMAL_THRESHOLD = 9, + EC_HOST_EVENT_THERMAL_OVERLOAD = 10, + EC_HOST_EVENT_THERMAL = 11, + EC_HOST_EVENT_USB_CHARGER = 12, + EC_HOST_EVENT_KEY_PRESSED = 13, + /* + * EC has finished initializing the host interface. The host can check + * for this event following sending a EC_CMD_REBOOT_EC command to + * determine when the EC is ready to accept subsequent commands. + */ + EC_HOST_EVENT_INTERFACE_READY = 14, + /* Keyboard recovery combo has been pressed */ + EC_HOST_EVENT_KEYBOARD_RECOVERY = 15, + + /* Shutdown due to thermal overload */ + EC_HOST_EVENT_THERMAL_SHUTDOWN = 16, + /* Shutdown due to battery level too low */ + EC_HOST_EVENT_BATTERY_SHUTDOWN = 17, + + /* + * The high bit of the event mask is not used as a host event code. If + * it reads back as set, then the entire event mask should be + * considered invalid by the host. This can happen when reading the + * raw event status via EC_MEMMAP_HOST_EVENTS but the LPC interface is + * not initialized on the EC, or improperly configured on the host. + */ + EC_HOST_EVENT_INVALID = 32 +}; +/* Host event mask */ +#define EC_HOST_EVENT_MASK(event_code) (1UL << ((event_code) - 1)) + +/* Arguments at EC_LPC_ADDR_HOST_ARGS */ +struct ec_lpc_host_args { + uint8_t flags; + uint8_t command_version; + uint8_t data_size; + /* + * Checksum; sum of command + flags + command_version + data_size + + * all params/response data bytes. + */ + uint8_t checksum; +} __packed; + +/* Flags for ec_lpc_host_args.flags */ +/* + * Args are from host. Data area at EC_LPC_ADDR_HOST_PARAM contains command + * params. + * + * If EC gets a command and this flag is not set, this is an old-style command. + * Command version is 0 and params from host are at EC_LPC_ADDR_OLD_PARAM with + * unknown length. EC must respond with an old-style response (that is, + * withouth setting EC_HOST_ARGS_FLAG_TO_HOST). + */ +#define EC_HOST_ARGS_FLAG_FROM_HOST 0x01 +/* + * Args are from EC. Data area at EC_LPC_ADDR_HOST_PARAM contains response. + * + * If EC responds to a command and this flag is not set, this is an old-style + * response. Command version is 0 and response data from EC is at + * EC_LPC_ADDR_OLD_PARAM with unknown length. + */ +#define EC_HOST_ARGS_FLAG_TO_HOST 0x02 + +/* + * Notes on commands: + * + * Each command is an 8-byte command value. Commands which take params or + * return response data specify structs for that data. If no struct is + * specified, the command does not input or output data, respectively. + * Parameter/response length is implicit in the structs. Some underlying + * communication protocols (I2C, SPI) may add length or checksum headers, but + * those are implementation-dependent and not defined here. + */ + +/*****************************************************************************/ +/* General / test commands */ + +/* + * Get protocol version, used to deal with non-backward compatible protocol + * changes. + */ +#define EC_CMD_PROTO_VERSION 0x00 + +struct ec_response_proto_version { + uint32_t version; +} __packed; + +/* + * Hello. This is a simple command to test the EC is responsive to + * commands. + */ +#define EC_CMD_HELLO 0x01 + +struct ec_params_hello { + uint32_t in_data; /* Pass anything here */ +} __packed; + +struct ec_response_hello { + uint32_t out_data; /* Output will be in_data + 0x01020304 */ +} __packed; + +/* Get version number */ +#define EC_CMD_GET_VERSION 0x02 + +enum ec_current_image { + EC_IMAGE_UNKNOWN = 0, + EC_IMAGE_RO, + EC_IMAGE_RW +}; + +struct ec_response_get_version { + /* Null-terminated version strings for RO, RW */ + char version_string_ro[32]; + char version_string_rw[32]; + char reserved[32]; /* Was previously RW-B string */ + uint32_t current_image; /* One of ec_current_image */ +} __packed; + +/* Read test */ +#define EC_CMD_READ_TEST 0x03 + +struct ec_params_read_test { + uint32_t offset; /* Starting value for read buffer */ + uint32_t size; /* Size to read in bytes */ +} __packed; + +struct ec_response_read_test { + uint32_t data[32]; +} __packed; + +/* + * Get build information + * + * Response is null-terminated string. + */ +#define EC_CMD_GET_BUILD_INFO 0x04 + +/* Get chip info */ +#define EC_CMD_GET_CHIP_INFO 0x05 + +struct ec_response_get_chip_info { + /* Null-terminated strings */ + char vendor[32]; + char name[32]; + char revision[32]; /* Mask version */ +} __packed; + +/* Get board HW version */ +#define EC_CMD_GET_BOARD_VERSION 0x06 + +struct ec_response_board_version { + uint16_t board_version; /* A monotonously incrementing number. */ +} __packed; + +/* + * Read memory-mapped data. + * + * This is an alternate interface to memory-mapped data for bus protocols + * which don't support direct-mapped memory - I2C, SPI, etc. + * + * Response is params.size bytes of data. + */ +#define EC_CMD_READ_MEMMAP 0x07 + +struct ec_params_read_memmap { + uint8_t offset; /* Offset in memmap (EC_MEMMAP_*) */ + uint8_t size; /* Size to read in bytes */ +} __packed; + +/* Read versions supported for a command */ +#define EC_CMD_GET_CMD_VERSIONS 0x08 + +struct ec_params_get_cmd_versions { + uint8_t cmd; /* Command to check */ +} __packed; + +struct ec_response_get_cmd_versions { + /* + * Mask of supported versions; use EC_VER_MASK() to compare with a + * desired version. + */ + uint32_t version_mask; +} __packed; + +/* + * Check EC communcations status (busy). This is needed on i2c/spi but not + * on lpc since it has its own out-of-band busy indicator. + * + * lpc must read the status from the command register. Attempting this on + * lpc will overwrite the args/parameter space and corrupt its data. + */ +#define EC_CMD_GET_COMMS_STATUS 0x09 + +/* Avoid using ec_status which is for return values */ +enum ec_comms_status { + EC_COMMS_STATUS_PROCESSING = 1 << 0, /* Processing cmd */ +}; + +struct ec_response_get_comms_status { + uint32_t flags; /* Mask of enum ec_comms_status */ +} __packed; + + +/*****************************************************************************/ +/* Flash commands */ + +/* Get flash info */ +#define EC_CMD_FLASH_INFO 0x10 + +struct ec_response_flash_info { + /* Usable flash size, in bytes */ + uint32_t flash_size; + /* + * Write block size. Write offset and size must be a multiple + * of this. + */ + uint32_t write_block_size; + /* + * Erase block size. Erase offset and size must be a multiple + * of this. + */ + uint32_t erase_block_size; + /* + * Protection block size. Protection offset and size must be a + * multiple of this. + */ + uint32_t protect_block_size; +} __packed; + +/* + * Read flash + * + * Response is params.size bytes of data. + */ +#define EC_CMD_FLASH_READ 0x11 + +struct ec_params_flash_read { + uint32_t offset; /* Byte offset to read */ + uint32_t size; /* Size to read in bytes */ +} __packed; + +/* Write flash */ +#define EC_CMD_FLASH_WRITE 0x12 + +struct ec_params_flash_write { + uint32_t offset; /* Byte offset to write */ + uint32_t size; /* Size to write in bytes */ + /* + * Data to write. Could really use EC_PARAM_SIZE - 8, but tidiest to + * use a power of 2 so writes stay aligned. + */ + uint8_t data[64]; +} __packed; + +/* Erase flash */ +#define EC_CMD_FLASH_ERASE 0x13 + +struct ec_params_flash_erase { + uint32_t offset; /* Byte offset to erase */ + uint32_t size; /* Size to erase in bytes */ +} __packed; + +/* + * Get/set flash protection. + * + * If mask!=0, sets/clear the requested bits of flags. Depending on the + * firmware write protect GPIO, not all flags will take effect immediately; + * some flags require a subsequent hard reset to take effect. Check the + * returned flags bits to see what actually happened. + * + * If mask=0, simply returns the current flags state. + */ +#define EC_CMD_FLASH_PROTECT 0x15 +#define EC_VER_FLASH_PROTECT 1 /* Command version 1 */ + +/* Flags for flash protection */ +/* RO flash code protected when the EC boots */ +#define EC_FLASH_PROTECT_RO_AT_BOOT (1 << 0) +/* + * RO flash code protected now. If this bit is set, at-boot status cannot + * be changed. + */ +#define EC_FLASH_PROTECT_RO_NOW (1 << 1) +/* Entire flash code protected now, until reboot. */ +#define EC_FLASH_PROTECT_ALL_NOW (1 << 2) +/* Flash write protect GPIO is asserted now */ +#define EC_FLASH_PROTECT_GPIO_ASSERTED (1 << 3) +/* Error - at least one bank of flash is stuck locked, and cannot be unlocked */ +#define EC_FLASH_PROTECT_ERROR_STUCK (1 << 4) +/* + * Error - flash protection is in inconsistent state. At least one bank of + * flash which should be protected is not protected. Usually fixed by + * re-requesting the desired flags, or by a hard reset if that fails. + */ +#define EC_FLASH_PROTECT_ERROR_INCONSISTENT (1 << 5) +/* Entile flash code protected when the EC boots */ +#define EC_FLASH_PROTECT_ALL_AT_BOOT (1 << 6) + +struct ec_params_flash_protect { + uint32_t mask; /* Bits in flags to apply */ + uint32_t flags; /* New flags to apply */ +} __packed; + +struct ec_response_flash_protect { + /* Current value of flash protect flags */ + uint32_t flags; + /* + * Flags which are valid on this platform. This allows the caller + * to distinguish between flags which aren't set vs. flags which can't + * be set on this platform. + */ + uint32_t valid_flags; + /* Flags which can be changed given the current protection state */ + uint32_t writable_flags; +} __packed; + +/* + * Note: commands 0x14 - 0x19 version 0 were old commands to get/set flash + * write protect. These commands may be reused with version > 0. + */ + +/* Get the region offset/size */ +#define EC_CMD_FLASH_REGION_INFO 0x16 +#define EC_VER_FLASH_REGION_INFO 1 + +enum ec_flash_region { + /* Region which holds read-only EC image */ + EC_FLASH_REGION_RO, + /* Region which holds rewritable EC image */ + EC_FLASH_REGION_RW, + /* + * Region which should be write-protected in the factory (a superset of + * EC_FLASH_REGION_RO) + */ + EC_FLASH_REGION_WP_RO, +}; + +struct ec_params_flash_region_info { + uint32_t region; /* enum ec_flash_region */ +} __packed; + +struct ec_response_flash_region_info { + uint32_t offset; + uint32_t size; +} __packed; + +/* Read/write VbNvContext */ +#define EC_CMD_VBNV_CONTEXT 0x17 +#define EC_VER_VBNV_CONTEXT 1 +#define EC_VBNV_BLOCK_SIZE 16 + +enum ec_vbnvcontext_op { + EC_VBNV_CONTEXT_OP_READ, + EC_VBNV_CONTEXT_OP_WRITE, +}; + +struct ec_params_vbnvcontext { + uint32_t op; + uint8_t block[EC_VBNV_BLOCK_SIZE]; +} __packed; + +struct ec_response_vbnvcontext { + uint8_t block[EC_VBNV_BLOCK_SIZE]; +} __packed; + +/*****************************************************************************/ +/* PWM commands */ + +/* Get fan target RPM */ +#define EC_CMD_PWM_GET_FAN_TARGET_RPM 0x20 + +struct ec_response_pwm_get_fan_rpm { + uint32_t rpm; +} __packed; + +/* Set target fan RPM */ +#define EC_CMD_PWM_SET_FAN_TARGET_RPM 0x21 + +struct ec_params_pwm_set_fan_target_rpm { + uint32_t rpm; +} __packed; + +/* Get keyboard backlight */ +#define EC_CMD_PWM_GET_KEYBOARD_BACKLIGHT 0x22 + +struct ec_response_pwm_get_keyboard_backlight { + uint8_t percent; + uint8_t enabled; +} __packed; + +/* Set keyboard backlight */ +#define EC_CMD_PWM_SET_KEYBOARD_BACKLIGHT 0x23 + +struct ec_params_pwm_set_keyboard_backlight { + uint8_t percent; +} __packed; + +/* Set target fan PWM duty cycle */ +#define EC_CMD_PWM_SET_FAN_DUTY 0x24 + +struct ec_params_pwm_set_fan_duty { + uint32_t percent; +} __packed; + +/*****************************************************************************/ +/* + * Lightbar commands. This looks worse than it is. Since we only use one HOST + * command to say "talk to the lightbar", we put the "and tell it to do X" part + * into a subcommand. We'll make separate structs for subcommands with + * different input args, so that we know how much to expect. + */ +#define EC_CMD_LIGHTBAR_CMD 0x28 + +struct rgb_s { + uint8_t r, g, b; +}; + +#define LB_BATTERY_LEVELS 4 +/* List of tweakable parameters. NOTE: It's __packed so it can be sent in a + * host command, but the alignment is the same regardless. Keep it that way. + */ +struct lightbar_params { + /* Timing */ + int google_ramp_up; + int google_ramp_down; + int s3s0_ramp_up; + int s0_tick_delay[2]; /* AC=0/1 */ + int s0a_tick_delay[2]; /* AC=0/1 */ + int s0s3_ramp_down; + int s3_sleep_for; + int s3_ramp_up; + int s3_ramp_down; + + /* Oscillation */ + uint8_t new_s0; + uint8_t osc_min[2]; /* AC=0/1 */ + uint8_t osc_max[2]; /* AC=0/1 */ + uint8_t w_ofs[2]; /* AC=0/1 */ + + /* Brightness limits based on the backlight and AC. */ + uint8_t bright_bl_off_fixed[2]; /* AC=0/1 */ + uint8_t bright_bl_on_min[2]; /* AC=0/1 */ + uint8_t bright_bl_on_max[2]; /* AC=0/1 */ + + /* Battery level thresholds */ + uint8_t battery_threshold[LB_BATTERY_LEVELS - 1]; + + /* Map [AC][battery_level] to color index */ + uint8_t s0_idx[2][LB_BATTERY_LEVELS]; /* AP is running */ + uint8_t s3_idx[2][LB_BATTERY_LEVELS]; /* AP is sleeping */ + + /* Color palette */ + struct rgb_s color[8]; /* 0-3 are Google colors */ +} __packed; + +struct ec_params_lightbar { + uint8_t cmd; /* Command (see enum lightbar_command) */ + union { + struct { + /* no args */ + } dump, off, on, init, get_seq, get_params; + + struct num { + uint8_t num; + } brightness, seq, demo; + + struct reg { + uint8_t ctrl, reg, value; + } reg; + + struct rgb { + uint8_t led, red, green, blue; + } rgb; + + struct lightbar_params set_params; + }; +} __packed; + +struct ec_response_lightbar { + union { + struct dump { + struct { + uint8_t reg; + uint8_t ic0; + uint8_t ic1; + } vals[23]; + } dump; + + struct get_seq { + uint8_t num; + } get_seq; + + struct lightbar_params get_params; + + struct { + /* no return params */ + } off, on, init, brightness, seq, reg, rgb, demo, set_params; + }; +} __packed; + +/* Lightbar commands */ +enum lightbar_command { + LIGHTBAR_CMD_DUMP = 0, + LIGHTBAR_CMD_OFF = 1, + LIGHTBAR_CMD_ON = 2, + LIGHTBAR_CMD_INIT = 3, + LIGHTBAR_CMD_BRIGHTNESS = 4, + LIGHTBAR_CMD_SEQ = 5, + LIGHTBAR_CMD_REG = 6, + LIGHTBAR_CMD_RGB = 7, + LIGHTBAR_CMD_GET_SEQ = 8, + LIGHTBAR_CMD_DEMO = 9, + LIGHTBAR_CMD_GET_PARAMS = 10, + LIGHTBAR_CMD_SET_PARAMS = 11, + LIGHTBAR_NUM_CMDS +}; + +/*****************************************************************************/ +/* Verified boot commands */ + +/* + * Note: command code 0x29 version 0 was VBOOT_CMD in Link EVT; it may be + * reused for other purposes with version > 0. + */ + +/* Verified boot hash command */ +#define EC_CMD_VBOOT_HASH 0x2A + +struct ec_params_vboot_hash { + uint8_t cmd; /* enum ec_vboot_hash_cmd */ + uint8_t hash_type; /* enum ec_vboot_hash_type */ + uint8_t nonce_size; /* Nonce size; may be 0 */ + uint8_t reserved0; /* Reserved; set 0 */ + uint32_t offset; /* Offset in flash to hash */ + uint32_t size; /* Number of bytes to hash */ + uint8_t nonce_data[64]; /* Nonce data; ignored if nonce_size=0 */ +} __packed; + +struct ec_response_vboot_hash { + uint8_t status; /* enum ec_vboot_hash_status */ + uint8_t hash_type; /* enum ec_vboot_hash_type */ + uint8_t digest_size; /* Size of hash digest in bytes */ + uint8_t reserved0; /* Ignore; will be 0 */ + uint32_t offset; /* Offset in flash which was hashed */ + uint32_t size; /* Number of bytes hashed */ + uint8_t hash_digest[64]; /* Hash digest data */ +} __packed; + +enum ec_vboot_hash_cmd { + EC_VBOOT_HASH_GET = 0, /* Get current hash status */ + EC_VBOOT_HASH_ABORT = 1, /* Abort calculating current hash */ + EC_VBOOT_HASH_START = 2, /* Start computing a new hash */ + EC_VBOOT_HASH_RECALC = 3, /* Synchronously compute a new hash */ +}; + +enum ec_vboot_hash_type { + EC_VBOOT_HASH_TYPE_SHA256 = 0, /* SHA-256 */ +}; + +enum ec_vboot_hash_status { + EC_VBOOT_HASH_STATUS_NONE = 0, /* No hash (not started, or aborted) */ + EC_VBOOT_HASH_STATUS_DONE = 1, /* Finished computing a hash */ + EC_VBOOT_HASH_STATUS_BUSY = 2, /* Busy computing a hash */ +}; + +/* + * Special values for offset for EC_VBOOT_HASH_START and EC_VBOOT_HASH_RECALC. + * If one of these is specified, the EC will automatically update offset and + * size to the correct values for the specified image (RO or RW). + */ +#define EC_VBOOT_HASH_OFFSET_RO 0xfffffffe +#define EC_VBOOT_HASH_OFFSET_RW 0xfffffffd + +/*****************************************************************************/ +/* USB charging control commands */ + +/* Set USB port charging mode */ +#define EC_CMD_USB_CHARGE_SET_MODE 0x30 + +struct ec_params_usb_charge_set_mode { + uint8_t usb_port_id; + uint8_t mode; +} __packed; + +/*****************************************************************************/ +/* Persistent storage for host */ + +/* Maximum bytes that can be read/written in a single command */ +#define EC_PSTORE_SIZE_MAX 64 + +/* Get persistent storage info */ +#define EC_CMD_PSTORE_INFO 0x40 + +struct ec_response_pstore_info { + /* Persistent storage size, in bytes */ + uint32_t pstore_size; + /* Access size; read/write offset and size must be a multiple of this */ + uint32_t access_size; +} __packed; + +/* + * Read persistent storage + * + * Response is params.size bytes of data. + */ +#define EC_CMD_PSTORE_READ 0x41 + +struct ec_params_pstore_read { + uint32_t offset; /* Byte offset to read */ + uint32_t size; /* Size to read in bytes */ +} __packed; + +/* Write persistent storage */ +#define EC_CMD_PSTORE_WRITE 0x42 + +struct ec_params_pstore_write { + uint32_t offset; /* Byte offset to write */ + uint32_t size; /* Size to write in bytes */ + uint8_t data[EC_PSTORE_SIZE_MAX]; +} __packed; + +/*****************************************************************************/ +/* Real-time clock */ + +/* RTC params and response structures */ +struct ec_params_rtc { + uint32_t time; +} __packed; + +struct ec_response_rtc { + uint32_t time; +} __packed; + +/* These use ec_response_rtc */ +#define EC_CMD_RTC_GET_VALUE 0x44 +#define EC_CMD_RTC_GET_ALARM 0x45 + +/* These all use ec_params_rtc */ +#define EC_CMD_RTC_SET_VALUE 0x46 +#define EC_CMD_RTC_SET_ALARM 0x47 + +/*****************************************************************************/ +/* Port80 log access */ + +/* Get last port80 code from previous boot */ +#define EC_CMD_PORT80_LAST_BOOT 0x48 + +struct ec_response_port80_last_boot { + uint16_t code; +} __packed; + +/*****************************************************************************/ +/* Thermal engine commands */ + +/* Set thershold value */ +#define EC_CMD_THERMAL_SET_THRESHOLD 0x50 + +struct ec_params_thermal_set_threshold { + uint8_t sensor_type; + uint8_t threshold_id; + uint16_t value; +} __packed; + +/* Get threshold value */ +#define EC_CMD_THERMAL_GET_THRESHOLD 0x51 + +struct ec_params_thermal_get_threshold { + uint8_t sensor_type; + uint8_t threshold_id; +} __packed; + +struct ec_response_thermal_get_threshold { + uint16_t value; +} __packed; + +/* Toggle automatic fan control */ +#define EC_CMD_THERMAL_AUTO_FAN_CTRL 0x52 + +/* Get TMP006 calibration data */ +#define EC_CMD_TMP006_GET_CALIBRATION 0x53 + +struct ec_params_tmp006_get_calibration { + uint8_t index; +} __packed; + +struct ec_response_tmp006_get_calibration { + float s0; + float b0; + float b1; + float b2; +} __packed; + +/* Set TMP006 calibration data */ +#define EC_CMD_TMP006_SET_CALIBRATION 0x54 + +struct ec_params_tmp006_set_calibration { + uint8_t index; + uint8_t reserved[3]; /* Reserved; set 0 */ + float s0; + float b0; + float b1; + float b2; +} __packed; + +/*****************************************************************************/ +/* CROS_EC - Matrix KeyBoard Protocol */ + +/* + * Read key state + * + * Returns raw data for keyboard cols; see ec_response_cros_ec_info.cols for + * expected response size. + */ +#define EC_CMD_CROS_EC_STATE 0x60 + +/* Provide information about the matrix : number of rows and columns */ +#define EC_CMD_CROS_EC_INFO 0x61 + +struct ec_response_cros_ec_info { + uint32_t rows; + uint32_t cols; + uint8_t switches; +} __packed; + +/* Simulate key press */ +#define EC_CMD_CROS_EC_SIMULATE_KEY 0x62 + +struct ec_params_cros_ec_simulate_key { + uint8_t col; + uint8_t row; + uint8_t pressed; +} __packed; + +/* Configure keyboard scanning */ +#define EC_CMD_CROS_EC_SET_CONFIG 0x64 +#define EC_CMD_CROS_EC_GET_CONFIG 0x65 + +/* flags */ +enum cros_ec_config_flags { + EC_CROS_EC_FLAGS_ENABLE = 1, /* Enable keyboard scanning */ +}; + +enum cros_ec_config_valid { + EC_CROS_EC_VALID_SCAN_PERIOD = 1 << 0, + EC_CROS_EC_VALID_POLL_TIMEOUT = 1 << 1, + EC_CROS_EC_VALID_MIN_POST_SCAN_DELAY = 1 << 3, + EC_CROS_EC_VALID_OUTPUT_SETTLE = 1 << 4, + EC_CROS_EC_VALID_DEBOUNCE_DOWN = 1 << 5, + EC_CROS_EC_VALID_DEBOUNCE_UP = 1 << 6, + EC_CROS_EC_VALID_FIFO_MAX_DEPTH = 1 << 7, +}; + +/* Configuration for our key scanning algorithm */ +struct ec_cros_ec_config { + uint32_t valid_mask; /* valid fields */ + uint8_t flags; /* some flags (enum cros_ec_config_flags) */ + uint8_t valid_flags; /* which flags are valid */ + uint16_t scan_period_us; /* period between start of scans */ + /* revert to interrupt mode after no activity for this long */ + uint32_t poll_timeout_us; + /* + * minimum post-scan relax time. Once we finish a scan we check + * the time until we are due to start the next one. If this time is + * shorter this field, we use this instead. + */ + uint16_t min_post_scan_delay_us; + /* delay between setting up output and waiting for it to settle */ + uint16_t output_settle_us; + uint16_t debounce_down_us; /* time for debounce on key down */ + uint16_t debounce_up_us; /* time for debounce on key up */ + /* maximum depth to allow for fifo (0 = no keyscan output) */ + uint8_t fifo_max_depth; +} __packed; + +struct ec_params_cros_ec_set_config { + struct ec_cros_ec_config config; +} __packed; + +struct ec_response_cros_ec_get_config { + struct ec_cros_ec_config config; +} __packed; + +/* Run the key scan emulation */ +#define EC_CMD_KEYSCAN_SEQ_CTRL 0x66 + +enum ec_keyscan_seq_cmd { + EC_KEYSCAN_SEQ_STATUS = 0, /* Get status information */ + EC_KEYSCAN_SEQ_CLEAR = 1, /* Clear sequence */ + EC_KEYSCAN_SEQ_ADD = 2, /* Add item to sequence */ + EC_KEYSCAN_SEQ_START = 3, /* Start running sequence */ + EC_KEYSCAN_SEQ_COLLECT = 4, /* Collect sequence summary data */ +}; + +enum ec_collect_flags { + /* + * Indicates this scan was processed by the EC. Due to timing, some + * scans may be skipped. + */ + EC_KEYSCAN_SEQ_FLAG_DONE = 1 << 0, +}; + +struct ec_collect_item { + uint8_t flags; /* some flags (enum ec_collect_flags) */ +}; + +struct ec_params_keyscan_seq_ctrl { + uint8_t cmd; /* Command to send (enum ec_keyscan_seq_cmd) */ + union { + struct { + uint8_t active; /* still active */ + uint8_t num_items; /* number of items */ + /* Current item being presented */ + uint8_t cur_item; + } status; + struct { + /* + * Absolute time for this scan, measured from the + * start of the sequence. + */ + uint32_t time_us; + uint8_t scan[0]; /* keyscan data */ + } add; + struct { + uint8_t start_item; /* First item to return */ + uint8_t num_items; /* Number of items to return */ + } collect; + }; +} __packed; + +struct ec_result_keyscan_seq_ctrl { + union { + struct { + uint8_t num_items; /* Number of items */ + /* Data for each item */ + struct ec_collect_item item[0]; + } collect; + }; +} __packed; + +/*****************************************************************************/ +/* Temperature sensor commands */ + +/* Read temperature sensor info */ +#define EC_CMD_TEMP_SENSOR_GET_INFO 0x70 + +struct ec_params_temp_sensor_get_info { + uint8_t id; +} __packed; + +struct ec_response_temp_sensor_get_info { + char sensor_name[32]; + uint8_t sensor_type; +} __packed; + +/*****************************************************************************/ + +/* + * Note: host commands 0x80 - 0x87 are reserved to avoid conflict with ACPI + * commands accidentally sent to the wrong interface. See the ACPI section + * below. + */ + +/*****************************************************************************/ +/* Host event commands */ + +/* + * Host event mask params and response structures, shared by all of the host + * event commands below. + */ +struct ec_params_host_event_mask { + uint32_t mask; +} __packed; + +struct ec_response_host_event_mask { + uint32_t mask; +} __packed; + +/* These all use ec_response_host_event_mask */ +#define EC_CMD_HOST_EVENT_GET_B 0x87 +#define EC_CMD_HOST_EVENT_GET_SMI_MASK 0x88 +#define EC_CMD_HOST_EVENT_GET_SCI_MASK 0x89 +#define EC_CMD_HOST_EVENT_GET_WAKE_MASK 0x8d + +/* These all use ec_params_host_event_mask */ +#define EC_CMD_HOST_EVENT_SET_SMI_MASK 0x8a +#define EC_CMD_HOST_EVENT_SET_SCI_MASK 0x8b +#define EC_CMD_HOST_EVENT_CLEAR 0x8c +#define EC_CMD_HOST_EVENT_SET_WAKE_MASK 0x8e +#define EC_CMD_HOST_EVENT_CLEAR_B 0x8f + +/*****************************************************************************/ +/* Switch commands */ + +/* Enable/disable LCD backlight */ +#define EC_CMD_SWITCH_ENABLE_BKLIGHT 0x90 + +struct ec_params_switch_enable_backlight { + uint8_t enabled; +} __packed; + +/* Enable/disable WLAN/Bluetooth */ +#define EC_CMD_SWITCH_ENABLE_WIRELESS 0x91 + +struct ec_params_switch_enable_wireless { + uint8_t enabled; +} __packed; + +/*****************************************************************************/ +/* GPIO commands. Only available on EC if write protect has been disabled. */ + +/* Set GPIO output value */ +#define EC_CMD_GPIO_SET 0x92 + +struct ec_params_gpio_set { + char name[32]; + uint8_t val; +} __packed; + +/* Get GPIO value */ +#define EC_CMD_GPIO_GET 0x93 + +struct ec_params_gpio_get { + char name[32]; +} __packed; +struct ec_response_gpio_get { + uint8_t val; +} __packed; + +/*****************************************************************************/ +/* I2C commands. Only available when flash write protect is unlocked. */ + +/* Read I2C bus */ +#define EC_CMD_I2C_READ 0x94 + +struct ec_params_i2c_read { + uint16_t addr; + uint8_t read_size; /* Either 8 or 16. */ + uint8_t port; + uint8_t offset; +} __packed; +struct ec_response_i2c_read { + uint16_t data; +} __packed; + +/* Write I2C bus */ +#define EC_CMD_I2C_WRITE 0x95 + +struct ec_params_i2c_write { + uint16_t data; + uint16_t addr; + uint8_t write_size; /* Either 8 or 16. */ + uint8_t port; + uint8_t offset; +} __packed; + +/*****************************************************************************/ +/* Charge state commands. Only available when flash write protect unlocked. */ + +/* Force charge state machine to stop in idle mode */ +#define EC_CMD_CHARGE_FORCE_IDLE 0x96 + +struct ec_params_force_idle { + uint8_t enabled; +} __packed; + +/*****************************************************************************/ +/* Console commands. Only available when flash write protect is unlocked. */ + +/* Snapshot console output buffer for use by EC_CMD_CONSOLE_READ. */ +#define EC_CMD_CONSOLE_SNAPSHOT 0x97 + +/* + * Read next chunk of data from saved snapshot. + * + * Response is null-terminated string. Empty string, if there is no more + * remaining output. + */ +#define EC_CMD_CONSOLE_READ 0x98 + +/*****************************************************************************/ + +/* + * Cut off battery power output if the battery supports. + * + * For unsupported battery, just don't implement this command and lets EC + * return EC_RES_INVALID_COMMAND. + */ +#define EC_CMD_BATTERY_CUT_OFF 0x99 + +/*****************************************************************************/ +/* USB port mux control. */ + +/* + * Switch USB mux or return to automatic switching. + */ +#define EC_CMD_USB_MUX 0x9a + +struct ec_params_usb_mux { + uint8_t mux; +} __packed; + +/*****************************************************************************/ +/* LDOs / FETs control. */ + +enum ec_ldo_state { + EC_LDO_STATE_OFF = 0, /* the LDO / FET is shut down */ + EC_LDO_STATE_ON = 1, /* the LDO / FET is ON / providing power */ +}; + +/* + * Switch on/off a LDO. + */ +#define EC_CMD_LDO_SET 0x9b + +struct ec_params_ldo_set { + uint8_t index; + uint8_t state; +} __packed; + +/* + * Get LDO state. + */ +#define EC_CMD_LDO_GET 0x9c + +struct ec_params_ldo_get { + uint8_t index; +} __packed; + +struct ec_response_ldo_get { + uint8_t state; +} __packed; + +/*****************************************************************************/ +/* Temporary debug commands. TODO: remove this crosbug.com/p/13849 */ + +/* + * Dump charge state machine context. + * + * Response is a binary dump of charge state machine context. + */ +#define EC_CMD_CHARGE_DUMP 0xa0 + +/* + * Set maximum battery charging current. + */ +#define EC_CMD_CHARGE_CURRENT_LIMIT 0xa1 + +struct ec_params_current_limit { + uint32_t limit; +} __packed; + +/*****************************************************************************/ +/* Smart battery pass-through */ + +/* Get / Set 16-bit smart battery registers */ +#define EC_CMD_SB_READ_WORD 0xb0 +#define EC_CMD_SB_WRITE_WORD 0xb1 + +/* Get / Set string smart battery parameters + * formatted as SMBUS "block". + */ +#define EC_CMD_SB_READ_BLOCK 0xb2 +#define EC_CMD_SB_WRITE_BLOCK 0xb3 + +struct ec_params_sb_rd { + uint8_t reg; +} __packed; + +struct ec_response_sb_rd_word { + uint16_t value; +} __packed; + +struct ec_params_sb_wr_word { + uint8_t reg; + uint16_t value; +} __packed; + +struct ec_response_sb_rd_block { + uint8_t data[32]; +} __packed; + +struct ec_params_sb_wr_block { + uint8_t reg; + uint16_t data[32]; +} __packed; + +/*****************************************************************************/ +/* System commands */ + +/* + * TODO: this is a confusing name, since it doesn't necessarily reboot the EC. + * Rename to "set image" or something similar. + */ +#define EC_CMD_REBOOT_EC 0xd2 + +/* Command */ +enum ec_reboot_cmd { + EC_REBOOT_CANCEL = 0, /* Cancel a pending reboot */ + EC_REBOOT_JUMP_RO = 1, /* Jump to RO without rebooting */ + EC_REBOOT_JUMP_RW = 2, /* Jump to RW without rebooting */ + /* (command 3 was jump to RW-B) */ + EC_REBOOT_COLD = 4, /* Cold-reboot */ + EC_REBOOT_DISABLE_JUMP = 5, /* Disable jump until next reboot */ + EC_REBOOT_HIBERNATE = 6 /* Hibernate EC */ +}; + +/* Flags for ec_params_reboot_ec.reboot_flags */ +#define EC_REBOOT_FLAG_RESERVED0 (1 << 0) /* Was recovery request */ +#define EC_REBOOT_FLAG_ON_AP_SHUTDOWN (1 << 1) /* Reboot after AP shutdown */ + +struct ec_params_reboot_ec { + uint8_t cmd; /* enum ec_reboot_cmd */ + uint8_t flags; /* See EC_REBOOT_FLAG_* */ +} __packed; + +/* + * Get information on last EC panic. + * + * Returns variable-length platform-dependent panic information. See panic.h + * for details. + */ +#define EC_CMD_GET_PANIC_INFO 0xd3 + +/*****************************************************************************/ +/* + * ACPI commands + * + * These are valid ONLY on the ACPI command/data port. + */ + +/* + * ACPI Read Embedded Controller + * + * This reads from ACPI memory space on the EC (EC_ACPI_MEM_*). + * + * Use the following sequence: + * + * - Write EC_CMD_ACPI_READ to EC_LPC_ADDR_ACPI_CMD + * - Wait for EC_LPC_CMDR_PENDING bit to clear + * - Write address to EC_LPC_ADDR_ACPI_DATA + * - Wait for EC_LPC_CMDR_DATA bit to set + * - Read value from EC_LPC_ADDR_ACPI_DATA + */ +#define EC_CMD_ACPI_READ 0x80 + +/* + * ACPI Write Embedded Controller + * + * This reads from ACPI memory space on the EC (EC_ACPI_MEM_*). + * + * Use the following sequence: + * + * - Write EC_CMD_ACPI_WRITE to EC_LPC_ADDR_ACPI_CMD + * - Wait for EC_LPC_CMDR_PENDING bit to clear + * - Write address to EC_LPC_ADDR_ACPI_DATA + * - Wait for EC_LPC_CMDR_PENDING bit to clear + * - Write value to EC_LPC_ADDR_ACPI_DATA + */ +#define EC_CMD_ACPI_WRITE 0x81 + +/* + * ACPI Query Embedded Controller + * + * This clears the lowest-order bit in the currently pending host events, and + * sets the result code to the 1-based index of the bit (event 0x00000001 = 1, + * event 0x80000000 = 32), or 0 if no event was pending. + */ +#define EC_CMD_ACPI_QUERY_EVENT 0x84 + +/* Valid addresses in ACPI memory space, for read/write commands */ +/* Memory space version; set to EC_ACPI_MEM_VERSION_CURRENT */ +#define EC_ACPI_MEM_VERSION 0x00 +/* + * Test location; writing value here updates test compliment byte to (0xff - + * value). + */ +#define EC_ACPI_MEM_TEST 0x01 +/* Test compliment; writes here are ignored. */ +#define EC_ACPI_MEM_TEST_COMPLIMENT 0x02 +/* Keyboard backlight brightness percent (0 - 100) */ +#define EC_ACPI_MEM_KEYBOARD_BACKLIGHT 0x03 + +/* Current version of ACPI memory address space */ +#define EC_ACPI_MEM_VERSION_CURRENT 1 + + +/*****************************************************************************/ +/* + * Special commands + * + * These do not follow the normal rules for commands. See each command for + * details. + */ + +/* + * Reboot NOW + * + * This command will work even when the EC LPC interface is busy, because the + * reboot command is processed at interrupt level. Note that when the EC + * reboots, the host will reboot too, so there is no response to this command. + * + * Use EC_CMD_REBOOT_EC to reboot the EC more politely. + */ +#define EC_CMD_REBOOT 0xd1 /* Think "die" */ + +/* + * Resend last response (not supported on LPC). + * + * Returns EC_RES_UNAVAILABLE if there is no response available - for example, + * there was no previous command, or the previous command's response was too + * big to save. + */ +#define EC_CMD_RESEND_RESPONSE 0xdb + +/* + * This header byte on a command indicate version 0. Any header byte less + * than this means that we are talking to an old EC which doesn't support + * versioning. In that case, we assume version 0. + * + * Header bytes greater than this indicate a later version. For example, + * EC_CMD_VERSION0 + 1 means we are using version 1. + * + * The old EC interface must not use commands 0dc or higher. + */ +#define EC_CMD_VERSION0 0xdc + +#endif /* !__ACPI__ */ + +#endif /* __CROS_EC_COMMANDS_H */ diff --git a/include/fdtdec.h b/include/fdtdec.h index 8845e294b9..d93e102ac6 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -84,6 +84,8 @@ enum fdt_compat_id { COMPAT_SAMSUNG_EXYNOS5_SOUND, /* Exynos Sound */ COMPAT_WOLFSON_WM8994_CODEC, /* Wolfson WM8994 Sound Codec */ COMPAT_SAMSUNG_EXYNOS_SPI, /* Exynos SPI */ + COMPAT_GOOGLE_CROS_EC, /* Google CROS_EC Protocol */ + COMPAT_GOOGLE_CROS_EC_KEYB, /* Google CROS_EC Keyboard */ COMPAT_SAMSUNG_EXYNOS_EHCI, /* Exynos EHCI controller */ COMPAT_SAMSUNG_EXYNOS_USB_PHY, /* Exynos phy controller for usb2.0 */ COMPAT_SAMSUNG_EXYNOS_TMU, /* Exynos TMU */ diff --git a/include/ide.h b/include/ide.h index afea85cdc2..f691a74ab4 100644 --- a/include/ide.h +++ b/include/ide.h @@ -54,8 +54,9 @@ typedef ulong lbaint_t; */ void ide_init(void); -ulong ide_read(int device, ulong blknr, lbaint_t blkcnt, void *buffer); -ulong ide_write(int device, ulong blknr, lbaint_t blkcnt, const void *buffer); +ulong ide_read(int device, lbaint_t blknr, lbaint_t blkcnt, void *buffer); +ulong ide_write(int device, lbaint_t blknr, lbaint_t blkcnt, + const void *buffer); #ifdef CONFIG_IDE_PREINIT int ide_preinit(void); diff --git a/include/image.h b/include/image.h index 8ccc00b76a..261491880e 100644 --- a/include/image.h +++ b/include/image.h @@ -46,6 +46,9 @@ struct lmb; #define CONFIG_OF_LIBFDT 1 #define CONFIG_FIT_VERBOSE 1 /* enable fit_format_{error,warning}() */ +/* Support FIT image signing on host */ +#define CONFIG_FIT_SIGNATURE + #define IMAGE_ENABLE_IGNORE 0 #define IMAGE_INDENT_STRING "" @@ -320,13 +323,16 @@ typedef struct bootm_headers { int verify; /* getenv("verify")[0] != 'n' */ #define BOOTM_STATE_START (0x00000001) -#define BOOTM_STATE_LOADOS (0x00000002) -#define BOOTM_STATE_RAMDISK (0x00000004) -#define BOOTM_STATE_FDT (0x00000008) -#define BOOTM_STATE_OS_CMDLINE (0x00000010) -#define BOOTM_STATE_OS_BD_T (0x00000020) -#define BOOTM_STATE_OS_PREP (0x00000040) -#define BOOTM_STATE_OS_GO (0x00000080) +#define BOOTM_STATE_FINDOS (0x00000002) +#define BOOTM_STATE_FINDOTHER (0x00000004) +#define BOOTM_STATE_LOADOS (0x00000008) +#define BOOTM_STATE_RAMDISK (0x00000010) +#define BOOTM_STATE_FDT (0x00000020) +#define BOOTM_STATE_OS_CMDLINE (0x00000040) +#define BOOTM_STATE_OS_BD_T (0x00000080) +#define BOOTM_STATE_OS_PREP (0x00000100) +#define BOOTM_STATE_OS_FAKE_GO (0x00000200) /* 'Almost' run the OS */ +#define BOOTM_STATE_OS_GO (0x00000400) int state; #ifdef CONFIG_LMB @@ -667,11 +673,12 @@ int image_setup_linux(bootm_headers_t *images); #define FIT_IMAGES_PATH "/images" #define FIT_CONFS_PATH "/configurations" -/* hash node */ +/* hash/signature node */ #define FIT_HASH_NODENAME "hash" #define FIT_ALGO_PROP "algo" #define FIT_VALUE_PROP "value" #define FIT_IGNORE_PROP "uboot-ignore" +#define FIT_SIG_NODENAME "signature" /* image node */ #define FIT_DATA_PROP "data" @@ -759,12 +766,26 @@ int fit_image_hash_get_value(const void *fit, int noffset, uint8_t **value, int fit_set_timestamp(void *fit, int noffset, time_t timestamp); /** - * fit_add_verification_data() - Calculate and add hashes to FIT + * fit_add_verification_data() - add verification data to FIT image nodes + * + * @keydir: Directory containing keys + * @kwydest: FDT blob to write public key information to + * @fit: Pointer to the FIT format image header + * @comment: Comment to add to signature nodes + * @require_keys: Mark all keys as 'required' + * + * Adds hash values for all component images in the FIT blob. + * Hashes are calculated for all component images which have hash subnodes + * with algorithm property set to one of the supported hash algorithms. + * + * Also add signatures if signature nodes are present. * - * @fit: Fit image to process - * @return 0 if ok, <0 for error + * returns + * 0, on success + * libfdt error code, on failure */ -int fit_add_verification_data(void *fit); +int fit_add_verification_data(const char *keydir, void *keydest, void *fit, + const char *comment, int require_keys); int fit_image_verify(const void *fit, int noffset); int fit_config_verify(const void *fit, int conf_noffset); @@ -801,15 +822,19 @@ int calculate_hash(const void *data, int data_len, const char *algo, uint8_t *value, int *value_len); /* - * At present we only support verification on the device + * At present we only support signing on the host, and verification on the + * device */ #if defined(CONFIG_FIT_SIGNATURE) # ifdef USE_HOSTCC +# define IMAGE_ENABLE_SIGN 1 # define IMAGE_ENABLE_VERIFY 0 #else +# define IMAGE_ENABLE_SIGN 0 # define IMAGE_ENABLE_VERIFY 1 # endif #else +# define IMAGE_ENABLE_SIGN 0 # define IMAGE_ENABLE_VERIFY 0 #endif @@ -825,6 +850,137 @@ int calculate_hash(const void *data, int data_len, const char *algo, #define IMAGE_ENABLE_BEST_MATCH 0 #endif +/* Information passed to the signing routines */ +struct image_sign_info { + const char *keydir; /* Directory conaining keys */ + const char *keyname; /* Name of key to use */ + void *fit; /* Pointer to FIT blob */ + int node_offset; /* Offset of signature node */ + struct image_sig_algo *algo; /* Algorithm information */ + const void *fdt_blob; /* FDT containing public keys */ + int required_keynode; /* Node offset of key to use: -1=any */ + const char *require_keys; /* Value for 'required' property */ +}; + +/* A part of an image, used for hashing */ +struct image_region { + const void *data; + int size; +}; + +struct image_sig_algo { + const char *name; /* Name of algorithm */ + + /** + * sign() - calculate and return signature for given input data + * + * @info: Specifies key and FIT information + * @data: Pointer to the input data + * @data_len: Data length + * @sigp: Set to an allocated buffer holding the signature + * @sig_len: Set to length of the calculated hash + * + * This computes input data signature according to selected algorithm. + * Resulting signature value is placed in an allocated buffer, the + * pointer is returned as *sigp. The length of the calculated + * signature is returned via the sig_len pointer argument. The caller + * should free *sigp. + * + * @return: 0, on success, -ve on error + */ + int (*sign)(struct image_sign_info *info, + const struct image_region region[], + int region_count, uint8_t **sigp, uint *sig_len); + + /** + * add_verify_data() - Add verification information to FDT + * + * Add public key information to the FDT node, suitable for + * verification at run-time. The information added depends on the + * algorithm being used. + * + * @info: Specifies key and FIT information + * @keydest: Destination FDT blob for public key data + * @return: 0, on success, -ve on error + */ + int (*add_verify_data)(struct image_sign_info *info, void *keydest); + + /** + * verify() - Verify a signature against some data + * + * @info: Specifies key and FIT information + * @data: Pointer to the input data + * @data_len: Data length + * @sig: Signature + * @sig_len: Number of bytes in signature + * @return 0 if verified, -ve on error + */ + int (*verify)(struct image_sign_info *info, + const struct image_region region[], int region_count, + uint8_t *sig, uint sig_len); +}; + +/** + * image_get_sig_algo() - Look up a signature algortihm + * + * @param name Name of algorithm + * @return pointer to algorithm information, or NULL if not found + */ +struct image_sig_algo *image_get_sig_algo(const char *name); + +/** + * fit_image_verify_required_sigs() - Verify signatures marked as 'required' + * + * @fit: FIT to check + * @image_noffset: Offset of image node to check + * @data: Image data to check + * @size: Size of image data + * @sig_blob: FDT containing public keys + * @no_sigsp: Returns 1 if no signatures were required, and + * therefore nothing was checked. The caller may wish + * to fall back to other mechanisms, or refuse to + * boot. + * @return 0 if all verified ok, <0 on error + */ +int fit_image_verify_required_sigs(const void *fit, int image_noffset, + const char *data, size_t size, const void *sig_blob, + int *no_sigsp); + +/** + * fit_image_check_sig() - Check a single image signature node + * + * @fit: FIT to check + * @noffset: Offset of signature node to check + * @data: Image data to check + * @size: Size of image data + * @required_keynode: Offset in the control FDT of the required key node, + * if any. If this is given, then the image wil not + * pass verification unless that key is used. If this is + * -1 then any signature will do. + * @err_msgp: In the event of an error, this will be pointed to a + * help error string to display to the user. + * @return 0 if all verified ok, <0 on error + */ +int fit_image_check_sig(const void *fit, int noffset, const void *data, + size_t size, int required_keynode, char **err_msgp); + +/** + * fit_region_make_list() - Make a list of regions to hash + * + * Given a list of FIT regions (offset, size) provided by libfdt, create + * a list of regions (void *, size) for use by the signature creationg + * and verification code. + * + * @fit: FIT image to process + * @fdt_regions: Regions as returned by libfdt + * @count: Number of regions returned by libfdt + * @region: Place to put list of regions (NULL to allocate it) + * @return pointer to list of regions, or NULL if out of memory + */ +struct image_region *fit_region_make_list(const void *fit, + struct fdt_region *fdt_regions, int count, + struct image_region *region); + static inline int fit_image_check_target_arch(const void *fdt, int node) { return fit_image_check_arch(fdt, node, IH_ARCH_DEFAULT); diff --git a/include/libfdt.h b/include/libfdt.h index c5ec2acfd8..765d84f5e4 100644 --- a/include/libfdt.h +++ b/include/libfdt.h @@ -1511,4 +1511,68 @@ int fdt_del_node(void *fdt, int nodeoffset); const char *fdt_strerror(int errval); +struct fdt_region { + int offset; + int size; +}; + +/** + * fdt_find_regions() - find regions in device tree + * + * Given a list of nodes to include and properties to exclude, find + * the regions of the device tree which describe those included parts. + * + * The intent is to get a list of regions which will be invariant provided + * those parts are invariant. For example, if you request a list of regions + * for all nodes but exclude the property "data", then you will get the + * same region contents regardless of any change to "data" properties. + * + * This function can be used to produce a byte-stream to send to a hashing + * function to verify that critical parts of the FDT have not changed. + * + * Nodes which are given in 'inc' are included in the region list, as + * are the names of the immediate subnodes nodes (but not the properties + * or subnodes of those subnodes). + * + * For eaxample "/" means to include the root node, all root properties + * and the FDT_BEGIN_NODE and FDT_END_NODE of all subnodes of /. The latter + * ensures that we capture the names of the subnodes. In a hashing situation + * it prevents the root node from changing at all Any change to non-excluded + * properties, names of subnodes or number of subnodes would be detected. + * + * When used with FITs this provides the ability to hash and sign parts of + * the FIT based on different configurations in the FIT. Then it is + * impossible to change anything about that configuration (include images + * attached to the configuration), but it may be possible to add new + * configurations, new images or new signatures within the existing + * framework. + * + * Adding new properties to a device tree may result in the string table + * being extended (if the new property names are different from those + * already added). This function can optionally include a region for + * the string table so that this can be part of the hash too. + * + * The device tree header is not included in the list. + * + * @fdt: Device tree to check + * @inc: List of node paths to included + * @inc_count: Number of node paths in list + * @exc_prop: List of properties names to exclude + * @exc_prop_count: Number of properties in exclude list + * @region: Returns list of regions + * @max_region: Maximum length of region list + * @path: Pointer to a temporary string for the function to use for + * building path names + * @path_len: Length of path, must be large enough to hold the longest + * path in the tree + * @add_string_tab: 1 to add a region for the string table + * @return number of regions in list. If this is >max_regions then the + * region array was exhausted. You should increase max_regions and try + * the call again. + */ +int fdt_find_regions(const void *fdt, char * const inc[], int inc_count, + char * const exc_prop[], int exc_prop_count, + struct fdt_region region[], int max_regions, + char *path, int path_len, int add_string_tab); + #endif /* _LIBFDT_H */ diff --git a/include/part.h b/include/part.h index f7c7cc59fc..35c1c5b5f5 100644 --- a/include/part.h +++ b/include/part.h @@ -43,15 +43,15 @@ typedef struct block_dev_desc { char product[20+1]; /* IDE Serial no, SCSI product */ char revision[8+1]; /* firmware revision */ unsigned long (*block_read)(int dev, - unsigned long start, + lbaint_t start, lbaint_t blkcnt, void *buffer); unsigned long (*block_write)(int dev, - unsigned long start, + lbaint_t start, lbaint_t blkcnt, const void *buffer); unsigned long (*block_erase)(int dev, - unsigned long start, + lbaint_t start, lbaint_t blkcnt); void *priv; /* driver private struct pointer */ }block_dev_desc_t; diff --git a/include/pci.h b/include/pci.h index f9c5148255..98ba151f90 100644 --- a/include/pci.h +++ b/include/pci.h @@ -462,7 +462,7 @@ struct pci_region { #define PCI_REGION_SYS_MEMORY 0x00000100 /* System memory */ #define PCI_REGION_RO 0x00000200 /* Read-only memory */ -extern __inline__ void pci_set_region(struct pci_region *reg, +static inline void pci_set_region(struct pci_region *reg, pci_addr_t bus_start, phys_addr_t phys_start, pci_size_t size, @@ -548,7 +548,7 @@ struct pci_controller { void *priv_data; }; -extern __inline__ void pci_set_ops(struct pci_controller *hose, +static inline void pci_set_ops(struct pci_controller *hose, int (*read_byte)(struct pci_controller*, pci_dev_t, int where, u8 *), int (*read_word)(struct pci_controller*, diff --git a/include/rsa.h b/include/rsa.h new file mode 100644 index 0000000000..a5dd676b6b --- /dev/null +++ b/include/rsa.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2013, Google Inc. + * + * (C) Copyright 2008 Semihalf + * + * (C) Copyright 2000-2006 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _RSA_H +#define _RSA_H + +#include <errno.h> +#include <image.h> + +#if IMAGE_ENABLE_SIGN +/** + * sign() - calculate and return signature for given input data + * + * @info: Specifies key and FIT information + * @data: Pointer to the input data + * @data_len: Data length + * @sigp: Set to an allocated buffer holding the signature + * @sig_len: Set to length of the calculated hash + * + * This computes input data signature according to selected algorithm. + * Resulting signature value is placed in an allocated buffer, the + * pointer is returned as *sigp. The length of the calculated + * signature is returned via the sig_len pointer argument. The caller + * should free *sigp. + * + * @return: 0, on success, -ve on error + */ +int rsa_sign(struct image_sign_info *info, + const struct image_region region[], + int region_count, uint8_t **sigp, uint *sig_len); + +/** + * add_verify_data() - Add verification information to FDT + * + * Add public key information to the FDT node, suitable for + * verification at run-time. The information added depends on the + * algorithm being used. + * + * @info: Specifies key and FIT information + * @keydest: Destination FDT blob for public key data + * @return: 0, on success, -ve on error +*/ +int rsa_add_verify_data(struct image_sign_info *info, void *keydest); +#else +static inline int rsa_sign(struct image_sign_info *info, + const struct image_region region[], int region_count, + uint8_t **sigp, uint *sig_len) +{ + return -ENXIO; +} + +static inline int rsa_add_verify_data(struct image_sign_info *info, + void *keydest) +{ + return -ENXIO; +} +#endif + +#if IMAGE_ENABLE_VERIFY +/** + * rsa_verify() - Verify a signature against some data + * + * Verify a RSA PKCS1.5 signature against an expected hash. + * + * @info: Specifies key and FIT information + * @data: Pointer to the input data + * @data_len: Data length + * @sig: Signature + * @sig_len: Number of bytes in signature + * @return 0 if verified, -ve on error + */ +int rsa_verify(struct image_sign_info *info, + const struct image_region region[], int region_count, + uint8_t *sig, uint sig_len); +#else +static inline int rsa_verify(struct image_sign_info *info, + const struct image_region region[], int region_count, + uint8_t *sig, uint sig_len) +{ + return -ENXIO; +} +#endif + +#endif diff --git a/include/spi.h b/include/spi.h index 1638b50394..e8e654467f 100644 --- a/include/spi.h +++ b/include/spi.h @@ -247,4 +247,20 @@ static inline int spi_w8r8(struct spi_slave *slave, unsigned char byte) return ret < 0 ? ret : din[1]; } +/** + * Set up a SPI slave for a particular device tree node + * + * This calls spi_setup_slave() with the correct bus number. Call + * spi_free_slave() to free it later. + * + * @param blob Device tree blob + * @param node SPI peripheral node to use + * @param cs Chip select to use + * @param max_hz Maximum SCK rate in Hz (0 for default) + * @param mode Clock polarity, clock phase and other parameters + * @return pointer to new spi_slave structure + */ +struct spi_slave *spi_setup_slave_fdt(const void *blob, int node, + unsigned int cs, unsigned int max_hz, unsigned int mode); + #endif /* _SPI_H_ */ diff --git a/include/trace.h b/include/trace.h new file mode 100644 index 0000000000..8082466f54 --- /dev/null +++ b/include/trace.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2012 The Chromium OS Authors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef __TRACE_H +#define __TRACE_H + +enum { + /* + * This affects the granularity of our trace. We can bin function + * entry points into groups on the basis that functions typically + * have a minimum size, so entry points can't appear any closer + * than this to each other. + * + * The value here assumes a minimum instruction size of 4 bytes, + * or that instructions are 2 bytes but there are at least 2 of + * them in every function. + * + * Increasing this value reduces the number of functions we can + * resolve, but reduces the size of the uintptr_t array used for + * our function list, which is the length of the code divided by + * this value. + */ + FUNC_SITE_SIZE = 4, /* distance between function sites */ +}; + +enum trace_chunk_type { + TRACE_CHUNK_FUNCS, + TRACE_CHUNK_CALLS, +}; + +/* A trace record for a function, as written to the profile output file */ +struct trace_output_func { + uint32_t offset; /* Function offset into code */ + uint32_t call_count; /* Number of times called */ +}; + +/* A header at the start of the trace output buffer */ +struct trace_output_hdr { + enum trace_chunk_type type; /* Record type */ + uint32_t rec_count; /* Number of records */ +}; + +/* Print statistics about traced function calls */ +void trace_print_stats(void); + +/** + * Dump a list of functions and call counts into a buffer + * + * Each record in the buffer is a struct trace_func_stats. The 'needed' + * parameter returns the number of bytes needed to complete the operation, + * which may be more than buff_size if your buffer is too small. + * + * @param buff Buffer in which to place data, or NULL to count size + * @param buff_size Size of buffer + * @param needed Returns number of bytes used / needed + * @return 0 if ok, -1 on error (buffer exhausted) + */ +int trace_list_functions(void *buff, int buff_size, unsigned *needed); + +/* Flags for ftrace_record */ +enum ftrace_flags { + FUNCF_EXIT = 0UL << 30, + FUNCF_ENTRY = 1UL << 30, + FUNCF_TEXTBASE = 2UL << 30, + + FUNCF_TIMESTAMP_MASK = 0x3fffffff, +}; + +#define TRACE_CALL_TYPE(call) ((call)->flags & 0xc0000000UL) + +/* Information about a single function entry/exit */ +struct trace_call { + uint32_t func; /* Function offset */ + uint32_t caller; /* Caller function offset */ + uint32_t flags; /* Flags and timestamp */ +}; + +int trace_list_calls(void *buff, int buff_size, unsigned int *needed); + +/** + * Turn function tracing on and off + * + * Don't enable trace if it has not been initialised. + * + * @param enabled 1 to enable trace, 0 to disable + */ +void trace_set_enabled(int enabled); + +#ifdef CONFIG_TRACE_EARLY +int trace_early_init(void); +#else +static inline int trace_early_init(void) +{ + return 0; +} +#endif + +/** + * Init the trace system + * + * This should be called after relocation with a suitably large buffer + * (typically as large as the U-Boot text area) + * + * @param buff Pointer to trace buffer + * @param buff_size Size of trace buffer + */ +int trace_init(void *buff, size_t buff_size); + +#endif diff --git a/include/vsprintf.h b/include/vsprintf.h index 651077ca4d..6568854fbe 100644 --- a/include/vsprintf.h +++ b/include/vsprintf.h @@ -178,4 +178,15 @@ int vscnprintf(char *buf, size_t size, const char *fmt, va_list args); #define vscnprintf(buf, size, fmt, args...) vsprintf(buf, fmt, ##args) #endif /* CONFIG_SYS_VSNPRINTF */ +/** + * print_grouped_ull() - print a value with digits grouped by ',' + * + * This prints a value with grouped digits, like 12,345,678 to make it easier + * to read. + * + * @val: Value to print + * @digits: Number of digiits to print + */ +void print_grouped_ull(unsigned long long int_val, int digits); + #endif diff --git a/lib/Makefile b/lib/Makefile index a94830f51e..f5a8819f39 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -71,6 +71,7 @@ COBJS-y += linux_string.o COBJS-$(CONFIG_REGEX) += slre.o COBJS-y += string.o COBJS-y += time.o +COBJS-$(CONFIG_TRACE) += trace.o COBJS-$(CONFIG_BOOTP_PXE) += uuid.o COBJS-y += vsprintf.o COBJS-$(CONFIG_RANDOM_MACADDR) += rand.o diff --git a/lib/fdtdec.c b/lib/fdtdec.c index ad25a0c9c9..b3142685a2 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -57,6 +57,8 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(SAMSUNG_EXYNOS5_SOUND, "samsung,exynos-sound"), COMPAT(WOLFSON_WM8994_CODEC, "wolfson,wm8994-codec"), COMPAT(SAMSUNG_EXYNOS_SPI, "samsung,exynos-spi"), + COMPAT(GOOGLE_CROS_EC, "google,cros-ec"), + COMPAT(GOOGLE_CROS_EC_KEYB, "google,cros-ec-keyb"), COMPAT(SAMSUNG_EXYNOS_EHCI, "samsung,exynos-ehci"), COMPAT(SAMSUNG_EXYNOS_USB_PHY, "samsung,exynos-usb-phy"), COMPAT(SAMSUNG_EXYNOS_TMU, "samsung,exynos-tmu"), diff --git a/lib/libfdt/fdt_wip.c b/lib/libfdt/fdt_wip.c index 63e67b78c8..b9e3c4a742 100644 --- a/lib/libfdt/fdt_wip.c +++ b/lib/libfdt/fdt_wip.c @@ -120,3 +120,132 @@ int fdt_nop_node(void *fdt, int nodeoffset) endoffset - nodeoffset); return 0; } + +#define FDT_MAX_DEPTH 32 + +static int str_in_list(const char *str, char * const list[], int count) +{ + int i; + + for (i = 0; i < count; i++) + if (!strcmp(list[i], str)) + return 1; + + return 0; +} + +int fdt_find_regions(const void *fdt, char * const inc[], int inc_count, + char * const exc_prop[], int exc_prop_count, + struct fdt_region region[], int max_regions, + char *path, int path_len, int add_string_tab) +{ + int stack[FDT_MAX_DEPTH]; + char *end; + int nextoffset = 0; + uint32_t tag; + int count = 0; + int start = -1; + int depth = -1; + int want = 0; + int base = fdt_off_dt_struct(fdt); + + end = path; + *end = '\0'; + do { + const struct fdt_property *prop; + const char *name; + const char *str; + int include = 0; + int stop_at = 0; + int offset; + int len; + + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); + stop_at = nextoffset; + + switch (tag) { + case FDT_PROP: + include = want >= 2; + stop_at = offset; + prop = fdt_get_property_by_offset(fdt, offset, NULL); + str = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + if (str_in_list(str, exc_prop, exc_prop_count)) + include = 0; + break; + + case FDT_NOP: + include = want >= 2; + stop_at = offset; + break; + + case FDT_BEGIN_NODE: + depth++; + if (depth == FDT_MAX_DEPTH) + return -FDT_ERR_BADSTRUCTURE; + name = fdt_get_name(fdt, offset, &len); + if (end - path + 2 + len >= path_len) + return -FDT_ERR_NOSPACE; + if (end != path + 1) + *end++ = '/'; + strcpy(end, name); + end += len; + stack[depth] = want; + if (want == 1) + stop_at = offset; + if (str_in_list(path, inc, inc_count)) + want = 2; + else if (want) + want--; + else + stop_at = offset; + include = want; + break; + + case FDT_END_NODE: + include = want; + want = stack[depth--]; + while (end > path && *--end != '/') + ; + *end = '\0'; + break; + + case FDT_END: + include = 1; + break; + } + + if (include && start == -1) { + /* Should we merge with previous? */ + if (count && count <= max_regions && + offset == region[count - 1].offset + + region[count - 1].size - base) + start = region[--count].offset - base; + else + start = offset; + } + + if (!include && start != -1) { + if (count < max_regions) { + region[count].offset = base + start; + region[count].size = stop_at - start; + } + count++; + start = -1; + } + } while (tag != FDT_END); + + if (nextoffset != fdt_size_dt_struct(fdt)) + return -FDT_ERR_BADLAYOUT; + + /* Add a region for the END tag and the string table */ + if (count < max_regions) { + region[count].offset = base + start; + region[count].size = nextoffset - start; + if (add_string_tab) + region[count].size += fdt_size_dt_strings(fdt); + } + count++; + + return count; +} diff --git a/lib/rsa/Makefile b/lib/rsa/Makefile new file mode 100644 index 0000000000..9eb3e40f56 --- /dev/null +++ b/lib/rsa/Makefile @@ -0,0 +1,48 @@ +# +# Copyright (c) 2013, Google Inc. +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. +# +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +include $(TOPDIR)/config.mk + +LIB = $(obj)librsa.o + +ifdef CONFIG_FIT_SIGNATURE +COBJS-$(CONFIG_RSA) += rsa-verify.o +endif + +COBJS := $(sort $(COBJS-y)) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +$(LIB): $(obj).depend $(OBJS) + $(call cmd_link_o_target, $(OBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/lib/rsa/rsa-sign.c b/lib/rsa/rsa-sign.c new file mode 100644 index 0000000000..a75ae245ef --- /dev/null +++ b/lib/rsa/rsa-sign.c @@ -0,0 +1,460 @@ +/* + * Copyright (c) 2013, Google Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include "mkimage.h" +#include <stdio.h> +#include <string.h> +#include <error.h> +#include <image.h> +#include <time.h> +#include <openssl/rsa.h> +#include <openssl/pem.h> +#include <openssl/err.h> +#include <openssl/ssl.h> +#include <openssl/evp.h> + +#if OPENSSL_VERSION_NUMBER >= 0x10000000L +#define HAVE_ERR_REMOVE_THREAD_STATE +#endif + +static int rsa_err(const char *msg) +{ + unsigned long sslErr = ERR_get_error(); + + fprintf(stderr, "%s", msg); + fprintf(stderr, ": %s\n", + ERR_error_string(sslErr, 0)); + + return -1; +} + +/** + * rsa_get_pub_key() - read a public key from a .crt file + * + * @keydir: Directory containins the key + * @name Name of key file (will have a .crt extension) + * @rsap Returns RSA object, or NULL on failure + * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL) + */ +static int rsa_get_pub_key(const char *keydir, const char *name, RSA **rsap) +{ + char path[1024]; + EVP_PKEY *key; + X509 *cert; + RSA *rsa; + FILE *f; + int ret; + + *rsap = NULL; + snprintf(path, sizeof(path), "%s/%s.crt", keydir, name); + f = fopen(path, "r"); + if (!f) { + fprintf(stderr, "Couldn't open RSA certificate: '%s': %s\n", + path, strerror(errno)); + return -EACCES; + } + + /* Read the certificate */ + cert = NULL; + if (!PEM_read_X509(f, &cert, NULL, NULL)) { + rsa_err("Couldn't read certificate"); + ret = -EINVAL; + goto err_cert; + } + + /* Get the public key from the certificate. */ + key = X509_get_pubkey(cert); + if (!key) { + rsa_err("Couldn't read public key\n"); + ret = -EINVAL; + goto err_pubkey; + } + + /* Convert to a RSA_style key. */ + rsa = EVP_PKEY_get1_RSA(key); + if (!rsa) { + rsa_err("Couldn't convert to a RSA style key"); + goto err_rsa; + } + fclose(f); + EVP_PKEY_free(key); + X509_free(cert); + *rsap = rsa; + + return 0; + +err_rsa: + EVP_PKEY_free(key); +err_pubkey: + X509_free(cert); +err_cert: + fclose(f); + return ret; +} + +/** + * rsa_get_priv_key() - read a private key from a .key file + * + * @keydir: Directory containins the key + * @name Name of key file (will have a .key extension) + * @rsap Returns RSA object, or NULL on failure + * @return 0 if ok, -ve on error (in which case *rsap will be set to NULL) + */ +static int rsa_get_priv_key(const char *keydir, const char *name, RSA **rsap) +{ + char path[1024]; + RSA *rsa; + FILE *f; + + *rsap = NULL; + snprintf(path, sizeof(path), "%s/%s.key", keydir, name); + f = fopen(path, "r"); + if (!f) { + fprintf(stderr, "Couldn't open RSA private key: '%s': %s\n", + path, strerror(errno)); + return -ENOENT; + } + + rsa = PEM_read_RSAPrivateKey(f, 0, NULL, path); + if (!rsa) { + rsa_err("Failure reading private key"); + fclose(f); + return -EPROTO; + } + fclose(f); + *rsap = rsa; + + return 0; +} + +static int rsa_init(void) +{ + int ret; + + ret = SSL_library_init(); + if (!ret) { + fprintf(stderr, "Failure to init SSL library\n"); + return -1; + } + SSL_load_error_strings(); + + OpenSSL_add_all_algorithms(); + OpenSSL_add_all_digests(); + OpenSSL_add_all_ciphers(); + + return 0; +} + +static void rsa_remove(void) +{ + CRYPTO_cleanup_all_ex_data(); + ERR_free_strings(); +#ifdef HAVE_ERR_REMOVE_THREAD_STATE + ERR_remove_thread_state(NULL); +#else + ERR_remove_state(0); +#endif + EVP_cleanup(); +} + +static int rsa_sign_with_key(RSA *rsa, const struct image_region region[], + int region_count, uint8_t **sigp, uint *sig_size) +{ + EVP_PKEY *key; + EVP_MD_CTX *context; + int size, ret = 0; + uint8_t *sig; + int i; + + key = EVP_PKEY_new(); + if (!key) + return rsa_err("EVP_PKEY object creation failed"); + + if (!EVP_PKEY_set1_RSA(key, rsa)) { + ret = rsa_err("EVP key setup failed"); + goto err_set; + } + + size = EVP_PKEY_size(key); + sig = malloc(size); + if (!sig) { + fprintf(stderr, "Out of memory for signature (%d bytes)\n", + size); + ret = -ENOMEM; + goto err_alloc; + } + + context = EVP_MD_CTX_create(); + if (!context) { + ret = rsa_err("EVP context creation failed"); + goto err_create; + } + EVP_MD_CTX_init(context); + if (!EVP_SignInit(context, EVP_sha1())) { + ret = rsa_err("Signer setup failed"); + goto err_sign; + } + + for (i = 0; i < region_count; i++) { + if (!EVP_SignUpdate(context, region[i].data, region[i].size)) { + ret = rsa_err("Signing data failed"); + goto err_sign; + } + } + + if (!EVP_SignFinal(context, sig, sig_size, key)) { + ret = rsa_err("Could not obtain signature"); + goto err_sign; + } + EVP_MD_CTX_cleanup(context); + EVP_MD_CTX_destroy(context); + EVP_PKEY_free(key); + + debug("Got signature: %d bytes, expected %d\n", *sig_size, size); + *sigp = sig; + *sig_size = size; + + return 0; + +err_sign: + EVP_MD_CTX_destroy(context); +err_create: + free(sig); +err_alloc: +err_set: + EVP_PKEY_free(key); + return ret; +} + +int rsa_sign(struct image_sign_info *info, + const struct image_region region[], int region_count, + uint8_t **sigp, uint *sig_len) +{ + RSA *rsa; + int ret; + + ret = rsa_init(); + if (ret) + return ret; + + ret = rsa_get_priv_key(info->keydir, info->keyname, &rsa); + if (ret) + goto err_priv; + ret = rsa_sign_with_key(rsa, region, region_count, sigp, sig_len); + if (ret) + goto err_sign; + + RSA_free(rsa); + rsa_remove(); + + return ret; + +err_sign: + RSA_free(rsa); +err_priv: + rsa_remove(); + return ret; +} + +/* + * rsa_get_params(): - Get the important parameters of an RSA public key + */ +int rsa_get_params(RSA *key, uint32_t *n0_invp, BIGNUM **modulusp, + BIGNUM **r_squaredp) +{ + BIGNUM *big1, *big2, *big32, *big2_32; + BIGNUM *n, *r, *r_squared, *tmp; + BN_CTX *bn_ctx = BN_CTX_new(); + int ret = 0; + + /* Initialize BIGNUMs */ + big1 = BN_new(); + big2 = BN_new(); + big32 = BN_new(); + r = BN_new(); + r_squared = BN_new(); + tmp = BN_new(); + big2_32 = BN_new(); + n = BN_new(); + if (!big1 || !big2 || !big32 || !r || !r_squared || !tmp || !big2_32 || + !n) { + fprintf(stderr, "Out of memory (bignum)\n"); + return -ENOMEM; + } + + if (!BN_copy(n, key->n) || !BN_set_word(big1, 1L) || + !BN_set_word(big2, 2L) || !BN_set_word(big32, 32L)) + ret = -1; + + /* big2_32 = 2^32 */ + if (!BN_exp(big2_32, big2, big32, bn_ctx)) + ret = -1; + + /* Calculate n0_inv = -1 / n[0] mod 2^32 */ + if (!BN_mod_inverse(tmp, n, big2_32, bn_ctx) || + !BN_sub(tmp, big2_32, tmp)) + ret = -1; + *n0_invp = BN_get_word(tmp); + + /* Calculate R = 2^(# of key bits) */ + if (!BN_set_word(tmp, BN_num_bits(n)) || + !BN_exp(r, big2, tmp, bn_ctx)) + ret = -1; + + /* Calculate r_squared = R^2 mod n */ + if (!BN_copy(r_squared, r) || + !BN_mul(tmp, r_squared, r, bn_ctx) || + !BN_mod(r_squared, tmp, n, bn_ctx)) + ret = -1; + + *modulusp = n; + *r_squaredp = r_squared; + + BN_free(big1); + BN_free(big2); + BN_free(big32); + BN_free(r); + BN_free(tmp); + BN_free(big2_32); + if (ret) { + fprintf(stderr, "Bignum operations failed\n"); + return -ENOMEM; + } + + return ret; +} + +static int fdt_add_bignum(void *blob, int noffset, const char *prop_name, + BIGNUM *num, int num_bits) +{ + int nwords = num_bits / 32; + int size; + uint32_t *buf, *ptr; + BIGNUM *tmp, *big2, *big32, *big2_32; + BN_CTX *ctx; + int ret; + + tmp = BN_new(); + big2 = BN_new(); + big32 = BN_new(); + big2_32 = BN_new(); + if (!tmp || !big2 || !big32 || !big2_32) { + fprintf(stderr, "Out of memory (bignum)\n"); + return -ENOMEM; + } + ctx = BN_CTX_new(); + if (!tmp) { + fprintf(stderr, "Out of memory (bignum context)\n"); + return -ENOMEM; + } + BN_set_word(big2, 2L); + BN_set_word(big32, 32L); + BN_exp(big2_32, big2, big32, ctx); /* B = 2^32 */ + + size = nwords * sizeof(uint32_t); + buf = malloc(size); + if (!buf) { + fprintf(stderr, "Out of memory (%d bytes)\n", size); + return -ENOMEM; + } + + /* Write out modulus as big endian array of integers */ + for (ptr = buf + nwords - 1; ptr >= buf; ptr--) { + BN_mod(tmp, num, big2_32, ctx); /* n = N mod B */ + *ptr = cpu_to_fdt32(BN_get_word(tmp)); + BN_rshift(num, num, 32); /* N = N/B */ + } + + ret = fdt_setprop(blob, noffset, prop_name, buf, size); + if (ret) { + fprintf(stderr, "Failed to write public key to FIT\n"); + return -ENOSPC; + } + free(buf); + BN_free(tmp); + BN_free(big2); + BN_free(big32); + BN_free(big2_32); + + return ret; +} + +int rsa_add_verify_data(struct image_sign_info *info, void *keydest) +{ + BIGNUM *modulus, *r_squared; + uint32_t n0_inv; + int parent, node; + char name[100]; + int ret; + int bits; + RSA *rsa; + + debug("%s: Getting verification data\n", __func__); + ret = rsa_get_pub_key(info->keydir, info->keyname, &rsa); + if (ret) + return ret; + ret = rsa_get_params(rsa, &n0_inv, &modulus, &r_squared); + if (ret) + return ret; + bits = BN_num_bits(modulus); + parent = fdt_subnode_offset(keydest, 0, FIT_SIG_NODENAME); + if (parent == -FDT_ERR_NOTFOUND) { + parent = fdt_add_subnode(keydest, 0, FIT_SIG_NODENAME); + if (parent < 0) { + fprintf(stderr, "Couldn't create signature node: %s\n", + fdt_strerror(parent)); + return -EINVAL; + } + } + + /* Either create or overwrite the named key node */ + snprintf(name, sizeof(name), "key-%s", info->keyname); + node = fdt_subnode_offset(keydest, parent, name); + if (node == -FDT_ERR_NOTFOUND) { + node = fdt_add_subnode(keydest, parent, name); + if (node < 0) { + fprintf(stderr, "Could not create key subnode: %s\n", + fdt_strerror(node)); + return -EINVAL; + } + } else if (node < 0) { + fprintf(stderr, "Cannot select keys parent: %s\n", + fdt_strerror(node)); + return -ENOSPC; + } + + ret = fdt_setprop_string(keydest, node, "key-name-hint", + info->keyname); + ret |= fdt_setprop_u32(keydest, node, "rsa,num-bits", bits); + ret |= fdt_setprop_u32(keydest, node, "rsa,n0-inverse", n0_inv); + ret |= fdt_add_bignum(keydest, node, "rsa,modulus", modulus, bits); + ret |= fdt_add_bignum(keydest, node, "rsa,r-squared", r_squared, bits); + ret |= fdt_setprop_string(keydest, node, FIT_ALGO_PROP, + info->algo->name); + if (info->require_keys) { + fdt_setprop_string(keydest, node, "required", + info->require_keys); + } + BN_free(modulus); + BN_free(r_squared); + if (ret) + return -EIO; + + return 0; +} diff --git a/lib/rsa/rsa-verify.c b/lib/rsa/rsa-verify.c new file mode 100644 index 0000000000..6a0268919d --- /dev/null +++ b/lib/rsa/rsa-verify.c @@ -0,0 +1,385 @@ +/* + * Copyright (c) 2013, Google Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <fdtdec.h> +#include <rsa.h> +#include <sha1.h> +#include <asm/byteorder.h> +#include <asm/errno.h> +#include <asm/unaligned.h> + +/** + * struct rsa_public_key - holder for a public key + * + * An RSA public key consists of a modulus (typically called N), the inverse + * and R^2, where R is 2^(# key bits). + */ +struct rsa_public_key { + uint len; /* Length of modulus[] in number of uint32_t */ + uint32_t n0inv; /* -1 / modulus[0] mod 2^32 */ + uint32_t *modulus; /* modulus as little endian array */ + uint32_t *rr; /* R^2 as little endian array */ +}; + +#define UINT64_MULT32(v, multby) (((uint64_t)(v)) * ((uint32_t)(multby))) + +#define RSA2048_BYTES (2048 / 8) + +/* This is the minimum/maximum key size we support, in bits */ +#define RSA_MIN_KEY_BITS 2048 +#define RSA_MAX_KEY_BITS 2048 + +/* This is the maximum signature length that we support, in bits */ +#define RSA_MAX_SIG_BITS 2048 + +static const uint8_t padding_sha1_rsa2048[RSA2048_BYTES - SHA1_SUM_LEN] = { + 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x00, 0x30, 0x21, 0x30, + 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, + 0x05, 0x00, 0x04, 0x14 +}; + +/** + * subtract_modulus() - subtract modulus from the given value + * + * @key: Key containing modulus to subtract + * @num: Number to subtract modulus from, as little endian word array + */ +static void subtract_modulus(const struct rsa_public_key *key, uint32_t num[]) +{ + int64_t acc = 0; + uint i; + + for (i = 0; i < key->len; i++) { + acc += (uint64_t)num[i] - key->modulus[i]; + num[i] = (uint32_t)acc; + acc >>= 32; + } +} + +/** + * greater_equal_modulus() - check if a value is >= modulus + * + * @key: Key containing modulus to check + * @num: Number to check against modulus, as little endian word array + * @return 0 if num < modulus, 1 if num >= modulus + */ +static int greater_equal_modulus(const struct rsa_public_key *key, + uint32_t num[]) +{ + uint32_t i; + + for (i = key->len - 1; i >= 0; i--) { + if (num[i] < key->modulus[i]) + return 0; + if (num[i] > key->modulus[i]) + return 1; + } + + return 1; /* equal */ +} + +/** + * montgomery_mul_add_step() - Perform montgomery multiply-add step + * + * Operation: montgomery result[] += a * b[] / n0inv % modulus + * + * @key: RSA key + * @result: Place to put result, as little endian word array + * @a: Multiplier + * @b: Multiplicand, as little endian word array + */ +static void montgomery_mul_add_step(const struct rsa_public_key *key, + uint32_t result[], const uint32_t a, const uint32_t b[]) +{ + uint64_t acc_a, acc_b; + uint32_t d0; + uint i; + + acc_a = (uint64_t)a * b[0] + result[0]; + d0 = (uint32_t)acc_a * key->n0inv; + acc_b = (uint64_t)d0 * key->modulus[0] + (uint32_t)acc_a; + for (i = 1; i < key->len; i++) { + acc_a = (acc_a >> 32) + (uint64_t)a * b[i] + result[i]; + acc_b = (acc_b >> 32) + (uint64_t)d0 * key->modulus[i] + + (uint32_t)acc_a; + result[i - 1] = (uint32_t)acc_b; + } + + acc_a = (acc_a >> 32) + (acc_b >> 32); + + result[i - 1] = (uint32_t)acc_a; + + if (acc_a >> 32) + subtract_modulus(key, result); +} + +/** + * montgomery_mul() - Perform montgomery mutitply + * + * Operation: montgomery result[] = a[] * b[] / n0inv % modulus + * + * @key: RSA key + * @result: Place to put result, as little endian word array + * @a: Multiplier, as little endian word array + * @b: Multiplicand, as little endian word array + */ +static void montgomery_mul(const struct rsa_public_key *key, + uint32_t result[], uint32_t a[], const uint32_t b[]) +{ + uint i; + + for (i = 0; i < key->len; ++i) + result[i] = 0; + for (i = 0; i < key->len; ++i) + montgomery_mul_add_step(key, result, a[i], b); +} + +/** + * pow_mod() - in-place public exponentiation + * + * @key: RSA key + * @inout: Big-endian word array containing value and result + */ +static int pow_mod(const struct rsa_public_key *key, uint32_t *inout) +{ + uint32_t *result, *ptr; + uint i; + + /* Sanity check for stack size - key->len is in 32-bit words */ + if (key->len > RSA_MAX_KEY_BITS / 32) { + debug("RSA key words %u exceeds maximum %d\n", key->len, + RSA_MAX_KEY_BITS / 32); + return -EINVAL; + } + + uint32_t val[key->len], acc[key->len], tmp[key->len]; + result = tmp; /* Re-use location. */ + + /* Convert from big endian byte array to little endian word array. */ + for (i = 0, ptr = inout + key->len - 1; i < key->len; i++, ptr--) + val[i] = get_unaligned_be32(ptr); + + montgomery_mul(key, acc, val, key->rr); /* axx = a * RR / R mod M */ + for (i = 0; i < 16; i += 2) { + montgomery_mul(key, tmp, acc, acc); /* tmp = acc^2 / R mod M */ + montgomery_mul(key, acc, tmp, tmp); /* acc = tmp^2 / R mod M */ + } + montgomery_mul(key, result, acc, val); /* result = XX * a / R mod M */ + + /* Make sure result < mod; result is at most 1x mod too large. */ + if (greater_equal_modulus(key, result)) + subtract_modulus(key, result); + + /* Convert to bigendian byte array */ + for (i = key->len - 1, ptr = inout; (int)i >= 0; i--, ptr++) + put_unaligned_be32(result[i], ptr); + + return 0; +} + +static int rsa_verify_key(const struct rsa_public_key *key, const uint8_t *sig, + const uint32_t sig_len, const uint8_t *hash) +{ + const uint8_t *padding; + int pad_len; + int ret; + + if (!key || !sig || !hash) + return -EIO; + + if (sig_len != (key->len * sizeof(uint32_t))) { + debug("Signature is of incorrect length %d\n", sig_len); + return -EINVAL; + } + + /* Sanity check for stack size */ + if (sig_len > RSA_MAX_SIG_BITS / 8) { + debug("Signature length %u exceeds maximum %d\n", sig_len, + RSA_MAX_SIG_BITS / 8); + return -EINVAL; + } + + uint32_t buf[sig_len / sizeof(uint32_t)]; + + memcpy(buf, sig, sig_len); + + ret = pow_mod(key, buf); + if (ret) + return ret; + + /* Determine padding to use depending on the signature type. */ + padding = padding_sha1_rsa2048; + pad_len = RSA2048_BYTES - SHA1_SUM_LEN; + + /* Check pkcs1.5 padding bytes. */ + if (memcmp(buf, padding, pad_len)) { + debug("In RSAVerify(): Padding check failed!\n"); + return -EINVAL; + } + + /* Check hash. */ + if (memcmp((uint8_t *)buf + pad_len, hash, sig_len - pad_len)) { + debug("In RSAVerify(): Hash check failed!\n"); + return -EACCES; + } + + return 0; +} + +static void rsa_convert_big_endian(uint32_t *dst, const uint32_t *src, int len) +{ + int i; + + for (i = 0; i < len; i++) + dst[i] = fdt32_to_cpu(src[len - 1 - i]); +} + +static int rsa_verify_with_keynode(struct image_sign_info *info, + const void *hash, uint8_t *sig, uint sig_len, int node) +{ + const void *blob = info->fdt_blob; + struct rsa_public_key key; + const void *modulus, *rr; + int ret; + + if (node < 0) { + debug("%s: Skipping invalid node", __func__); + return -EBADF; + } + if (!fdt_getprop(blob, node, "rsa,n0-inverse", NULL)) { + debug("%s: Missing rsa,n0-inverse", __func__); + return -EFAULT; + } + key.len = fdtdec_get_int(blob, node, "rsa,num-bits", 0); + key.n0inv = fdtdec_get_int(blob, node, "rsa,n0-inverse", 0); + modulus = fdt_getprop(blob, node, "rsa,modulus", NULL); + rr = fdt_getprop(blob, node, "rsa,r-squared", NULL); + if (!key.len || !modulus || !rr) { + debug("%s: Missing RSA key info", __func__); + return -EFAULT; + } + + /* Sanity check for stack size */ + if (key.len > RSA_MAX_KEY_BITS || key.len < RSA_MIN_KEY_BITS) { + debug("RSA key bits %u outside allowed range %d..%d\n", + key.len, RSA_MIN_KEY_BITS, RSA_MAX_KEY_BITS); + return -EFAULT; + } + key.len /= sizeof(uint32_t) * 8; + uint32_t key1[key.len], key2[key.len]; + + key.modulus = key1; + key.rr = key2; + rsa_convert_big_endian(key.modulus, modulus, key.len); + rsa_convert_big_endian(key.rr, rr, key.len); + if (!key.modulus || !key.rr) { + debug("%s: Out of memory", __func__); + return -ENOMEM; + } + + debug("key length %d\n", key.len); + ret = rsa_verify_key(&key, sig, sig_len, hash); + if (ret) { + printf("%s: RSA failed to verify: %d\n", __func__, ret); + return ret; + } + + return 0; +} + +int rsa_verify(struct image_sign_info *info, + const struct image_region region[], int region_count, + uint8_t *sig, uint sig_len) +{ + const void *blob = info->fdt_blob; + uint8_t hash[SHA1_SUM_LEN]; + int ndepth, noffset; + int sig_node, node; + char name[100]; + sha1_context ctx; + int ret, i; + + sig_node = fdt_subnode_offset(blob, 0, FIT_SIG_NODENAME); + if (sig_node < 0) { + debug("%s: No signature node found\n", __func__); + return -ENOENT; + } + + sha1_starts(&ctx); + for (i = 0; i < region_count; i++) + sha1_update(&ctx, region[i].data, region[i].size); + sha1_finish(&ctx, hash); + + /* See if we must use a particular key */ + if (info->required_keynode != -1) { + ret = rsa_verify_with_keynode(info, hash, sig, sig_len, + info->required_keynode); + if (!ret) + return ret; + } + + /* Look for a key that matches our hint */ + snprintf(name, sizeof(name), "key-%s", info->keyname); + node = fdt_subnode_offset(blob, sig_node, name); + ret = rsa_verify_with_keynode(info, hash, sig, sig_len, node); + if (!ret) + return ret; + + /* No luck, so try each of the keys in turn */ + for (ndepth = 0, noffset = fdt_next_node(info->fit, sig_node, &ndepth); + (noffset >= 0) && (ndepth > 0); + noffset = fdt_next_node(info->fit, noffset, &ndepth)) { + if (ndepth == 1 && noffset != node) { + ret = rsa_verify_with_keynode(info, hash, sig, sig_len, + noffset); + if (!ret) + break; + } + } + + return ret; +} diff --git a/lib/trace.c b/lib/trace.c new file mode 100644 index 0000000000..e7455bcfe6 --- /dev/null +++ b/lib/trace.c @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2012 The Chromium OS Authors. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <trace.h> +#include <asm/io.h> +#include <asm/sections.h> + +DECLARE_GLOBAL_DATA_PTR; + +static char trace_enabled __attribute__((section(".data"))); +static char trace_inited __attribute__((section(".data"))); + +/* The header block at the start of the trace memory area */ +struct trace_hdr { + int func_count; /* Total number of function call sites */ + u64 call_count; /* Total number of tracked function calls */ + u64 untracked_count; /* Total number of untracked function calls */ + int funcs_used; /* Total number of functions used */ + + /* + * Call count for each function. This is indexed by the word offset + * of the function from gd->relocaddr + */ + uintptr_t *call_accum; + + /* Function trace list */ + struct trace_call *ftrace; /* The function call records */ + ulong ftrace_size; /* Num. of ftrace records we have space for */ + ulong ftrace_count; /* Num. of ftrace records written */ + ulong ftrace_too_deep_count; /* Functions that were too deep */ + + int depth; + int depth_limit; + int max_depth; +}; + +static struct trace_hdr *hdr; /* Pointer to start of trace buffer */ + +static inline uintptr_t __attribute__((no_instrument_function)) + func_ptr_to_num(void *func_ptr) +{ + uintptr_t offset = (uintptr_t)func_ptr; + +#ifdef CONFIG_SANDBOX + offset -= (uintptr_t)&_init; +#else + if (gd->flags & GD_FLG_RELOC) + offset -= gd->relocaddr; + else + offset -= CONFIG_SYS_TEXT_BASE; +#endif + return offset / FUNC_SITE_SIZE; +} + +static void __attribute__((no_instrument_function)) add_ftrace(void *func_ptr, + void *caller, ulong flags) +{ + if (hdr->depth > hdr->depth_limit) { + hdr->ftrace_too_deep_count++; + return; + } + if (hdr->ftrace_count < hdr->ftrace_size) { + struct trace_call *rec = &hdr->ftrace[hdr->ftrace_count]; + + rec->func = func_ptr_to_num(func_ptr); + rec->caller = func_ptr_to_num(caller); + rec->flags = flags | (timer_get_us() & FUNCF_TIMESTAMP_MASK); + } + hdr->ftrace_count++; +} + +static void __attribute__((no_instrument_function)) add_textbase(void) +{ + if (hdr->ftrace_count < hdr->ftrace_size) { + struct trace_call *rec = &hdr->ftrace[hdr->ftrace_count]; + + rec->func = CONFIG_SYS_TEXT_BASE; + rec->caller = 0; + rec->flags = FUNCF_TEXTBASE; + } + hdr->ftrace_count++; +} + +/** + * This is called on every function entry + * + * We add to our tally for this function and add to the list of called + * functions. + * + * @param func_ptr Pointer to function being entered + * @param caller Pointer to function which called this function + */ +void __attribute__((no_instrument_function)) __cyg_profile_func_enter( + void *func_ptr, void *caller) +{ + if (trace_enabled) { + int func; + + add_ftrace(func_ptr, caller, FUNCF_ENTRY); + func = func_ptr_to_num(func_ptr); + if (func < hdr->func_count) { + hdr->call_accum[func]++; + hdr->call_count++; + } else { + hdr->untracked_count++; + } + hdr->depth++; + if (hdr->depth > hdr->depth_limit) + hdr->max_depth = hdr->depth; + } +} + +/** + * This is called on every function exit + * + * We do nothing here. + * + * @param func_ptr Pointer to function being entered + * @param caller Pointer to function which called this function + */ +void __attribute__((no_instrument_function)) __cyg_profile_func_exit( + void *func_ptr, void *caller) +{ + if (trace_enabled) { + add_ftrace(func_ptr, caller, FUNCF_EXIT); + hdr->depth--; + } +} + +/** + * Produce a list of called functions + * + * The information is written into the supplied buffer - a header followed + * by a list of function records. + * + * @param buff Buffer to place list into + * @param buff_size Size of buffer + * @param needed Returns size of buffer needed, which may be + * greater than buff_size if we ran out of space. + * @return 0 if ok, -1 if space was exhausted + */ +int trace_list_functions(void *buff, int buff_size, unsigned int *needed) +{ + struct trace_output_hdr *output_hdr = NULL; + void *end, *ptr = buff; + int func; + int upto; + + end = buff ? buff + buff_size : NULL; + + /* Place some header information */ + if (ptr + sizeof(struct trace_output_hdr) < end) + output_hdr = ptr; + ptr += sizeof(struct trace_output_hdr); + + /* Add information about each function */ + for (func = upto = 0; func < hdr->func_count; func++) { + int calls = hdr->call_accum[func]; + + if (!calls) + continue; + + if (ptr + sizeof(struct trace_output_func) < end) { + struct trace_output_func *stats = ptr; + + stats->offset = func * FUNC_SITE_SIZE; + stats->call_count = calls; + upto++; + } + ptr += sizeof(struct trace_output_func); + } + + /* Update the header */ + if (output_hdr) { + output_hdr->rec_count = upto; + output_hdr->type = TRACE_CHUNK_FUNCS; + } + + /* Work out how must of the buffer we used */ + *needed = ptr - buff; + if (ptr > end) + return -1; + return 0; +} + +int trace_list_calls(void *buff, int buff_size, unsigned *needed) +{ + struct trace_output_hdr *output_hdr = NULL; + void *end, *ptr = buff; + int rec, upto; + int count; + + end = buff ? buff + buff_size : NULL; + + /* Place some header information */ + if (ptr + sizeof(struct trace_output_hdr) < end) + output_hdr = ptr; + ptr += sizeof(struct trace_output_hdr); + + /* Add information about each call */ + count = hdr->ftrace_count; + if (count > hdr->ftrace_size) + count = hdr->ftrace_size; + for (rec = upto = 0; rec < count; rec++) { + if (ptr + sizeof(struct trace_call) < end) { + struct trace_call *call = &hdr->ftrace[rec]; + struct trace_call *out = ptr; + + out->func = call->func * FUNC_SITE_SIZE; + out->caller = call->caller * FUNC_SITE_SIZE; + out->flags = call->flags; + upto++; + } + ptr += sizeof(struct trace_call); + } + + /* Update the header */ + if (output_hdr) { + output_hdr->rec_count = upto; + output_hdr->type = TRACE_CHUNK_CALLS; + } + + /* Work out how must of the buffer we used */ + *needed = ptr - buff; + if (ptr > end) + return -1; + return 0; +} + +/* Print basic information about tracing */ +void trace_print_stats(void) +{ + ulong count; + +#ifndef FTRACE + puts("Warning: make U-Boot with FTRACE to enable function instrumenting.\n"); + puts("You will likely get zeroed data here\n"); +#endif + if (!trace_inited) { + printf("Trace is disabled\n"); + return; + } + print_grouped_ull(hdr->func_count, 10); + puts(" function sites\n"); + print_grouped_ull(hdr->call_count, 10); + puts(" function calls\n"); + print_grouped_ull(hdr->untracked_count, 10); + puts(" untracked function calls\n"); + count = min(hdr->ftrace_count, hdr->ftrace_size); + print_grouped_ull(count, 10); + puts(" traced function calls"); + if (hdr->ftrace_count > hdr->ftrace_size) { + printf(" (%lu dropped due to overflow)", + hdr->ftrace_count - hdr->ftrace_size); + } + puts("\n"); + printf("%15d maximum observed call depth\n", hdr->max_depth); + printf("%15d call depth limit\n", hdr->depth_limit); + print_grouped_ull(hdr->ftrace_too_deep_count, 10); + puts(" calls not traced due to depth\n"); +} + +void __attribute__((no_instrument_function)) trace_set_enabled(int enabled) +{ + trace_enabled = enabled != 0; +} + +/** + * Init the tracing system ready for used, and enable it + * + * @param buff Pointer to trace buffer + * @param buff_size Size of trace buffer + */ +int __attribute__((no_instrument_function)) trace_init(void *buff, + size_t buff_size) +{ + ulong func_count = gd->mon_len / FUNC_SITE_SIZE; + size_t needed; + int was_disabled = !trace_enabled; + + if (!was_disabled) { +#ifdef CONFIG_TRACE_EARLY + char *end; + ulong used; + + /* + * Copy over the early trace data if we have it. Disable + * tracing while we are doing this. + */ + trace_enabled = 0; + hdr = map_sysmem(CONFIG_TRACE_EARLY_ADDR, + CONFIG_TRACE_EARLY_SIZE); + end = (char *)&hdr->ftrace[hdr->ftrace_count]; + used = end - (char *)hdr; + printf("trace: copying %08lx bytes of early data from %x to %08lx\n", + used, CONFIG_TRACE_EARLY_ADDR, + (ulong)map_to_sysmem(buff)); + memcpy(buff, hdr, used); +#else + puts("trace: already enabled\n"); + return -1; +#endif + } + hdr = (struct trace_hdr *)buff; + needed = sizeof(*hdr) + func_count * sizeof(uintptr_t); + if (needed > buff_size) { + printf("trace: buffer size %zd bytes: at least %zd needed\n", + buff_size, needed); + return -1; + } + + if (was_disabled) + memset(hdr, '\0', needed); + hdr->func_count = func_count; + hdr->call_accum = (uintptr_t *)(hdr + 1); + + /* Use any remaining space for the timed function trace */ + hdr->ftrace = (struct trace_call *)(buff + needed); + hdr->ftrace_size = (buff_size - needed) / sizeof(*hdr->ftrace); + add_textbase(); + + puts("trace: enabled\n"); + hdr->depth_limit = 15; + trace_enabled = 1; + trace_inited = 1; + return 0; +} + +#ifdef CONFIG_TRACE_EARLY +int __attribute__((no_instrument_function)) trace_early_init(void) +{ + ulong func_count = gd->mon_len / FUNC_SITE_SIZE; + size_t buff_size = CONFIG_TRACE_EARLY_SIZE; + size_t needed; + + /* We can ignore additional calls to this function */ + if (trace_enabled) + return 0; + + hdr = map_sysmem(CONFIG_TRACE_EARLY_ADDR, CONFIG_TRACE_EARLY_SIZE); + needed = sizeof(*hdr) + func_count * sizeof(uintptr_t); + if (needed > buff_size) { + printf("trace: buffer size is %zd bytes, at least %zd needed\n", + buff_size, needed); + return -1; + } + + memset(hdr, '\0', needed); + hdr->call_accum = (uintptr_t *)(hdr + 1); + hdr->func_count = func_count; + + /* Use any remaining space for the timed function trace */ + hdr->ftrace = (struct trace_call *)((char *)hdr + needed); + hdr->ftrace_size = (buff_size - needed) / sizeof(*hdr->ftrace); + add_textbase(); + hdr->depth_limit = 200; + printf("trace: early enable at %08x\n", CONFIG_TRACE_EARLY_ADDR); + + trace_enabled = 1; + return 0; +} +#endif diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 533a96b85e..82e5c13653 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -870,3 +870,19 @@ char *simple_itoa(ulong i) } while (i > 0); return p + 1; } + +/* We don't seem to have %'d in U-Boot */ +void print_grouped_ull(unsigned long long int_val, int digits) +{ + char str[21], *s; + int grab = 3; + + digits = (digits + 2) / 3; + sprintf(str, "%*llu", digits * 3, int_val); + for (s = str; *s; s += grab) { + if (s != str) + putc(s[-1] != ' ' ? ',' : ' '); + printf("%.*s", grab, s); + grab = 3; + } +} diff --git a/test/image/test-fit.py b/test/image/test-fit.py index c4e8211162..aad9f59019 100755 --- a/test/image/test-fit.py +++ b/test/image/test-fit.py @@ -272,12 +272,13 @@ def set_test(name): test_name = name print name -def fail(msg): +def fail(msg, stdout): """Raise an error with a helpful failure message Args: msg: Message to display """ + print stdout raise ValueError("Test '%s' failed: %s" % (test_name, msg)) def run_fit_test(mkimage, u_boot): @@ -341,11 +342,11 @@ def run_fit_test(mkimage, u_boot): set_test('Kernel load') stdout = command.Output(u_boot, '-d', control_dtb, '-c', cmd) if read_file(kernel) != read_file(kernel_out): - fail('Kernel not loaded') + fail('Kernel not loaded', stdout) if read_file(control_dtb) == read_file(fdt_out): - fail('FDT loaded but should be ignored') + fail('FDT loaded but should be ignored', stdout) if read_file(ramdisk) == read_file(ramdisk_out): - fail('Ramdisk loaded but should not be') + fail('Ramdisk loaded but should not be', stdout) # Find out the offset in the FIT where U-Boot has found the FDT line = find_matching(stdout, 'Booting using the fdt blob at ') @@ -357,7 +358,7 @@ def run_fit_test(mkimage, u_boot): real_fit_offset = data.find(fdt_magic, 4) if fit_offset != real_fit_offset: fail('U-Boot loaded FDT from offset %#x, FDT is actually at %#x' % - (fit_offset, real_fit_offset)) + (fit_offset, real_fit_offset), stdout) # Now a kernel and an FDT set_test('Kernel + FDT load') @@ -365,11 +366,11 @@ def run_fit_test(mkimage, u_boot): fit = make_fit(mkimage, params) stdout = command.Output(u_boot, '-d', control_dtb, '-c', cmd) if read_file(kernel) != read_file(kernel_out): - fail('Kernel not loaded') + fail('Kernel not loaded', stdout) if read_file(control_dtb) != read_file(fdt_out): - fail('FDT not loaded') + fail('FDT not loaded', stdout) if read_file(ramdisk) == read_file(ramdisk_out): - fail('Ramdisk loaded but should not be') + fail('Ramdisk loaded but should not be', stdout) # Try a ramdisk set_test('Kernel + FDT + Ramdisk load') @@ -378,7 +379,7 @@ def run_fit_test(mkimage, u_boot): fit = make_fit(mkimage, params) stdout = command.Output(u_boot, '-d', control_dtb, '-c', cmd) if read_file(ramdisk) != read_file(ramdisk_out): - fail('Ramdisk not loaded') + fail('Ramdisk not loaded', stdout) def run_tests(): """Parse options, run the FIT tests and print the result""" diff --git a/test/trace/test-trace.sh b/test/trace/test-trace.sh new file mode 100755 index 0000000000..696a39675b --- /dev/null +++ b/test/trace/test-trace.sh @@ -0,0 +1,89 @@ +# Copyright (c) 2013 The Chromium OS Authors. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA +# + +# Simple test script for tracing with sandbox + +OUTPUT_DIR=sandbox +TRACE_OPT="FTRACE=1" + +fail() { + echo "Test failed: $1" + if [ -n ${tmp} ]; then + rm ${tmp} + fi + exit 1 +} + +build_uboot() { + echo "Build sandbox" + OPTS="O=${OUTPUT_DIR} ${TRACE_OPT}" + NUM_CPUS=$(grep -c processor /proc/cpuinfo) + make ${OPTS} sandbox_config + make ${OPTS} -s -j${NUM_CPUS} +} + +run_trace() { + echo "Run trace" + ./${OUTPUT_DIR}/u-boot <<END + trace stats + hash sha256 0 10000 + trace pause + trace stats + hash sha256 0 10000 + trace stats + trace resume + hash sha256 0 10000 + trace pause + trace stats + reset +END +} + +check_results() { + echo "Check results" + + # Expect sha256 to run 3 times, so we see the string 6 times + if [ $(grep -c sha256 ${tmp}) -ne 6 ]; then + fail "sha256 error" + fi + + # 4 sets of results (output of 'trace stats') + if [ $(grep -c "traced function calls" ${tmp}) -ne 4 ]; then + fail "trace output error" + fi + + # Check trace counts. We expect to see an increase in the number of + # traced function calls between each 'trace stats' command, except + # between calls 2 and 3, where tracing is paused. + # This code gets the sign of the difference between each number and + # its predecessor. + counts="$(tr -d , <${tmp} | awk '/traced function calls/ { diff = $1 - upto; upto = $1; printf "%d ", diff < 0 ? -1 : (diff > 0 ? 1 : 0)}')" + + if [ "${counts}" != "1 1 0 1 " ]; then + fail "trace collection error: ${counts}" + fi +} + +echo "Simple trace test / sanity check using sandbox" +echo +tmp="$(tempfile)" +build_uboot +run_trace >${tmp} +check_results ${tmp} +rm ${tmp} +echo "Test passed" diff --git a/test/vboot/.gitignore b/test/vboot/.gitignore new file mode 100644 index 0000000000..4631242709 --- /dev/null +++ b/test/vboot/.gitignore @@ -0,0 +1,3 @@ +/*.dtb +/test.fit +/dev-keys diff --git a/test/vboot/sandbox-kernel.dts b/test/vboot/sandbox-kernel.dts new file mode 100644 index 0000000000..a1e853c9ca --- /dev/null +++ b/test/vboot/sandbox-kernel.dts @@ -0,0 +1,7 @@ +/dts-v1/; + +/ { + model = "Sandbox Verified Boot Test"; + compatible = "sandbox"; + +}; diff --git a/test/vboot/sandbox-u-boot.dts b/test/vboot/sandbox-u-boot.dts new file mode 100644 index 0000000000..a1e853c9ca --- /dev/null +++ b/test/vboot/sandbox-u-boot.dts @@ -0,0 +1,7 @@ +/dts-v1/; + +/ { + model = "Sandbox Verified Boot Test"; + compatible = "sandbox"; + +}; diff --git a/test/vboot/sign-configs.its b/test/vboot/sign-configs.its new file mode 100644 index 0000000000..db2ed79355 --- /dev/null +++ b/test/vboot/sign-configs.its @@ -0,0 +1,45 @@ +/dts-v1/; + +/ { + description = "Chrome OS kernel image with one or more FDT blobs"; + #address-cells = <1>; + + images { + kernel@1 { + data = /incbin/("test-kernel.bin"); + type = "kernel_noload"; + arch = "sandbox"; + os = "linux"; + compression = "none"; + load = <0x4>; + entry = <0x8>; + kernel-version = <1>; + hash@1 { + algo = "sha1"; + }; + }; + fdt@1 { + description = "snow"; + data = /incbin/("sandbox-kernel.dtb"); + type = "flat_dt"; + arch = "sandbox"; + compression = "none"; + fdt-version = <1>; + hash@1 { + algo = "sha1"; + }; + }; + }; + configurations { + default = "conf@1"; + conf@1 { + kernel = "kernel@1"; + fdt = "fdt@1"; + signature@1 { + algo = "sha1,rsa2048"; + key-name-hint = "dev"; + sign-images = "fdt", "kernel"; + }; + }; + }; +}; diff --git a/test/vboot/sign-images.its b/test/vboot/sign-images.its new file mode 100644 index 0000000000..f69326a39b --- /dev/null +++ b/test/vboot/sign-images.its @@ -0,0 +1,42 @@ +/dts-v1/; + +/ { + description = "Chrome OS kernel image with one or more FDT blobs"; + #address-cells = <1>; + + images { + kernel@1 { + data = /incbin/("test-kernel.bin"); + type = "kernel_noload"; + arch = "sandbox"; + os = "linux"; + compression = "none"; + load = <0x4>; + entry = <0x8>; + kernel-version = <1>; + signature@1 { + algo = "sha1,rsa2048"; + key-name-hint = "dev"; + }; + }; + fdt@1 { + description = "snow"; + data = /incbin/("sandbox-kernel.dtb"); + type = "flat_dt"; + arch = "sandbox"; + compression = "none"; + fdt-version = <1>; + signature@1 { + algo = "sha1,rsa2048"; + key-name-hint = "dev"; + }; + }; + }; + configurations { + default = "conf@1"; + conf@1 { + kernel = "kernel@1"; + fdt = "fdt@1"; + }; + }; +}; diff --git a/test/vboot/vboot_test.sh b/test/vboot/vboot_test.sh new file mode 100755 index 0000000000..c3cfadeb1b --- /dev/null +++ b/test/vboot/vboot_test.sh @@ -0,0 +1,126 @@ +#!/bin/sh +# +# Copyright (c) 2013, Google Inc. +# +# Simple Verified Boot Test Script +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA + +set -e + +# Run U-Boot and report the result +# Args: +# $1: Test message +run_uboot() { + echo -n "Test Verified Boot Run: $1: " + ${uboot} -d sandbox-u-boot.dtb >${tmp} -c ' +sb load host 0 100 test.fit; +fdt addr 100; +bootm 100; +reset' + if ! grep -q "$2" ${tmp}; then + echo + echo "Verified boot key check failed, output follows:" + cat ${tmp} + false + else + echo "OK" + fi +} + +echo "Simple Verified Boot Test" +echo "=========================" +echo +echo "Please see doc/uImage.FIT/verified-boot.txt for more information" +echo + +err=0 +tmp=/tmp/vboot_test.$$ + +dir=$(dirname $0) + +if [ -z ${O} ]; then + O=. +fi +O=$(readlink -f ${O}) + +dtc="-I dts -O dtb -p 2000" +uboot="${O}/u-boot" +mkimage="${O}/tools/mkimage" +keys="${dir}/dev-keys" +echo ${mkimage} -D "${dtc}" + +echo "Build keys" +mkdir -p ${keys} + +# Create an RSA key pair +openssl genrsa -F4 -out ${keys}/dev.key 2048 2>/dev/null + +# Create a certificate containing the public key +openssl req -batch -new -x509 -key ${keys}/dev.key -out ${keys}/dev.crt + +pushd ${dir} >/dev/null + +# Compile our device tree files for kernel and U-Boot (CONFIG_OF_CONTROL) +dtc -p 0x1000 sandbox-kernel.dts -O dtb -o sandbox-kernel.dtb +dtc -p 0x1000 sandbox-u-boot.dts -O dtb -o sandbox-u-boot.dtb + +# Create a number kernel image with zeroes +head -c 5000 /dev/zero >test-kernel.bin + +# Build the FIT, but don't sign anything yet +echo Build FIT with signed images +${mkimage} -D "${dtc}" -f sign-images.its test.fit >${tmp} + +run_uboot "unsigned signatures:" "dev-" + +# Sign images with our dev keys +echo Sign images +${mkimage} -D "${dtc}" -F -k dev-keys -K sandbox-u-boot.dtb -r test.fit >${tmp} + +run_uboot "signed images" "dev+" + + +# Create a fresh .dtb without the public keys +dtc -p 0x1000 sandbox-u-boot.dts -O dtb -o sandbox-u-boot.dtb + +echo Build FIT with signed configuration +${mkimage} -D "${dtc}" -f sign-configs.its test.fit >${tmp} + +run_uboot "unsigned config" "sha1+ OK" + +# Sign images with our dev keys +echo Sign images +${mkimage} -D "${dtc}" -F -k dev-keys -K sandbox-u-boot.dtb -r test.fit >${tmp} + +run_uboot "signed config" "dev+" + +# Increment the first byte of the signature, which should cause failure +sig=$(fdtget -t bx test.fit /configurations/conf@1/signature@1 value) +newbyte=$(printf %x $((0x${sig:0:2} + 1))) +sig="${newbyte} ${sig:2}" +fdtput -t bx test.fit /configurations/conf@1/signature@1 value ${sig} + +run_uboot "signed config with bad hash" "Bad Data Hash" + +popd >/dev/null + +echo +if ${ok}; then + echo "Test passed" +else + echo "Test failed" +fi diff --git a/tools/.gitignore b/tools/.gitignore index 9bce719478..a7fee26cdd 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -9,6 +9,7 @@ /mxsboot /ncb /ncp +/proftool /ubsha1 /xway-swap-bytes /*.exe diff --git a/tools/Makefile b/tools/Makefile index 4630f03dc5..cc912fb066 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -74,11 +74,13 @@ BIN_FILES-$(CONFIG_MX28) += mxsboot$(SFX) BIN_FILES-$(CONFIG_NETCONSOLE) += ncb$(SFX) BIN_FILES-$(CONFIG_SHA1_CHECK_UB_IMG) += ubsha1$(SFX) BIN_FILES-$(CONFIG_KIRKWOOD) += kwboot$(SFX) +BIN_FILES-y += proftool(SFX) # Source files which exist outside the tools directory EXT_OBJ_FILES-$(CONFIG_BUILD_ENVCRC) += common/env_embedded.o EXT_OBJ_FILES-y += common/image.o EXT_OBJ_FILES-$(CONFIG_FIT) += common/image-fit.o +EXT_OBJ_FILES-y += common/image-sig.o EXT_OBJ_FILES-y += lib/crc32.o EXT_OBJ_FILES-y += lib/md5.o EXT_OBJ_FILES-y += lib/sha1.o @@ -87,6 +89,7 @@ EXT_OBJ_FILES-y += lib/sha1.o OBJ_FILES-$(CONFIG_LCD_LOGO) += bmp_logo.o OBJ_FILES-$(CONFIG_VIDEO_LOGO) += bmp_logo.o NOPED_OBJ_FILES-y += default_image.o +NOPED_OBJ_FILES-y += proftool.o OBJ_FILES-$(CONFIG_BUILD_ENVCRC) += envcrc.o NOPED_OBJ_FILES-y += fit_image.o OBJ_FILES-$(CONFIG_CMD_NET) += gen_eth_addr.o @@ -122,6 +125,9 @@ LIBFDT_OBJ_FILES-y += fdt_rw.o LIBFDT_OBJ_FILES-y += fdt_strerror.o LIBFDT_OBJ_FILES-y += fdt_wip.o +# RSA objects +RSA_OBJ_FILES-y += rsa-sign.o + # Generated LCD/video logo LOGO_H = $(OBJTREE)/include/bmp_logo.h LOGO_DATA_H = $(OBJTREE)/include/bmp_logo_data.h @@ -149,8 +155,14 @@ endif # !LOGO_BMP HOSTSRCS += $(addprefix $(SRCTREE)/,$(EXT_OBJ_FILES-y:.o=.c)) HOSTSRCS += $(addprefix $(SRCTREE)/tools/,$(OBJ_FILES-y:.o=.c)) HOSTSRCS += $(addprefix $(SRCTREE)/lib/libfdt/,$(LIBFDT_OBJ_FILES-y:.o=.c)) +HOSTSRCS += $(addprefix $(SRCTREE)/lib/rsa/,$(RSA_OBJ_FILES-y:.o=.c)) BINS := $(addprefix $(obj),$(sort $(BIN_FILES-y))) LIBFDT_OBJS := $(addprefix $(obj),$(LIBFDT_OBJ_FILES-y)) +RSA_OBJS := $(addprefix $(obj),$(RSA_OBJ_FILES-y)) + +# We cannot check CONFIG_FIT_SIGNATURE here since it is not set on the host +FIT_SIG_OBJ_FILES := image-sig.o +FIT_SIG_OBJS := $(addprefix $(obj),$(FIT_SIG_OBJ_FILES)) HOSTOBJS := $(addprefix $(obj),$(OBJ_FILES-y)) NOPEDOBJS := $(addprefix $(obj),$(NOPED_OBJ_FILES-y)) @@ -180,6 +192,10 @@ $(obj)bmp_logo$(SFX): $(obj)bmp_logo.o $(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^ $(HOSTSTRIP) $@ +$(obj)proftool(SFX): $(obj)proftool.o + $(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^ + $(HOSTSTRIP) $@ + $(obj)envcrc$(SFX): $(obj)crc32.o $(obj)env_embedded.o $(obj)envcrc.o $(obj)sha1.o $(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^ @@ -207,6 +223,7 @@ $(obj)mkimage$(SFX): $(obj)aisimage.o \ $(obj)image-fit.o \ $(obj)image.o \ $(obj)image-host.o \ + $(FIT_SIG_OBJS) \ $(obj)imximage.o \ $(obj)kwbimage.o \ $(obj)pblimage.o \ @@ -216,8 +233,9 @@ $(obj)mkimage$(SFX): $(obj)aisimage.o \ $(obj)omapimage.o \ $(obj)sha1.o \ $(obj)ublimage.o \ - $(LIBFDT_OBJS) - $(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^ + $(LIBFDT_OBJS) \ + $(RSA_OBJS) + $(HOSTCC) $(HOSTCFLAGS) $(HOSTLDFLAGS) -o $@ $^ $(HOSTLIBS) $(HOSTSTRIP) $@ $(obj)mk$(BOARD)spl$(SFX): $(obj)mkexynosspl.o @@ -253,6 +271,9 @@ $(obj)%.o: $(SRCTREE)/lib/%.c $(obj)%.o: $(SRCTREE)/lib/libfdt/%.c $(HOSTCC) -g $(HOSTCFLAGS_NOPED) -c -o $@ $< +$(obj)%.o: $(SRCTREE)/lib/rsa/%.c + $(HOSTCC) -g $(HOSTCFLAGS_NOPED) -c -o $@ $< + subdirs: ifeq ($(TOOLSUBDIRS),) @: diff --git a/tools/fit_image.c b/tools/fit_image.c index cc123dd37a..281c2bda13 100644 --- a/tools/fit_image.c +++ b/tools/fit_image.c @@ -105,9 +105,11 @@ static int fit_handle_file (struct mkimage_params *params) { char tmpfile[MKIMAGE_MAX_TMPFILE_LEN]; char cmd[MKIMAGE_MAX_DTC_CMDLINE_LEN]; - int tfd; + int tfd, destfd = 0; + void *dest_blob = NULL; struct stat sbuf; void *ptr; + off_t destfd_size = 0; /* Flattened Image Tree (FIT) format handling */ debug ("FIT format handling\n"); @@ -122,29 +124,44 @@ static int fit_handle_file (struct mkimage_params *params) } sprintf (tmpfile, "%s%s", params->imagefile, MKIMAGE_TMPFILE_SUFFIX); - /* dtc -I dts -O dtb -p 500 datafile > tmpfile */ - sprintf (cmd, "%s %s %s > %s", - MKIMAGE_DTC, params->dtc, params->datafile, tmpfile); - debug ("Trying to execute \"%s\"\n", cmd); + /* We either compile the source file, or use the existing FIT image */ + if (params->datafile) { + /* dtc -I dts -O dtb -p 500 datafile > tmpfile */ + snprintf(cmd, sizeof(cmd), "%s %s %s > %s", + MKIMAGE_DTC, params->dtc, params->datafile, tmpfile); + debug("Trying to execute \"%s\"\n", cmd); + } else { + snprintf(cmd, sizeof(cmd), "cp %s %s", + params->imagefile, tmpfile); + } if (system (cmd) == -1) { fprintf (stderr, "%s: system(%s) failed: %s\n", params->cmdname, cmd, strerror(errno)); goto err_system; } + if (params->keydest) { + destfd = mmap_fdt(params, params->keydest, &dest_blob, &sbuf); + if (destfd < 0) + goto err_keydest; + destfd_size = sbuf.st_size; + } + tfd = mmap_fdt(params, tmpfile, &ptr, &sbuf); if (tfd < 0) goto err_mmap; /* set hashes for images in the blob */ - if (fit_add_verification_data(ptr)) { - fprintf (stderr, "%s Can't add hashes to FIT blob", - params->cmdname); + if (fit_add_verification_data(params->keydir, + dest_blob, ptr, params->comment, + params->require_keys)) { + fprintf(stderr, "%s Can't add hashes to FIT blob\n", + params->cmdname); goto err_add_hashes; } - /* add a timestamp at offset 0 i.e., root */ - if (fit_set_timestamp (ptr, 0, sbuf.st_mtime)) { + /* for first image creation, add a timestamp at offset 0 i.e., root */ + if (params->datafile && fit_set_timestamp(ptr, 0, sbuf.st_mtime)) { fprintf (stderr, "%s: Can't add image timestamp\n", params->cmdname); goto err_add_timestamp; @@ -153,6 +170,10 @@ static int fit_handle_file (struct mkimage_params *params) munmap ((void *)ptr, sbuf.st_size); close (tfd); + if (dest_blob) { + munmap(dest_blob, destfd_size); + close(destfd); + } if (rename (tmpfile, params->imagefile) == -1) { fprintf (stderr, "%s: Can't rename %s to %s: %s\n", @@ -168,6 +189,9 @@ err_add_timestamp: err_add_hashes: munmap(ptr, sbuf.st_size); err_mmap: + if (dest_blob) + munmap(dest_blob, destfd_size); +err_keydest: err_system: unlink(tmpfile); return -1; diff --git a/tools/image-host.c b/tools/image-host.c index d944d0ff4e..932384beab 100644 --- a/tools/image-host.c +++ b/tools/image-host.c @@ -26,12 +26,8 @@ */ #include "mkimage.h" -#include <bootstage.h> #include <image.h> -#include <sha1.h> -#include <time.h> -#include <u-boot/crc.h> -#include <u-boot/md5.h> +#include <version.h> /** * fit_set_hash_value - set hash value in requested has node @@ -108,9 +104,165 @@ static int fit_image_process_hash(void *fit, const char *image_name, } /** - * fit_image_add_verification_data() - calculate/set hash data for image node + * fit_image_write_sig() - write the signature to a FIT * - * This adds hash values for a component image node. + * This writes the signature and signer data to the FIT. + * + * @fit: pointer to the FIT format image header + * @noffset: hash node offset + * @value: signature value to be set + * @value_len: signature value length + * @comment: Text comment to write (NULL for none) + * + * returns + * 0, on success + * -FDT_ERR_..., on failure + */ +static int fit_image_write_sig(void *fit, int noffset, uint8_t *value, + int value_len, const char *comment, const char *region_prop, + int region_proplen) +{ + int string_size; + int ret; + + /* + * Get the current string size, before we update the FIT and add + * more + */ + string_size = fdt_size_dt_strings(fit); + + ret = fdt_setprop(fit, noffset, FIT_VALUE_PROP, value, value_len); + if (!ret) { + ret = fdt_setprop_string(fit, noffset, "signer-name", + "mkimage"); + } + if (!ret) { + ret = fdt_setprop_string(fit, noffset, "signer-version", + PLAIN_VERSION); + } + if (comment && !ret) + ret = fdt_setprop_string(fit, noffset, "comment", comment); + if (!ret) + ret = fit_set_timestamp(fit, noffset, time(NULL)); + if (region_prop && !ret) { + uint32_t strdata[2]; + + ret = fdt_setprop(fit, noffset, "hashed-nodes", + region_prop, region_proplen); + strdata[0] = 0; + strdata[1] = cpu_to_fdt32(string_size); + if (!ret) { + ret = fdt_setprop(fit, noffset, "hashed-strings", + strdata, sizeof(strdata)); + } + } + + return ret; +} + +static int fit_image_setup_sig(struct image_sign_info *info, + const char *keydir, void *fit, const char *image_name, + int noffset, const char *require_keys) +{ + const char *node_name; + char *algo_name; + + node_name = fit_get_name(fit, noffset, NULL); + if (fit_image_hash_get_algo(fit, noffset, &algo_name)) { + printf("Can't get algo property for '%s' signature node in '%s' image node\n", + node_name, image_name); + return -1; + } + + memset(info, '\0', sizeof(*info)); + info->keydir = keydir; + info->keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); + info->fit = fit; + info->node_offset = noffset; + info->algo = image_get_sig_algo(algo_name); + info->require_keys = require_keys; + if (!info->algo) { + printf("Unsupported signature algorithm (%s) for '%s' signature node in '%s' image node\n", + algo_name, node_name, image_name); + return -1; + } + + return 0; +} + +/** + * fit_image_process_sig- Process a single subnode of the images/ node + * + * Check each subnode and process accordingly. For signature nodes we + * generate a signed hash of the supplised data and store it in the node. + * + * @keydir: Directory containing keys to use for signing + * @keydest: Destination FDT blob to write public keys into + * @fit: pointer to the FIT format image header + * @image_name: name of image being processes (used to display errors) + * @noffset: subnode offset + * @data: data to process + * @size: size of data in bytes + * @comment: Comment to add to signature nodes + * @require_keys: Mark all keys as 'required' + * @return 0 if ok, -1 on error + */ +static int fit_image_process_sig(const char *keydir, void *keydest, + void *fit, const char *image_name, + int noffset, const void *data, size_t size, + const char *comment, int require_keys) +{ + struct image_sign_info info; + struct image_region region; + const char *node_name; + uint8_t *value; + uint value_len; + int ret; + + if (fit_image_setup_sig(&info, keydir, fit, image_name, noffset, + require_keys ? "image" : NULL)) + return -1; + + node_name = fit_get_name(fit, noffset, NULL); + region.data = data; + region.size = size; + ret = info.algo->sign(&info, ®ion, 1, &value, &value_len); + if (ret) { + printf("Failed to sign '%s' signature node in '%s' image node: %d\n", + node_name, image_name, ret); + + /* We allow keys to be missing */ + if (ret == -ENOENT) + return 0; + return -1; + } + + ret = fit_image_write_sig(fit, noffset, value, value_len, comment, + NULL, 0); + if (ret) { + printf("Can't write signature for '%s' signature node in '%s' image node: %s\n", + node_name, image_name, fdt_strerror(ret)); + return -1; + } + free(value); + + /* Get keyname again, as FDT has changed and invalidated our pointer */ + info.keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); + + /* Write the public key into the supplied FDT file */ + if (keydest && info.algo->add_verify_data(&info, keydest)) { + printf("Failed to add verification data for '%s' signature node in '%s' image node\n", + node_name, image_name); + return -1; + } + + return 0; +} + +/** + * fit_image_add_verification_data() - calculate/set verig. data for image node + * + * This adds hash and signature values for an component image node. * * All existing hash subnodes are checked, if algorithm property is set to * one of the supported hash algorithms, hash value is computed and @@ -133,11 +285,17 @@ static int fit_image_process_hash(void *fit, const char *image_name, * * For signature details, please see doc/uImage.FIT/signature.txt * + * @keydir Directory containing *.key and *.crt files (or NULL) + * @keydest FDT Blob to write public keys into (NULL if none) * @fit: Pointer to the FIT format image header * @image_noffset: Requested component image node + * @comment: Comment to add to signature nodes + * @require_keys: Mark all keys as 'required' * @return: 0 on success, <0 on failure */ -int fit_image_add_verification_data(void *fit, int image_noffset) +int fit_image_add_verification_data(const char *keydir, void *keydest, + void *fit, int image_noffset, const char *comment, + int require_keys) { const char *image_name; const void *data; @@ -169,6 +327,12 @@ int fit_image_add_verification_data(void *fit, int image_noffset) strlen(FIT_HASH_NODENAME))) { ret = fit_image_process_hash(fit, image_name, noffset, data, size); + } else if (IMAGE_ENABLE_SIGN && keydir && + !strncmp(node_name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + ret = fit_image_process_sig(keydir, keydest, + fit, image_name, noffset, data, size, + comment, require_keys); } if (ret) return -1; @@ -177,9 +341,326 @@ int fit_image_add_verification_data(void *fit, int image_noffset) return 0; } -int fit_add_verification_data(void *fit) +struct strlist { + int count; + char **strings; +}; + +static void strlist_init(struct strlist *list) +{ + memset(list, '\0', sizeof(*list)); +} + +static void strlist_free(struct strlist *list) +{ + int i; + + for (i = 0; i < list->count; i++) + free(list->strings[i]); + free(list->strings); +} + +static int strlist_add(struct strlist *list, const char *str) +{ + char *dup; + + dup = strdup(str); + list->strings = realloc(list->strings, + (list->count + 1) * sizeof(char *)); + if (!list || !str) + return -1; + list->strings[list->count++] = dup; + + return 0; +} + +static const char *fit_config_get_image_list(void *fit, int noffset, + int *lenp, int *allow_missingp) +{ + static const char default_list[] = FIT_KERNEL_PROP "\0" + FIT_FDT_PROP; + const char *prop; + + /* If there is an "image" property, use that */ + prop = fdt_getprop(fit, noffset, "sign-images", lenp); + if (prop) { + *allow_missingp = 0; + return *lenp ? prop : NULL; + } + + /* Default image list */ + *allow_missingp = 1; + *lenp = sizeof(default_list); + + return default_list; +} + +static int fit_config_get_hash_list(void *fit, int conf_noffset, + int sig_offset, struct strlist *node_inc) +{ + int allow_missing; + const char *prop, *iname, *end; + const char *conf_name, *sig_name; + char name[200], path[200]; + int image_count; + int ret, len; + + conf_name = fit_get_name(fit, conf_noffset, NULL); + sig_name = fit_get_name(fit, sig_offset, NULL); + + /* + * Build a list of nodes we need to hash. We always need the root + * node and the configuration. + */ + strlist_init(node_inc); + snprintf(name, sizeof(name), "%s/%s", FIT_CONFS_PATH, conf_name); + if (strlist_add(node_inc, "/") || + strlist_add(node_inc, name)) + goto err_mem; + + /* Get a list of images that we intend to sign */ + prop = fit_config_get_image_list(fit, conf_noffset, &len, + &allow_missing); + if (!prop) + return 0; + + /* Locate the images */ + end = prop + len; + image_count = 0; + for (iname = prop; iname < end; iname += strlen(iname) + 1) { + int noffset; + int image_noffset; + int hash_count; + + image_noffset = fit_conf_get_prop_node(fit, conf_noffset, + iname); + if (image_noffset < 0) { + printf("Failed to find image '%s' in configuration '%s/%s'\n", + iname, conf_name, sig_name); + if (allow_missing) + continue; + + return -ENOENT; + } + + ret = fdt_get_path(fit, image_noffset, path, sizeof(path)); + if (ret < 0) + goto err_path; + if (strlist_add(node_inc, path)) + goto err_mem; + + snprintf(name, sizeof(name), "%s/%s", FIT_CONFS_PATH, + conf_name); + + /* Add all this image's hashes */ + hash_count = 0; + for (noffset = fdt_first_subnode(fit, image_noffset); + noffset >= 0; + noffset = fdt_next_subnode(fit, noffset)) { + const char *name = fit_get_name(fit, noffset, NULL); + + if (strncmp(name, FIT_HASH_NODENAME, + strlen(FIT_HASH_NODENAME))) + continue; + ret = fdt_get_path(fit, noffset, path, sizeof(path)); + if (ret < 0) + goto err_path; + if (strlist_add(node_inc, path)) + goto err_mem; + hash_count++; + } + + if (!hash_count) { + printf("Failed to find any hash nodes in configuration '%s/%s' image '%s' - without these it is not possible to verify this image\n", + conf_name, sig_name, iname); + return -ENOMSG; + } + + image_count++; + } + + if (!image_count) { + printf("Failed to find any images for configuration '%s/%s'\n", + conf_name, sig_name); + return -ENOMSG; + } + + return 0; + +err_mem: + printf("Out of memory processing configuration '%s/%s'\n", conf_name, + sig_name); + return -ENOMEM; + +err_path: + printf("Failed to get path for image '%s' in configuration '%s/%s': %s\n", + iname, conf_name, sig_name, fdt_strerror(ret)); + return -ENOENT; +} + +static int fit_config_get_data(void *fit, int conf_noffset, int noffset, + struct image_region **regionp, int *region_countp, + char **region_propp, int *region_proplen) { - int images_noffset; + char * const exc_prop[] = {"data"}; + struct strlist node_inc; + struct image_region *region; + struct fdt_region fdt_regions[100]; + const char *conf_name, *sig_name; + char path[200]; + int count, i; + char *region_prop; + int ret, len; + + conf_name = fit_get_name(fit, conf_noffset, NULL); + sig_name = fit_get_name(fit, conf_noffset, NULL); + debug("%s: conf='%s', sig='%s'\n", __func__, conf_name, sig_name); + + /* Get a list of nodes we want to hash */ + ret = fit_config_get_hash_list(fit, conf_noffset, noffset, &node_inc); + if (ret) + return ret; + + /* Get a list of regions to hash */ + count = fdt_find_regions(fit, node_inc.strings, node_inc.count, + exc_prop, ARRAY_SIZE(exc_prop), + fdt_regions, ARRAY_SIZE(fdt_regions), + path, sizeof(path), 1); + if (count < 0) { + printf("Failed to hash configuration '%s/%s': %s\n", conf_name, + sig_name, fdt_strerror(ret)); + return -EIO; + } + if (count == 0) { + printf("No data to hash for configuration '%s/%s': %s\n", + conf_name, sig_name, fdt_strerror(ret)); + return -EINVAL; + } + + /* Build our list of data blocks */ + region = fit_region_make_list(fit, fdt_regions, count, NULL); + if (!region) { + printf("Out of memory hashing configuration '%s/%s'\n", + conf_name, sig_name); + return -ENOMEM; + } + + /* Create a list of all hashed properties */ + debug("Hash nodes:\n"); + for (i = len = 0; i < node_inc.count; i++) { + debug(" %s\n", node_inc.strings[i]); + len += strlen(node_inc.strings[i]) + 1; + } + region_prop = malloc(len); + if (!region_prop) { + printf("Out of memory setting up regions for configuration '%s/%s'\n", + conf_name, sig_name); + return -ENOMEM; + } + for (i = len = 0; i < node_inc.count; + len += strlen(node_inc.strings[i]) + 1, i++) + strcpy(region_prop + len, node_inc.strings[i]); + strlist_free(&node_inc); + + *region_countp = count; + *regionp = region; + *region_propp = region_prop; + *region_proplen = len; + + return 0; +} + +static int fit_config_process_sig(const char *keydir, void *keydest, + void *fit, const char *conf_name, int conf_noffset, + int noffset, const char *comment, int require_keys) +{ + struct image_sign_info info; + const char *node_name; + struct image_region *region; + char *region_prop; + int region_proplen; + int region_count; + uint8_t *value; + uint value_len; + int ret; + + node_name = fit_get_name(fit, noffset, NULL); + if (fit_config_get_data(fit, conf_noffset, noffset, ®ion, + ®ion_count, ®ion_prop, ®ion_proplen)) + return -1; + + if (fit_image_setup_sig(&info, keydir, fit, conf_name, noffset, + require_keys ? "conf" : NULL)) + return -1; + + ret = info.algo->sign(&info, region, region_count, &value, &value_len); + free(region); + if (ret) { + printf("Failed to sign '%s' signature node in '%s' conf node\n", + node_name, conf_name); + + /* We allow keys to be missing */ + if (ret == -ENOENT) + return 0; + return -1; + } + + if (fit_image_write_sig(fit, noffset, value, value_len, comment, + region_prop, region_proplen)) { + printf("Can't write signature for '%s' signature node in '%s' conf node\n", + node_name, conf_name); + return -1; + } + free(value); + free(region_prop); + + /* Get keyname again, as FDT has changed and invalidated our pointer */ + info.keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); + + /* Write the public key into the supplied FDT file */ + if (keydest && info.algo->add_verify_data(&info, keydest)) { + printf("Failed to add verification data for '%s' signature node in '%s' image node\n", + node_name, conf_name); + return -1; + } + + return 0; +} + +static int fit_config_add_verification_data(const char *keydir, void *keydest, + void *fit, int conf_noffset, const char *comment, + int require_keys) +{ + const char *conf_name; + int noffset; + + conf_name = fit_get_name(fit, conf_noffset, NULL); + + /* Process all hash subnodes of the configuration node */ + for (noffset = fdt_first_subnode(fit, conf_noffset); + noffset >= 0; + noffset = fdt_next_subnode(fit, noffset)) { + const char *node_name; + int ret = 0; + + node_name = fit_get_name(fit, noffset, NULL); + if (!strncmp(node_name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + ret = fit_config_process_sig(keydir, keydest, + fit, conf_name, conf_noffset, noffset, comment, + require_keys); + } + if (ret) + return ret; + } + + return 0; +} + +int fit_add_verification_data(const char *keydir, void *keydest, void *fit, + const char *comment, int require_keys) +{ + int images_noffset, confs_noffset; int noffset; int ret; @@ -199,7 +680,31 @@ int fit_add_verification_data(void *fit) * Direct child node of the images parent node, * i.e. component image node. */ - ret = fit_image_add_verification_data(fit, noffset); + ret = fit_image_add_verification_data(keydir, keydest, + fit, noffset, comment, require_keys); + if (ret) + return ret; + } + + /* If there are no keys, we can't sign configurations */ + if (!IMAGE_ENABLE_SIGN || !keydir) + return 0; + + /* Find configurations parent node offset */ + confs_noffset = fdt_path_offset(fit, FIT_CONFS_PATH); + if (confs_noffset < 0) { + printf("Can't find images parent node '%s' (%s)\n", + FIT_IMAGES_PATH, fdt_strerror(confs_noffset)); + return -ENOENT; + } + + /* Process its subnodes, print out component images details */ + for (noffset = fdt_first_subnode(fit, confs_noffset); + noffset >= 0; + noffset = fdt_next_subnode(fit, noffset)) { + ret = fit_config_add_verification_data(keydir, keydest, + fit, noffset, comment, + require_keys); if (ret) return ret; } diff --git a/tools/mkimage.c b/tools/mkimage.c index e43b09f766..d312844e9c 100644 --- a/tools/mkimage.c +++ b/tools/mkimage.c @@ -183,6 +183,11 @@ main (int argc, char **argv) genimg_get_arch_id (*++argv)) < 0) usage (); goto NXTARG; + case 'c': + if (--argc <= 0) + usage(); + params.comment = *++argv; + goto NXTARG; case 'C': if ((--argc <= 0) || (params.comp = @@ -240,19 +245,34 @@ main (int argc, char **argv) case 'f': if (--argc <= 0) usage (); + params.datafile = *++argv; + /* no break */ + case 'F': /* * The flattened image tree (FIT) format * requires a flattened device tree image type */ params.type = IH_TYPE_FLATDT; - params.datafile = *++argv; params.fflag = 1; goto NXTARG; + case 'k': + if (--argc <= 0) + usage(); + params.keydir = *++argv; + goto NXTARG; + case 'K': + if (--argc <= 0) + usage(); + params.keydest = *++argv; + goto NXTARG; case 'n': if (--argc <= 0) usage (); params.imagename = *++argv; goto NXTARG; + case 'r': + params.require_keys = 1; + break; case 'R': if (--argc <= 0) usage(); @@ -623,8 +643,20 @@ usage () " -d ==> use image data from 'datafile'\n" " -x ==> set XIP (execute in place)\n", params.cmdname); - fprintf (stderr, " %s [-D dtc_options] -f fit-image.its fit-image\n", + fprintf(stderr, " %s [-D dtc_options] [-f fit-image.its|-F] fit-image\n", params.cmdname); + fprintf(stderr, " -D => set options for device tree compiler\n" + " -f => input filename for FIT source\n"); +#ifdef CONFIG_FIT_SIGNATURE + fprintf(stderr, "Signing / verified boot options: [-k keydir] [-K dtb] [ -c <comment>] [-r]\n" + " -k => set directory containing private keys\n" + " -K => write public keys to this .dtb file\n" + " -c => add comment in signature node\n" + " -F => re-sign existing FIT image\n" + " -r => mark keys used as 'required' in dtb\n"); +#else + fprintf(stderr, "Signing / verified boot not supported (CONFIG_FIT_SIGNATURE undefined)\n"); +#endif fprintf (stderr, " %s -V ==> print version information and exit\n", params.cmdname); diff --git a/tools/mkimage.h b/tools/mkimage.h index 03c6c8f523..1d9984e1a3 100644 --- a/tools/mkimage.h +++ b/tools/mkimage.h @@ -87,6 +87,10 @@ struct mkimage_params { char *datafile; char *imagefile; char *cmdname; + const char *keydir; /* Directory holding private keys */ + const char *keydest; /* Destination .dtb for public key */ + const char *comment; /* Comment to add to signature node */ + int require_keys; /* 1 to mark signing keys as 'required' */ }; /* diff --git a/tools/proftool.c b/tools/proftool.c new file mode 100644 index 0000000000..a48ed286a5 --- /dev/null +++ b/tools/proftool.c @@ -0,0 +1,611 @@ +/* + * Copyright (c) 2013 Google, Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +/* Decode and dump U-Boot profiling information */ + +#include <assert.h> +#include <ctype.h> +#include <limits.h> +#include <regex.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/param.h> + +#include <compiler.h> +#include <trace.h> + +#define MAX_LINE_LEN 500 + +enum { + FUNCF_TRACE = 1 << 0, /* Include this function in trace */ +}; + +struct func_info { + unsigned long offset; + const char *name; + unsigned long code_size; + unsigned long call_count; + unsigned flags; + /* the section this function is in */ + struct objsection_info *objsection; +}; + +enum trace_line_type { + TRACE_LINE_INCLUDE, + TRACE_LINE_EXCLUDE, +}; + +struct trace_configline_info { + struct trace_configline_info *next; + enum trace_line_type type; + const char *name; /* identifier name / wildcard */ + regex_t regex; /* Regex to use if name starts with / */ +}; + +/* The contents of the trace config file */ +struct trace_configline_info *trace_config_head; + +struct func_info *func_list; +int func_count; +struct trace_call *call_list; +int call_count; +int verbose; /* Verbosity level 0=none, 1=warn, 2=notice, 3=info, 4=debug */ +unsigned long text_offset; /* text address of first function */ + +static void outf(int level, const char *fmt, ...) + __attribute__ ((format (__printf__, 2, 3))); +#define error(fmt, b...) outf(0, fmt, ##b) +#define warn(fmt, b...) outf(1, fmt, ##b) +#define notice(fmt, b...) outf(2, fmt, ##b) +#define info(fmt, b...) outf(3, fmt, ##b) +#define debug(fmt, b...) outf(4, fmt, ##b) + + +static void outf(int level, const char *fmt, ...) +{ + if (verbose >= level) { + va_list args; + + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + } +} + +static void usage(void) +{ + fprintf(stderr, + "Usage: proftool -cds -v3 <cmd> <profdata>\n" + "\n" + "Commands\n" + " dump-ftrace\t\tDump out textual data in ftrace format\n" + "\n" + "Options:\n" + " -m <map>\tSpecify Systen.map file\n" + " -t <trace>\tSpecific trace data file (from U-Boot)\n" + " -v <0-4>\tSpecify verbosity\n"); + exit(EXIT_FAILURE); +} + +static int h_cmp_offset(const void *v1, const void *v2) +{ + const struct func_info *f1 = v1, *f2 = v2; + + return (f1->offset / FUNC_SITE_SIZE) - (f2->offset / FUNC_SITE_SIZE); +} + +static int read_system_map(FILE *fin) +{ + unsigned long offset, start = 0; + struct func_info *func; + char buff[MAX_LINE_LEN]; + char symtype; + char symname[MAX_LINE_LEN + 1]; + int linenum; + int alloced; + + for (linenum = 1, alloced = func_count = 0;; linenum++) { + int fields = 0; + + if (fgets(buff, sizeof(buff), fin)) + fields = sscanf(buff, "%lx %c %100s\n", &offset, + &symtype, symname); + if (fields == 2) { + continue; + } else if (feof(fin)) { + break; + } else if (fields < 2) { + error("Map file line %d: invalid format\n", linenum); + return 1; + } + + /* Must be a text symbol */ + symtype = tolower(symtype); + if (symtype != 't' && symtype != 'w') + continue; + + if (func_count == alloced) { + alloced += 256; + func_list = realloc(func_list, + sizeof(struct func_info) * alloced); + assert(func_list); + } + if (!func_count) + start = offset; + + func = &func_list[func_count++]; + memset(func, '\0', sizeof(*func)); + func->offset = offset - start; + func->name = strdup(symname); + func->flags = FUNCF_TRACE; /* trace by default */ + + /* Update previous function's code size */ + if (func_count > 1) + func[-1].code_size = func->offset - func[-1].offset; + } + notice("%d functions found in map file\n", func_count); + text_offset = start; + return 0; +} + +static int read_data(FILE *fin, void *buff, int size) +{ + int err; + + err = fread(buff, 1, size, fin); + if (!err) + return 1; + if (err != size) { + error("Cannot read profile file at pos %ld\n", ftell(fin)); + return -1; + } + return 0; +} + +static struct func_info *find_func_by_offset(uint32_t offset) +{ + struct func_info key, *found; + + key.offset = offset; + found = bsearch(&key, func_list, func_count, sizeof(struct func_info), + h_cmp_offset); + + return found; +} + +/* This finds the function which contains the given offset */ +static struct func_info *find_caller_by_offset(uint32_t offset) +{ + int low; /* least function that could be a match */ + int high; /* greated function that could be a match */ + struct func_info key; + + low = 0; + high = func_count - 1; + key.offset = offset; + while (high > low + 1) { + int mid = (low + high) / 2; + int result; + + result = h_cmp_offset(&key, &func_list[mid]); + if (result > 0) + low = mid; + else if (result < 0) + high = mid; + else + return &func_list[mid]; + } + + return low >= 0 ? &func_list[low] : NULL; +} + +static int read_calls(FILE *fin, int count) +{ + struct trace_call *call_data; + int i; + + notice("call count: %d\n", count); + call_list = (struct trace_call *)calloc(count, sizeof(*call_data)); + if (!call_list) { + error("Cannot allocate call_list\n"); + return -1; + } + call_count = count; + + call_data = call_list; + for (i = 0; i < count; i++, call_data++) { + if (read_data(fin, call_data, sizeof(*call_data))) + return 1; + } + return 0; +} + +static int read_profile(FILE *fin, int *not_found) +{ + struct trace_output_hdr hdr; + + *not_found = 0; + while (!feof(fin)) { + int err; + + err = read_data(fin, &hdr, sizeof(hdr)); + if (err == 1) + break; /* EOF */ + else if (err) + return 1; + + switch (hdr.type) { + case TRACE_CHUNK_FUNCS: + /* Ignored at present */ + break; + + case TRACE_CHUNK_CALLS: + if (read_calls(fin, hdr.rec_count)) + return 1; + break; + } + } + return 0; +} + +static int read_map_file(const char *fname) +{ + FILE *fmap; + int err = 0; + + fmap = fopen(fname, "r"); + if (!fmap) { + error("Cannot open map file '%s'\n", fname); + return 1; + } + if (fmap) { + err = read_system_map(fmap); + fclose(fmap); + } + return err; +} + +static int read_profile_file(const char *fname) +{ + int not_found = INT_MAX; + FILE *fprof; + int err; + + fprof = fopen(fname, "rb"); + if (!fprof) { + error("Cannot open profile data file '%s'\n", + fname); + return 1; + } else { + err = read_profile(fprof, ¬_found); + fclose(fprof); + if (err) + return err; + + if (not_found) { + warn("%d profile functions could not be found in the map file - are you sure that your profile data and map file correspond?\n", + not_found); + return 1; + } + } + return 0; +} + +static int regex_report_error(regex_t *regex, int err, const char *op, + const char *name) +{ + char buf[200]; + + regerror(err, regex, buf, sizeof(buf)); + error("Regex error '%s' in %s '%s'\n", buf, op, name); + return -1; +} + +static void check_trace_config_line(struct trace_configline_info *item) +{ + struct func_info *func, *end; + int err; + + debug("Checking trace config line '%s'\n", item->name); + for (func = func_list, end = func + func_count; func < end; func++) { + err = regexec(&item->regex, func->name, 0, NULL, 0); + debug(" - regex '%s', string '%s': %d\n", item->name, + func->name, err); + if (err == REG_NOMATCH) + continue; + + if (err != REG_NOERROR) { + regex_report_error(&item->regex, err, "match", + item->name); + break; + } + + /* It matches, so perform the action */ + switch (item->type) { + case TRACE_LINE_INCLUDE: + info(" include %s at %lx\n", func->name, + text_offset + func->offset); + func->flags |= FUNCF_TRACE; + break; + + case TRACE_LINE_EXCLUDE: + info(" exclude %s at %lx\n", func->name, + text_offset + func->offset); + func->flags &= ~FUNCF_TRACE; + break; + } + } +} + +static void check_trace_config(void) +{ + struct trace_configline_info *line; + + for (line = trace_config_head; line; line = line->next) + check_trace_config_line(line); +} + +/** + * Check the functions to see if they each have an objsection. If not, then + * the linker must have eliminated them. + */ +static void check_functions(void) +{ + struct func_info *func, *end; + unsigned long removed_code_size = 0; + int not_found = 0; + + /* Look for missing functions */ + for (func = func_list, end = func + func_count; func < end; func++) { + if (!func->objsection) { + removed_code_size += func->code_size; + not_found++; + } + } + + /* Figure out what functions we want to trace */ + check_trace_config(); + + warn("%d functions removed by linker, %ld code size\n", + not_found, removed_code_size); +} + +static int read_trace_config(FILE *fin) +{ + char buff[200]; + int linenum = 0; + struct trace_configline_info **tailp = &trace_config_head; + + while (fgets(buff, sizeof(buff), fin)) { + int len = strlen(buff); + struct trace_configline_info *line; + char *saveptr; + char *s, *tok; + int err; + + linenum++; + if (len && buff[len - 1] == '\n') + buff[len - 1] = '\0'; + + /* skip blank lines and comments */ + for (s = buff; *s == ' ' || *s == '\t'; s++) + ; + if (!*s || *s == '#') + continue; + + line = (struct trace_configline_info *)calloc(1, + sizeof(*line)); + if (!line) { + error("Cannot allocate config line\n"); + return -1; + } + + tok = strtok_r(s, " \t", &saveptr); + if (!tok) { + error("Invalid trace config data on line %d\n", + linenum); + return -1; + } + if (0 == strcmp(tok, "include-func")) { + line->type = TRACE_LINE_INCLUDE; + } else if (0 == strcmp(tok, "exclude-func")) { + line->type = TRACE_LINE_EXCLUDE; + } else { + error("Unknown command in trace config data line %d\n", + linenum); + return -1; + } + + tok = strtok_r(NULL, " \t", &saveptr); + if (!tok) { + error("Missing pattern in trace config data line %d\n", + linenum); + return -1; + } + + err = regcomp(&line->regex, tok, REG_NOSUB); + if (err) { + free(line); + return regex_report_error(&line->regex, err, "compile", + tok); + } + + /* link this new one to the end of the list */ + line->name = strdup(tok); + line->next = NULL; + *tailp = line; + tailp = &line->next; + } + + if (!feof(fin)) { + error("Cannot read from trace config file at position %ld\n", + ftell(fin)); + return -1; + } + return 0; +} + +static int read_trace_config_file(const char *fname) +{ + FILE *fin; + int err; + + fin = fopen(fname, "r"); + if (!fin) { + error("Cannot open trace_config file '%s'\n", fname); + return -1; + } + err = read_trace_config(fin); + fclose(fin); + return err; +} + +static void out_func(ulong func_offset, int is_caller, const char *suffix) +{ + struct func_info *func; + + func = (is_caller ? find_caller_by_offset : find_func_by_offset) + (func_offset); + + if (func) + printf("%s%s", func->name, suffix); + else + printf("%lx%s", func_offset, suffix); +} + +/* + * # tracer: function + * # + * # TASK-PID CPU# TIMESTAMP FUNCTION + * # | | | | | + * # bash-4251 [01] 10152.583854: path_put <-path_walk + * # bash-4251 [01] 10152.583855: dput <-path_put + * # bash-4251 [01] 10152.583855: _atomic_dec_and_lock <-dput + */ +static int make_ftrace(void) +{ + struct trace_call *call; + int missing_count = 0, skip_count = 0; + int i; + + printf("# tracer: ftrace\n" + "#\n" + "# TASK-PID CPU# TIMESTAMP FUNCTION\n" + "# | | | | |\n"); + for (i = 0, call = call_list; i < call_count; i++, call++) { + struct func_info *func = find_func_by_offset(call->func); + ulong time = call->flags & FUNCF_TIMESTAMP_MASK; + + if (TRACE_CALL_TYPE(call) != FUNCF_ENTRY && + TRACE_CALL_TYPE(call) != FUNCF_EXIT) + continue; + if (!func) { + warn("Cannot find function at %lx\n", + text_offset + call->func); + missing_count++; + continue; + } + + if (!(func->flags & FUNCF_TRACE)) { + debug("Funcion '%s' is excluded from trace\n", + func->name); + skip_count++; + continue; + } + + printf("%16s-%-5d [01] %lu.%06lu: ", "uboot", 1, + time / 1000000, time % 1000000); + + out_func(call->func, 0, " <- "); + out_func(call->caller, 1, "\n"); + } + info("ftrace: %d functions not found, %d excluded\n", missing_count, + skip_count); + + return 0; +} + +static int prof_tool(int argc, char * const argv[], + const char *prof_fname, const char *map_fname, + const char *trace_config_fname) +{ + int err = 0; + + if (read_map_file(map_fname)) + return -1; + if (prof_fname && read_profile_file(prof_fname)) + return -1; + if (trace_config_fname && read_trace_config_file(trace_config_fname)) + return -1; + + check_functions(); + + for (; argc; argc--, argv++) { + const char *cmd = *argv; + + if (0 == strcmp(cmd, "dump-ftrace")) + err = make_ftrace(); + else + warn("Unknown command '%s'\n", cmd); + } + + return err; +} + +int main(int argc, char *argv[]) +{ + const char *map_fname = "System.map"; + const char *prof_fname = NULL; + const char *trace_config_fname = NULL; + int opt; + + verbose = 2; + while ((opt = getopt(argc, argv, "m:p:t:v:")) != -1) { + switch (opt) { + case 'm': + map_fname = optarg; + break; + + case 'p': + prof_fname = optarg; + break; + + case 't': + trace_config_fname = optarg; + break; + + case 'v': + verbose = atoi(optarg); + break; + + default: + usage(); + } + } + argc -= optind; argv += optind; + if (argc < 1) + usage(); + + debug("Debug enabled\n"); + return prof_tool(argc, argv, prof_fname, map_fname, + trace_config_fname); +} |