From ee7c0fc7643c73c65f0054584f6c681baac62c80 Mon Sep 17 00:00:00 2001 From: Lars Rasmusson Date: Tue, 11 Dec 2012 11:11:52 +0100 Subject: Correct comment to show the parameters as defined in tools/mkimage.h Signed-off-by: Lars Rasmusson diff --git a/tools/fit_image.c b/tools/fit_image.c index ef9ffee..76bbba1 100644 --- a/tools/fit_image.c +++ b/tools/fit_image.c @@ -80,7 +80,7 @@ static int fit_handle_file (struct mkimage_params *params) } sprintf (tmpfile, "%s%s", params->imagefile, MKIMAGE_TMPFILE_SUFFIX); - /* dtc -I dts -O -p 200 datafile > tmpfile */ + /* 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); -- cgit v0.10.2 From eabd5d8d70ecf7072ac5b7aef2fe239553346dac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bie=C3=9Fmann?= Date: Thu, 10 Feb 2011 00:30:04 +0000 Subject: soft_i2c: add necessary includes for AVR32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Andreas Bießmann diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c index 1595c07..ae3c573 100644 --- a/drivers/i2c/soft_i2c.c +++ b/drivers/i2c/soft_i2c.c @@ -30,6 +30,9 @@ #include #include #endif +#if defined(CONFIG_AVR32) +#include +#endif #if defined(CONFIG_AT91FAMILY) #include #include -- cgit v0.10.2 From 2515d8437bf3a4415d3354d01e37f07d922250e2 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 12 Nov 2012 14:34:25 +0000 Subject: i2c: Use __weak instead of __attribute__((weak, alias)) Use __weak from linux/compiler.h instead of __attribute__((weak, alias)) to define overridable function. This patch is intended as a cleanup patch to bring some consistency into the code. Signed-off-by: Marek Vasut Cc: Heiko Schocher diff --git a/common/cmd_i2c.c b/common/cmd_i2c.c index 4438db5..c8990ef 100644 --- a/common/cmd_i2c.c +++ b/common/cmd_i2c.c @@ -82,6 +82,7 @@ #include #include #include +#include /* Display values from last command. * Memory modify remembered values are different from display memory. @@ -133,30 +134,27 @@ DECLARE_GLOBAL_DATA_PTR; #define DISP_LINE_LEN 16 /* implement possible board specific board init */ -static void __def_i2c_init_board(void) +__weak +void i2c_init_board(void) { return; } -void i2c_init_board(void) - __attribute__((weak, alias("__def_i2c_init_board"))); /* TODO: Implement architecture-specific get/set functions */ -static unsigned int __def_i2c_get_bus_speed(void) +__weak +unsigned int i2c_get_bus_speed(void) { return CONFIG_SYS_I2C_SPEED; } -unsigned int i2c_get_bus_speed(void) - __attribute__((weak, alias("__def_i2c_get_bus_speed"))); -static int __def_i2c_set_bus_speed(unsigned int speed) +__weak +int i2c_set_bus_speed(unsigned int speed) { if (speed != CONFIG_SYS_I2C_SPEED) return -1; return 0; } -int i2c_set_bus_speed(unsigned int) - __attribute__((weak, alias("__def_i2c_set_bus_speed"))); /* * get_alen: small parser helper function to get address length -- cgit v0.10.2 From 7f86bd576f7570b4d19d016f768c869d88b3d0ab Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 12 Nov 2012 14:34:26 +0000 Subject: i2c: Staticize local functions in mxc i2c driver Some functions in the MXC i2c driver were not static, fix this by making them so. Signed-off-by: Marek Vasut Cc: Heiko Schocher Cc: Stefano Babic diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index 18270b9..a73b10b 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -115,7 +115,7 @@ static uint8_t i2c_imx_get_clk(unsigned int rate) /* * Set I2C Bus speed */ -int bus_i2c_set_bus_speed(void *base, int speed) +static int bus_i2c_set_bus_speed(void *base, int speed) { struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)base; u8 clk_idx = i2c_imx_get_clk(speed); @@ -133,7 +133,7 @@ int bus_i2c_set_bus_speed(void *base, int speed) /* * Get I2C Speed */ -unsigned int bus_i2c_get_bus_speed(void *base) +static unsigned int bus_i2c_get_bus_speed(void *base) { struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)base; u8 clk_idx = readb(&i2c_regs->ifdr); -- cgit v0.10.2 From 06afa388b2f6f0f974a9d60f0e5fc273b857bdeb Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 12 Nov 2012 14:34:27 +0000 Subject: i2c: kerneldoc: Add kerneldoc annotations to cmd_i2c.c Add kerneldoc style documentation into cmd_i2c.c to properly describe all overridable functions and most of the command interface. Signed-off-by: Marek Vasut Cc: Heiko Schocher diff --git a/common/cmd_i2c.c b/common/cmd_i2c.c index c8990ef..e7df2e4 100644 --- a/common/cmd_i2c.c +++ b/common/cmd_i2c.c @@ -133,7 +133,13 @@ DECLARE_GLOBAL_DATA_PTR; #define DISP_LINE_LEN 16 -/* implement possible board specific board init */ +/** + * i2c_init_board() - Board-specific I2C bus init + * + * This function is the default no-op implementation of I2C bus + * initialization. This function can be overriden by board-specific + * implementation if needed. + */ __weak void i2c_init_board(void) { @@ -141,12 +147,38 @@ void i2c_init_board(void) } /* TODO: Implement architecture-specific get/set functions */ + +/** + * i2c_get_bus_speed() - Return I2C bus speed + * + * This function is the default implementation of function for retrieveing + * the current I2C bus speed in Hz. + * + * A driver implementing runtime switching of I2C bus speed must override + * this function to report the speed correctly. Simple or legacy drivers + * can use this fallback. + * + * Returns I2C bus speed in Hz. + */ __weak unsigned int i2c_get_bus_speed(void) { return CONFIG_SYS_I2C_SPEED; } +/** + * i2c_set_bus_speed() - Configure I2C bus speed + * @speed: Newly set speed of the I2C bus in Hz + * + * This function is the default implementation of function for setting + * the I2C bus speed in Hz. + * + * A driver implementing runtime switching of I2C bus speed must override + * this function to report the speed correctly. Simple or legacy drivers + * can use this fallback. + * + * Returns zero on success, negative value on error. + */ __weak int i2c_set_bus_speed(unsigned int speed) { @@ -156,9 +188,10 @@ int i2c_set_bus_speed(unsigned int speed) return 0; } -/* - * get_alen: small parser helper function to get address length - * returns the address length +/** + * get_alen() - Small parser helper function to get address length + * + * Returns the address length. */ static uint get_alen(char *arg) { @@ -176,11 +209,19 @@ static uint get_alen(char *arg) return alen; } -/* +/** + * do_i2c_read() - Handle the "i2c read" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + * * Syntax: * i2c read {i2c_chip} {devaddr}{.0, .1, .2} {len} {memaddr} */ - static int do_i2c_read ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { u_char chip; @@ -269,7 +310,16 @@ static int do_i2c_write(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[ return 0; } -/* +/** + * do_i2c_md() - Handle the "i2c md" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + * * Syntax: * i2c md {i2c_chip} {addr}{.0, .1, .2} {len} */ @@ -361,8 +411,15 @@ static int do_i2c_md ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[] return 0; } - -/* Write (fill) memory +/** + * do_i2c_mw() - Handle the "i2c mw" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. * * Syntax: * i2c mw {i2c_chip} {addr}{.0, .1, .2} {data} [{count}] @@ -419,10 +476,20 @@ static int do_i2c_mw ( cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[] #endif } - return (0); + return 0; } -/* Calculate a CRC on memory +/** + * do_i2c_crc() - Handle the "i2c crc32" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Calculate a CRC on memory + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. * * Syntax: * i2c crc32 {i2c_chip} {addr}{.0, .1, .2} {count} @@ -479,13 +546,22 @@ static int do_i2c_crc (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[] return 0; } -/* Modify memory. +/** + * mod_i2c_mem() - Handle the "i2c mm" and "i2c nm" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Modify memory. + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. * * Syntax: * i2c mm{.b, .w, .l} {i2c_chip} {addr}{.0, .1, .2} * i2c nm{.b, .w, .l} {i2c_chip} {addr}{.0, .1, .2} */ - static int mod_i2c_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char * const argv[]) { @@ -601,7 +677,16 @@ mod_i2c_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char * const arg return 0; } -/* +/** + * do_i2c_probe() - Handle the "i2c probe" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + * * Syntax: * i2c probe {addr} * @@ -655,7 +740,16 @@ static int do_i2c_probe (cmd_tbl_t *cmdtp, int flag, int argc, char * const argv return (0 == found); } -/* +/** + * do_i2c_loop() - Handle the "i2c loop" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + * * Syntax: * i2c loop {i2c_chip} {addr}{.0, .1, .2} [{length}] [{delay}] * {length} - Number of bytes to read @@ -716,6 +810,8 @@ static int do_i2c_loop(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[] /* * The SDRAM command is separately configured because many * (most?) embedded boards don't use SDRAM DIMMs. + * + * FIXME: Document and probably move elsewhere! */ #if defined(CONFIG_CMD_SDRAM) static void print_ddr2_tcyc (u_char const b) @@ -1245,6 +1341,15 @@ static int do_sdram (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) #endif #if defined(CONFIG_I2C_MUX) +/** + * do_i2c_add_bus() - Handle the "i2c bus" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero always. + */ static int do_i2c_add_bus(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { int ret=0; @@ -1274,6 +1379,16 @@ static int do_i2c_add_bus(cmd_tbl_t * cmdtp, int flag, int argc, char * const ar #endif /* CONFIG_I2C_MUX */ #if defined(CONFIG_I2C_MULTI_BUS) +/** + * do_i2c_bus_num() - Handle the "i2c dev" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + */ static int do_i2c_bus_num(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { int bus_idx, ret=0; @@ -1292,6 +1407,16 @@ static int do_i2c_bus_num(cmd_tbl_t * cmdtp, int flag, int argc, char * const ar } #endif /* CONFIG_I2C_MULTI_BUS */ +/** + * do_i2c_bus_speed() - Handle the "i2c speed" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + */ static int do_i2c_bus_speed(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { int speed, ret=0; @@ -1309,16 +1434,45 @@ static int do_i2c_bus_speed(cmd_tbl_t * cmdtp, int flag, int argc, char * const return ret; } +/** + * do_i2c_mm() - Handle the "i2c mm" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + */ static int do_i2c_mm(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { return mod_i2c_mem (cmdtp, 1, flag, argc, argv); } +/** + * do_i2c_nm() - Handle the "i2c nm" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + */ static int do_i2c_nm(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { return mod_i2c_mem (cmdtp, 0, flag, argc, argv); } +/** + * do_i2c_reset() - Handle the "i2c reset" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero always. + */ static int do_i2c_reset(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); @@ -1354,6 +1508,16 @@ void i2c_reloc(void) { } #endif +/** + * do_i2c() - Handle the "i2c" command-line command + * @cmdtp: Command data struct pointer + * @flag: Command flag + * @argc: Command-line argument count + * @argv: Array of command-line arguments + * + * Returns zero on success, CMD_RET_USAGE in case of misuse and negative + * on error. + */ static int do_i2c(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) { cmd_tbl_t *c; -- cgit v0.10.2 From f32a470ed3dfff493473bc25c7d0a01920507eed Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 12 Nov 2012 14:34:28 +0000 Subject: i2c: mxs: Abstract out the MXS I2C speed setup This patch pulls out the I2C speed setup from the i2c_init() call and implements the bus configuration lookup table with register values that needs to be programmed into the I2C IP to run at particular speed. This patch is a first step towards implementing run-time I2C bus speed configuration for the MXS I2C IP. Signed-off-by: Marek Vasut Cc: Stefano Babic Cc: Fabio Estevam diff --git a/drivers/i2c/mxs_i2c.c b/drivers/i2c/mxs_i2c.c index 2a193c2..98f6e8c 100644 --- a/drivers/i2c/mxs_i2c.c +++ b/drivers/i2c/mxs_i2c.c @@ -210,34 +210,51 @@ int i2c_probe(uchar chip) return ret; } +static struct mxs_i2c_speed_table { + uint32_t speed; + uint32_t timing0; + uint32_t timing1; +} mxs_i2c_tbl[] = { + { + 100000, + (0x0078 << I2C_TIMING0_HIGH_COUNT_OFFSET) | + (0x0030 << I2C_TIMING0_RCV_COUNT_OFFSET), + (0x0080 << I2C_TIMING1_LOW_COUNT_OFFSET) | + (0x0030 << I2C_TIMING1_XMIT_COUNT_OFFSET) + }, + { + 400000, + (0x000f << I2C_TIMING0_HIGH_COUNT_OFFSET) | + (0x0007 << I2C_TIMING0_RCV_COUNT_OFFSET), + (0x001f << I2C_TIMING1_LOW_COUNT_OFFSET) | + (0x000f << I2C_TIMING1_XMIT_COUNT_OFFSET), + } +}; + +static struct mxs_i2c_speed_table *mxs_i2c_speed_to_cfg(uint32_t speed) +{ + int i; + for (i = 0; i < ARRAY_SIZE(mxs_i2c_tbl); i++) + if (mxs_i2c_tbl[i].speed == speed) + return &mxs_i2c_tbl[i]; + return NULL; +} + void i2c_init(int speed, int slaveadd) { struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; + struct mxs_i2c_speed_table *spd = mxs_i2c_speed_to_cfg(speed); - mxs_i2c_reset(); - - switch (speed) { - case 100000: - writel((0x0078 << I2C_TIMING0_HIGH_COUNT_OFFSET) | - (0x0030 << I2C_TIMING0_RCV_COUNT_OFFSET), - &i2c_regs->hw_i2c_timing0); - writel((0x0080 << I2C_TIMING1_LOW_COUNT_OFFSET) | - (0x0030 << I2C_TIMING1_XMIT_COUNT_OFFSET), - &i2c_regs->hw_i2c_timing1); - break; - case 400000: - writel((0x000f << I2C_TIMING0_HIGH_COUNT_OFFSET) | - (0x0007 << I2C_TIMING0_RCV_COUNT_OFFSET), - &i2c_regs->hw_i2c_timing0); - writel((0x001f << I2C_TIMING1_LOW_COUNT_OFFSET) | - (0x000f << I2C_TIMING1_XMIT_COUNT_OFFSET), - &i2c_regs->hw_i2c_timing1); - break; - default: + if (!spd) { printf("MXS I2C: Invalid speed selected (%d Hz)\n", speed); return; } + mxs_i2c_reset(); + + writel(spd->timing0, &i2c_regs->hw_i2c_timing0); + writel(spd->timing1, &i2c_regs->hw_i2c_timing1); + writel((0x0015 << I2C_TIMING2_BUS_FREE_OFFSET) | (0x000d << I2C_TIMING2_LEADIN_COUNT_OFFSET), &i2c_regs->hw_i2c_timing2); -- cgit v0.10.2 From a06f590f7fce2f484d3bc0bff48a7aa90b70f13d Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 12 Nov 2012 14:34:29 +0000 Subject: i2c: mxs: Implement i2c_get/set_bus_speed() This patch implements the setup and retrieval functions for the I2C bus speed on the MXS I2C IP. Signed-off-by: Marek Vasut Cc: Stefano Babic Cc: Fabio Estevam diff --git a/drivers/i2c/mxs_i2c.c b/drivers/i2c/mxs_i2c.c index 98f6e8c..4152242 100644 --- a/drivers/i2c/mxs_i2c.c +++ b/drivers/i2c/mxs_i2c.c @@ -240,6 +240,51 @@ static struct mxs_i2c_speed_table *mxs_i2c_speed_to_cfg(uint32_t speed) return NULL; } +static uint32_t mxs_i2c_cfg_to_speed(uint32_t timing0, uint32_t timing1) +{ + int i; + for (i = 0; i < ARRAY_SIZE(mxs_i2c_tbl); i++) { + if (mxs_i2c_tbl[i].timing0 != timing0) + continue; + if (mxs_i2c_tbl[i].timing1 != timing1) + continue; + return mxs_i2c_tbl[i].speed; + } + + return 0; +} + +int i2c_set_bus_speed(unsigned int speed) +{ + struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; + struct mxs_i2c_speed_table *spd = mxs_i2c_speed_to_cfg(speed); + + if (!spd) { + printf("MXS I2C: Invalid speed selected (%d Hz)\n", speed); + return -EINVAL; + } + + writel(spd->timing0, &i2c_regs->hw_i2c_timing0); + writel(spd->timing1, &i2c_regs->hw_i2c_timing1); + + writel((0x0015 << I2C_TIMING2_BUS_FREE_OFFSET) | + (0x000d << I2C_TIMING2_LEADIN_COUNT_OFFSET), + &i2c_regs->hw_i2c_timing2); + + return 0; +} + +unsigned int i2c_get_bus_speed(void) +{ + struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; + uint32_t timing0, timing1; + + timing0 = readl(&i2c_regs->hw_i2c_timing0); + timing1 = readl(&i2c_regs->hw_i2c_timing1); + + return mxs_i2c_cfg_to_speed(timing0, timing1); +} + void i2c_init(int speed, int slaveadd) { struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; -- cgit v0.10.2 From a157e0d5f6aa0b86bd7b6ae09d11b12a4a736d97 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 12 Nov 2012 14:34:30 +0000 Subject: i2c: mxs: Use i2c_set_bus_speed() in i2c_init() Use i2c_set_bus_speed() in i2c_init() within the mxs i2c driver to avoid duplication of code. Signed-off-by: Marek Vasut Cc: Stefano Babic Cc: Fabio Estevam diff --git a/drivers/i2c/mxs_i2c.c b/drivers/i2c/mxs_i2c.c index 4152242..3771452 100644 --- a/drivers/i2c/mxs_i2c.c +++ b/drivers/i2c/mxs_i2c.c @@ -287,22 +287,8 @@ unsigned int i2c_get_bus_speed(void) void i2c_init(int speed, int slaveadd) { - struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; - struct mxs_i2c_speed_table *spd = mxs_i2c_speed_to_cfg(speed); - - if (!spd) { - printf("MXS I2C: Invalid speed selected (%d Hz)\n", speed); - return; - } - mxs_i2c_reset(); - - writel(spd->timing0, &i2c_regs->hw_i2c_timing0); - writel(spd->timing1, &i2c_regs->hw_i2c_timing1); - - writel((0x0015 << I2C_TIMING2_BUS_FREE_OFFSET) | - (0x000d << I2C_TIMING2_LEADIN_COUNT_OFFSET), - &i2c_regs->hw_i2c_timing2); + i2c_set_bus_speed(speed); return; } -- cgit v0.10.2 From aff36ea92ec0700cd9241bf01e72956a3ab9600e Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 12 Nov 2012 14:34:31 +0000 Subject: i2c: mxs: Fix TIMING2 register value According to FSL, the value in the TIMING2 register shall be 0x00300030 instead of what's written in the datasheet. This new value correlates with older STMP36xx datasheet. Issues were detected in Linux when this register was misconfigured, so write this correct value. Signed-off-by: Marek Vasut Cc: Stefano Babic Cc: Fabio Estevam diff --git a/drivers/i2c/mxs_i2c.c b/drivers/i2c/mxs_i2c.c index 3771452..006fb91 100644 --- a/drivers/i2c/mxs_i2c.c +++ b/drivers/i2c/mxs_i2c.c @@ -267,8 +267,8 @@ int i2c_set_bus_speed(unsigned int speed) writel(spd->timing0, &i2c_regs->hw_i2c_timing0); writel(spd->timing1, &i2c_regs->hw_i2c_timing1); - writel((0x0015 << I2C_TIMING2_BUS_FREE_OFFSET) | - (0x000d << I2C_TIMING2_LEADIN_COUNT_OFFSET), + writel((0x0030 << I2C_TIMING2_BUS_FREE_OFFSET) | + (0x0030 << I2C_TIMING2_LEADIN_COUNT_OFFSET), &i2c_regs->hw_i2c_timing2); return 0; -- cgit v0.10.2 From 1e2fc0d19bac9bf4f62d259169e902e700a18bad Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 30 Nov 2012 18:17:06 +0000 Subject: mxs: i2c: Restore speed setting after block reset The I2C block reset configures the I2C bus speed to strange value. Read the I2C speed from the block before reseting the block and restore it afterwards, so the I2C operates correctly. This issue can be replicated by doing unsuccessful I2C transfer, after such transfer finishes, the I2C block clock speed is misconfigured. Signed-off-by: Marek Vasut Cc: Heiko Schocher Cc: Fabio Estevam diff --git a/drivers/i2c/mxs_i2c.c b/drivers/i2c/mxs_i2c.c index 006fb91..73a6659 100644 --- a/drivers/i2c/mxs_i2c.c +++ b/drivers/i2c/mxs_i2c.c @@ -40,6 +40,7 @@ void mxs_i2c_reset(void) { struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; int ret; + int speed = i2c_get_bus_speed(); ret = mxs_reset_block(&i2c_regs->hw_i2c_ctrl0_reg); if (ret) { @@ -53,6 +54,8 @@ void mxs_i2c_reset(void) &i2c_regs->hw_i2c_ctrl1_clr); writel(I2C_QUEUECTRL_PIO_QUEUE_MODE, &i2c_regs->hw_i2c_queuectrl_set); + + i2c_set_bus_speed(speed); } void mxs_i2c_setup_read(uint8_t chip, int len) -- cgit v0.10.2 From fa5e2845a867a3715240ff221aaec2825b5c73df Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 30 Nov 2012 18:17:07 +0000 Subject: mxs: i2c: Implement algorithm to set up arbitrary i2c speed This algorithm computes the values of TIMING{0,1,2} registers for the MX28 I2C block. This algorithm was derived by using a scope, but the result seems correct. The resulting values programmed into the registers do not correlate with the contents in datasheet. When using the values from the datasheet, the I2C clock were completely wrong. Signed-off-by: Marek Vasut Cc: Stefano Babic Cc: Fabio Estevam Cc: Wolfgang Denk diff --git a/arch/arm/cpu/arm926ejs/mxs/clock.c b/arch/arm/cpu/arm926ejs/mxs/clock.c index bfea6ab..4ff19c3 100644 --- a/arch/arm/cpu/arm926ejs/mxs/clock.c +++ b/arch/arm/cpu/arm926ejs/mxs/clock.c @@ -333,6 +333,8 @@ uint32_t mxc_get_clock(enum mxc_clock clk) return mx28_get_sspclk(MXC_SSPCLK2); case MXC_SSP3_CLK: return mx28_get_sspclk(MXC_SSPCLK3); + case MXC_XTAL_CLK: + return XTAL_FREQ_KHZ * 1000; } return 0; diff --git a/arch/arm/include/asm/arch-mxs/clock.h b/arch/arm/include/asm/arch-mxs/clock.h index 1700fe3..3d39ef2 100644 --- a/arch/arm/include/asm/arch-mxs/clock.h +++ b/arch/arm/include/asm/arch-mxs/clock.h @@ -35,6 +35,7 @@ enum mxc_clock { MXC_SSP1_CLK, MXC_SSP2_CLK, MXC_SSP3_CLK, + MXC_XTAL_CLK, }; enum mxs_ioclock { diff --git a/drivers/i2c/mxs_i2c.c b/drivers/i2c/mxs_i2c.c index 73a6659..b907f7b 100644 --- a/drivers/i2c/mxs_i2c.c +++ b/drivers/i2c/mxs_i2c.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -213,62 +214,39 @@ int i2c_probe(uchar chip) return ret; } -static struct mxs_i2c_speed_table { - uint32_t speed; - uint32_t timing0; - uint32_t timing1; -} mxs_i2c_tbl[] = { - { - 100000, - (0x0078 << I2C_TIMING0_HIGH_COUNT_OFFSET) | - (0x0030 << I2C_TIMING0_RCV_COUNT_OFFSET), - (0x0080 << I2C_TIMING1_LOW_COUNT_OFFSET) | - (0x0030 << I2C_TIMING1_XMIT_COUNT_OFFSET) - }, - { - 400000, - (0x000f << I2C_TIMING0_HIGH_COUNT_OFFSET) | - (0x0007 << I2C_TIMING0_RCV_COUNT_OFFSET), - (0x001f << I2C_TIMING1_LOW_COUNT_OFFSET) | - (0x000f << I2C_TIMING1_XMIT_COUNT_OFFSET), - } -}; - -static struct mxs_i2c_speed_table *mxs_i2c_speed_to_cfg(uint32_t speed) -{ - int i; - for (i = 0; i < ARRAY_SIZE(mxs_i2c_tbl); i++) - if (mxs_i2c_tbl[i].speed == speed) - return &mxs_i2c_tbl[i]; - return NULL; -} - -static uint32_t mxs_i2c_cfg_to_speed(uint32_t timing0, uint32_t timing1) -{ - int i; - for (i = 0; i < ARRAY_SIZE(mxs_i2c_tbl); i++) { - if (mxs_i2c_tbl[i].timing0 != timing0) - continue; - if (mxs_i2c_tbl[i].timing1 != timing1) - continue; - return mxs_i2c_tbl[i].speed; - } - - return 0; -} - int i2c_set_bus_speed(unsigned int speed) { struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; - struct mxs_i2c_speed_table *spd = mxs_i2c_speed_to_cfg(speed); + /* + * The timing derivation algorithm. There is no documentation for this + * algorithm available, it was derived by using the scope and fiddling + * with constants until the result observed on the scope was good enough + * for 20kHz, 50kHz, 100kHz, 200kHz, 300kHz and 400kHz. It should be + * possible to assume the algorithm works for other frequencies as well. + * + * Note it was necessary to cap the frequency on both ends as it's not + * possible to configure completely arbitrary frequency for the I2C bus + * clock. + */ + uint32_t clk = mxc_get_clock(MXC_XTAL_CLK); + uint32_t base = ((clk / speed) - 38) / 2; + uint16_t high_count = base + 3; + uint16_t low_count = base - 3; + uint16_t rcv_count = (high_count * 3) / 4; + uint16_t xmit_count = low_count / 4; + + if (speed > 540000) { + printf("MXS I2C: Speed too high (%d Hz)\n", speed); + return -EINVAL; + } - if (!spd) { - printf("MXS I2C: Invalid speed selected (%d Hz)\n", speed); + if (speed < 12000) { + printf("MXS I2C: Speed too low (%d Hz)\n", speed); return -EINVAL; } - writel(spd->timing0, &i2c_regs->hw_i2c_timing0); - writel(spd->timing1, &i2c_regs->hw_i2c_timing1); + writel((high_count << 16) | rcv_count, &i2c_regs->hw_i2c_timing0); + writel((low_count << 16) | xmit_count, &i2c_regs->hw_i2c_timing1); writel((0x0030 << I2C_TIMING2_BUS_FREE_OFFSET) | (0x0030 << I2C_TIMING2_LEADIN_COUNT_OFFSET), @@ -280,12 +258,15 @@ int i2c_set_bus_speed(unsigned int speed) unsigned int i2c_get_bus_speed(void) { struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; - uint32_t timing0, timing1; + uint32_t clk = mxc_get_clock(MXC_XTAL_CLK); + uint32_t timing0; timing0 = readl(&i2c_regs->hw_i2c_timing0); - timing1 = readl(&i2c_regs->hw_i2c_timing1); - - return mxs_i2c_cfg_to_speed(timing0, timing1); + /* + * This is a reverse version of the algorithm presented in + * i2c_set_bus_speed(). Please refer there for details. + */ + return clk / ((((timing0 >> 16) - 3) * 2) + 38); } void i2c_init(int speed, int slaveadd) -- cgit v0.10.2 From 66839773a82cb56ebeb2b99322151f8a57a30efb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Stehl=C3=A9?= Date: Mon, 3 Dec 2012 06:07:21 +0000 Subject: omap24xx_i2c: Handle OMAP5 like OMAP2,3,4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OMAP5 has 8b i2c data register field, like OMAP2, 3 and 4. Handle in the same way. This fixes the following error on OMAP5: OMAP5430 EVM # mmc rescan timed out in wait_for_bb: I2C_STAT=1410 twl6035: could not turn on LDO9. Signed-off-by: Vincent Stehlé diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c index 094305f..af454f9 100644 --- a/drivers/i2c/omap24xx_i2c.c +++ b/drivers/i2c/omap24xx_i2c.c @@ -179,7 +179,8 @@ static int i2c_read_byte(u8 devaddr, u16 regoffset, u8 alen, u8 *value) if (status & I2C_STAT_XRDY) { w = tmpbuf[i++]; #if !(defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ - defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX)) + defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) || \ + defined(CONFIG_OMAP54XX)) w |= tmpbuf[i++] << 8; #endif writew(w, &i2c_base->data); @@ -209,7 +210,8 @@ static int i2c_read_byte(u8 devaddr, u16 regoffset, u8 alen, u8 *value) } if (status & I2C_STAT_RRDY) { #if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ - defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) + defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) || \ + defined(CONFIG_OMAP54XX) *value = readb(&i2c_base->data); #else *value = readw(&i2c_base->data); @@ -239,7 +241,8 @@ static void flush_fifo(void) stat = readw(&i2c_base->stat); if (stat == I2C_STAT_RRDY) { #if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ - defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) + defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) || \ + defined(CONFIG_OMAP54XX) readb(&i2c_base->data); #else readw(&i2c_base->data); @@ -289,7 +292,8 @@ int i2c_probe(uchar chip) if (status & I2C_STAT_RRDY) { res = 0; #if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ - defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) + defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) || \ + defined(CONFIG_OMAP54XX) readb(&i2c_base->data); #else readw(&i2c_base->data); @@ -376,7 +380,8 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) if (status & I2C_STAT_XRDY) { w = (i < 0) ? tmpbuf[2+i] : buffer[i]; #if !(defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ - defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX)) + defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) || \ + defined(CONFIG_OMAP54XX)) w |= ((++i < 0) ? tmpbuf[2+i] : buffer[i]) << 8; #endif writew(w, &i2c_base->data); -- cgit v0.10.2 From 83d745dd88743a8355a7e58bcb17d475c17b979d Mon Sep 17 00:00:00 2001 From: Piotr Wilczek Date: Tue, 20 Nov 2012 02:19:02 +0000 Subject: exynos:clock: Add i2c clock This patch adds i2c clock for Exynos4 Signed-off-by: Piotr Wilczek Signed-off-by: Kyungmin Park CC: Minkyu Kang diff --git a/arch/arm/cpu/armv7/exynos/clock.c b/arch/arm/cpu/armv7/exynos/clock.c index 4f3b451..21e45d2 100644 --- a/arch/arm/cpu/armv7/exynos/clock.c +++ b/arch/arm/cpu/armv7/exynos/clock.c @@ -732,6 +732,21 @@ static unsigned long exynos5_get_i2c_clk(void) return aclk_66; } +static unsigned long exynos4_get_i2c_clk(void) +{ + struct exynos4_clock *clk = + (struct exynos4_clock *)samsung_get_base_clock(); + unsigned long sclk, aclk_100; + unsigned int ratio; + + sclk = get_pll_clk(APLL); + + ratio = (readl(&clk->div_top)) >> 4; + ratio &= 0xf; + aclk_100 = sclk / (ratio + 1); + return aclk_100; +} + unsigned long get_pll_clk(int pllreg) { if (cpu_is_exynos5()) @@ -752,6 +767,8 @@ unsigned long get_i2c_clk(void) { if (cpu_is_exynos5()) { return exynos5_get_i2c_clk(); + } else if (cpu_is_exynos4()) { + return exynos4_get_i2c_clk(); } else { debug("I2C clock is not set for this CPU\n"); return 0; -- cgit v0.10.2 From 847ab8f6e2bb3431768e153d78c728a6da2f693f Mon Sep 17 00:00:00 2001 From: Piotr Wilczek Date: Tue, 20 Nov 2012 02:19:03 +0000 Subject: exynos:cpu: Add Exynos4 I2C spacing This patch add the spacing for i2c for Exynos4 Signed-off-by: Piotr Wilczek Signed-off-by: Kyungmin Park CC: Minkyu Kang diff --git a/arch/arm/include/asm/arch-exynos/cpu.h b/arch/arm/include/asm/arch-exynos/cpu.h index 2cd4ae1..3073ca1 100644 --- a/arch/arm/include/asm/arch-exynos/cpu.h +++ b/arch/arm/include/asm/arch-exynos/cpu.h @@ -28,6 +28,8 @@ #define EXYNOS4_ADDR_BASE 0x10000000 /* EXYNOS4 */ +#define EXYNOS4_I2C_SPACING 0x10000 + #define EXYNOS4_GPIO_PART3_BASE 0x03860000 #define EXYNOS4_PRO_ID 0x10000000 #define EXYNOS4_SYSREG_BASE 0x10010000 -- cgit v0.10.2 From 89bd58a1e9cb482109498ac38b38fc39d22fb421 Mon Sep 17 00:00:00 2001 From: Piotr Wilczek Date: Tue, 20 Nov 2012 02:19:04 +0000 Subject: exynos:pinmux: Add pinmux support for i2c This patch add pinmux for I2C for Exynos4 Signed-off-by: Piotr Wilczek Signed-off-by: Kyungmin Park CC: Minkyu Kang diff --git a/arch/arm/cpu/armv7/exynos/pinmux.c b/arch/arm/cpu/armv7/exynos/pinmux.c index 7776add..44ce072 100644 --- a/arch/arm/cpu/armv7/exynos/pinmux.c +++ b/arch/arm/cpu/armv7/exynos/pinmux.c @@ -265,10 +265,74 @@ static int exynos5_pinmux_config(int peripheral, int flags) return 0; } +static void exynos4_i2c_config(int peripheral, int flags) +{ + struct exynos4_gpio_part1 *gpio1 = + (struct exynos4_gpio_part1 *) samsung_get_base_gpio_part1(); + + switch (peripheral) { + case PERIPH_ID_I2C0: + s5p_gpio_cfg_pin(&gpio1->d1, 0, GPIO_FUNC(0x2)); + s5p_gpio_cfg_pin(&gpio1->d1, 1, GPIO_FUNC(0x2)); + break; + case PERIPH_ID_I2C1: + s5p_gpio_cfg_pin(&gpio1->d1, 2, GPIO_FUNC(0x2)); + s5p_gpio_cfg_pin(&gpio1->d1, 3, GPIO_FUNC(0x2)); + break; + case PERIPH_ID_I2C2: + s5p_gpio_cfg_pin(&gpio1->a0, 6, GPIO_FUNC(0x3)); + s5p_gpio_cfg_pin(&gpio1->a0, 7, GPIO_FUNC(0x3)); + break; + case PERIPH_ID_I2C3: + s5p_gpio_cfg_pin(&gpio1->a1, 2, GPIO_FUNC(0x3)); + s5p_gpio_cfg_pin(&gpio1->a1, 3, GPIO_FUNC(0x3)); + break; + case PERIPH_ID_I2C4: + s5p_gpio_cfg_pin(&gpio1->b, 2, GPIO_FUNC(0x3)); + s5p_gpio_cfg_pin(&gpio1->b, 3, GPIO_FUNC(0x3)); + break; + case PERIPH_ID_I2C5: + s5p_gpio_cfg_pin(&gpio1->b, 6, GPIO_FUNC(0x3)); + s5p_gpio_cfg_pin(&gpio1->b, 7, GPIO_FUNC(0x3)); + break; + case PERIPH_ID_I2C6: + s5p_gpio_cfg_pin(&gpio1->c1, 3, GPIO_FUNC(0x4)); + s5p_gpio_cfg_pin(&gpio1->c1, 4, GPIO_FUNC(0x4)); + break; + case PERIPH_ID_I2C7: + s5p_gpio_cfg_pin(&gpio1->d0, 2, GPIO_FUNC(0x3)); + s5p_gpio_cfg_pin(&gpio1->d0, 3, GPIO_FUNC(0x3)); + break; + } +} + +static int exynos4_pinmux_config(int peripheral, int flags) +{ + switch (peripheral) { + case PERIPH_ID_I2C0: + case PERIPH_ID_I2C1: + case PERIPH_ID_I2C2: + case PERIPH_ID_I2C3: + case PERIPH_ID_I2C4: + case PERIPH_ID_I2C5: + case PERIPH_ID_I2C6: + case PERIPH_ID_I2C7: + exynos4_i2c_config(peripheral, flags); + break; + default: + debug("%s: invalid peripheral %d", __func__, peripheral); + return -1; + } + + return 0; +} + int exynos_pinmux_config(int peripheral, int flags) { if (cpu_is_exynos5()) return exynos5_pinmux_config(peripheral, flags); + else if (cpu_is_exynos4()) + return exynos4_pinmux_config(peripheral, flags); else { debug("pinmux functionality not supported\n"); return -1; -- cgit v0.10.2 From c86d9ed3820bbd89e264261fb022bdf9448bfde7 Mon Sep 17 00:00:00 2001 From: Piotr Wilczek Date: Tue, 20 Nov 2012 02:19:05 +0000 Subject: drivers:i2c: Modify I2C driver for Exynos4 This patch modifies the S3C i2c driver to support both Exynos4 and Exynos5 Signed-off-by: Piotr Wilczek Signed-off-by: Kyungmin Park CC: Minkyu Kang diff --git a/drivers/i2c/s3c24x0_i2c.c b/drivers/i2c/s3c24x0_i2c.c index 9bc4c7f..90d297a 100644 --- a/drivers/i2c/s3c24x0_i2c.c +++ b/drivers/i2c/s3c24x0_i2c.c @@ -27,7 +27,7 @@ */ #include -#ifdef CONFIG_EXYNOS5 +#if (defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) #include #include #else @@ -62,7 +62,7 @@ static unsigned int g_current_bus; /* Stores Current I2C Bus */ -#ifndef CONFIG_EXYNOS5 +#if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) static int GetI2CSDA(void) { struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); @@ -121,7 +121,12 @@ static void ReadWriteByte(struct s3c24x0_i2c *i2c) static struct s3c24x0_i2c *get_base_i2c(void) { -#ifdef CONFIG_EXYNOS5 +#ifdef CONFIG_EXYNOS4 + struct s3c24x0_i2c *i2c = (struct s3c24x0_i2c *)(samsung_get_base_i2c() + + (EXYNOS4_I2C_SPACING + * g_current_bus)); + return i2c; +#elif defined CONFIG_EXYNOS5 struct s3c24x0_i2c *i2c = (struct s3c24x0_i2c *)(samsung_get_base_i2c() + (EXYNOS5_I2C_SPACING * g_current_bus)); @@ -134,7 +139,7 @@ static struct s3c24x0_i2c *get_base_i2c(void) static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd) { ulong freq, pres = 16, div; -#ifdef CONFIG_EXYNOS5 +#if (defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) freq = get_i2c_clk(); #else freq = get_PCLK(); @@ -188,7 +193,7 @@ unsigned int i2c_get_bus_num(void) void i2c_init(int speed, int slaveadd) { struct s3c24x0_i2c *i2c; -#ifndef CONFIG_EXYNOS5 +#if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); #endif int i; @@ -204,7 +209,7 @@ void i2c_init(int speed, int slaveadd) i--; } -#ifndef CONFIG_EXYNOS5 +#if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) if ((readl(&i2c->iicstat) & I2CSTAT_BSY) || GetI2CSDA() == 0) { #ifdef CONFIG_S3C2410 ulong old_gpecon = readl(&gpio->gpecon); @@ -248,7 +253,7 @@ void i2c_init(int speed, int slaveadd) writel(old_gpecon, &gpio->pgcon); #endif } -#endif /* #ifndef CONFIG_EXYNOS5 */ +#endif /* #if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) */ i2c_ch_init(i2c, speed, slaveadd); } -- cgit v0.10.2 From ac6e2fe6e4afe54a9c7ef0d93827a86e264814f2 Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Thu, 6 Dec 2012 00:04:15 +0000 Subject: designware_i2c.c: Added the support for MULTI_BUS This patch adds the capability to switch between 10 different I2C busses (from 0 to 9). Signed-off-by: Armando Visconti diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index bf64a2a..4e4bfd4 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -26,7 +26,12 @@ #include #include "designware_i2c.h" -static struct i2c_regs *const i2c_regs_p = +#ifdef CONFIG_I2C_MULTI_BUS +static unsigned int bus_initialized[CONFIG_SYS_I2C_BUS_MAX]; +static unsigned int current_bus = 0; +#endif + +static struct i2c_regs *i2c_regs_p = (struct i2c_regs *)CONFIG_SYS_I2C_BASE; /* @@ -150,6 +155,10 @@ void i2c_init(int speed, int slaveadd) enbl = readl(&i2c_regs_p->ic_enable); enbl |= IC_ENABLE_0B; writel(enbl, &i2c_regs_p->ic_enable); + +#ifdef CONFIG_I2C_MULTI_BUS + bus_initialized[current_bus] = 1; +#endif } /* @@ -344,3 +353,74 @@ int i2c_probe(uchar chip) return ret; } + +#ifdef CONFIG_I2C_MULTI_BUS +int i2c_set_bus_num(unsigned int bus) +{ + switch (bus) { + case 0: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE; + break; +#ifdef CONFIG_SYS_I2C_BASE1 + case 1: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE1; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE2 + case 2: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE2; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE3 + case 3: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE3; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE4 + case 4: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE4; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE5 + case 5: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE5; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE6 + case 6: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE6; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE7 + case 7: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE7; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE8 + case 8: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE8; + break; +#endif +#ifdef CONFIG_SYS_I2C_BASE9 + case 9: + i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE9; + break; +#endif + default: + printf("Bad bus: %d\n", bus); + return -1; + } + + current_bus = bus; + + if (!bus_initialized[current_bus]) + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + + return 0; +} + +int i2c_get_bus_num(void) +{ + return current_bus; +} +#endif -- cgit v0.10.2 From 491739bb7456b95bb863421f6cd76af0ff9b797c Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Thu, 6 Dec 2012 00:04:16 +0000 Subject: designware_i2c: Added s/w generation of stop bit In the newer versions of designware i2c IP there is the possibility of configuring it with IC_EMPTYFIFO_HOLD_MASTER_EN=1, which basically requires the s/w to generate the stop bit condition directly, as the h/w will not automatically generate it when TX_FIFO is empty. To avoid generation of an extra 0x0 byte sent as data, the IC_STOP command must be sent along with the last IC_CMD. This patch always writes bit[9] of ic_data_cmd even in the older versions, assuming that it is a noop there. Signed-off-by: Armando Visconti diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index 4e4bfd4..eab3131 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -283,7 +283,10 @@ int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) start_time_rx = get_timer(0); while (len) { - writel(IC_CMD, &i2c_regs_p->ic_cmd_data); + if (len == 1) + writel(IC_CMD | IC_STOP, &i2c_regs_p->ic_cmd_data); + else + writel(IC_CMD, &i2c_regs_p->ic_cmd_data); if (readl(&i2c_regs_p->ic_status) & IC_STATUS_RFNE) { *buffer++ = (uchar)readl(&i2c_regs_p->ic_cmd_data); @@ -322,9 +325,11 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) start_time_tx = get_timer(0); while (len) { if (readl(&i2c_regs_p->ic_status) & IC_STATUS_TFNF) { - writel(*buffer, &i2c_regs_p->ic_cmd_data); + if (--len == 0) + writel(*buffer | IC_STOP, &i2c_regs_p->ic_cmd_data); + else + writel(*buffer, &i2c_regs_p->ic_cmd_data); buffer++; - len--; start_time_tx = get_timer(0); } else if (get_timer(start_time_tx) > (nb * I2C_BYTE_TO)) { diff --git a/drivers/i2c/designware_i2c.h b/drivers/i2c/designware_i2c.h index 03b520e..e004152 100644 --- a/drivers/i2c/designware_i2c.h +++ b/drivers/i2c/designware_i2c.h @@ -95,6 +95,7 @@ struct i2c_regs { /* i2c data buffer and command register definitions */ #define IC_CMD 0x0100 +#define IC_STOP 0x0200 /* i2c interrupt status register definitions */ #define IC_GEN_CALL 0x0800 -- cgit v0.10.2 From 5b8439bbdea9ad8c120cda408a97ed2e55e943d3 Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Thu, 6 Dec 2012 00:04:17 +0000 Subject: designware_i2c: Fixed the setting of the i2c bus speed There are three couple (hcnt/lcnt) of registers for each speed (SS/FS/HS). The driver needs to set the proper couple of regs according to what speed we are setting. Signed-off-by: Armando Visconti diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index eab3131..6653870 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -44,7 +44,6 @@ static void set_speed(int i2c_spd) { unsigned int cntl; unsigned int hcnt, lcnt; - unsigned int high, low; unsigned int enbl; /* to set speed cltr must be disabled */ @@ -52,39 +51,38 @@ static void set_speed(int i2c_spd) enbl &= ~IC_ENABLE_0B; writel(enbl, &i2c_regs_p->ic_enable); - cntl = (readl(&i2c_regs_p->ic_con) & (~IC_CON_SPD_MSK)); switch (i2c_spd) { case IC_SPEED_MODE_MAX: cntl |= IC_CON_SPD_HS; - high = MIN_HS_SCL_HIGHTIME; - low = MIN_HS_SCL_LOWTIME; + hcnt = (IC_CLK * MIN_HS_SCL_HIGHTIME) / NANO_TO_MICRO; + writel(hcnt, &i2c_regs_p->ic_hs_scl_hcnt); + lcnt = (IC_CLK * MIN_HS_SCL_LOWTIME) / NANO_TO_MICRO; + writel(lcnt, &i2c_regs_p->ic_hs_scl_lcnt); break; case IC_SPEED_MODE_STANDARD: cntl |= IC_CON_SPD_SS; - high = MIN_SS_SCL_HIGHTIME; - low = MIN_SS_SCL_LOWTIME; + hcnt = (IC_CLK * MIN_SS_SCL_HIGHTIME) / NANO_TO_MICRO; + writel(hcnt, &i2c_regs_p->ic_ss_scl_hcnt); + lcnt = (IC_CLK * MIN_SS_SCL_LOWTIME) / NANO_TO_MICRO; + writel(lcnt, &i2c_regs_p->ic_ss_scl_lcnt); break; case IC_SPEED_MODE_FAST: default: cntl |= IC_CON_SPD_FS; - high = MIN_FS_SCL_HIGHTIME; - low = MIN_FS_SCL_LOWTIME; + hcnt = (IC_CLK * MIN_FS_SCL_HIGHTIME) / NANO_TO_MICRO; + writel(hcnt, &i2c_regs_p->ic_fs_scl_hcnt); + lcnt = (IC_CLK * MIN_FS_SCL_LOWTIME) / NANO_TO_MICRO; + writel(lcnt, &i2c_regs_p->ic_fs_scl_lcnt); break; } writel(cntl, &i2c_regs_p->ic_con); - hcnt = (IC_CLK * high) / NANO_TO_MICRO; - writel(hcnt, &i2c_regs_p->ic_fs_scl_hcnt); - - lcnt = (IC_CLK * low) / NANO_TO_MICRO; - writel(lcnt, &i2c_regs_p->ic_fs_scl_lcnt); - - /* re-enable i2c ctrl back now that speed is set */ + /* Enable back i2c now speed set */ enbl |= IC_ENABLE_0B; writel(enbl, &i2c_regs_p->ic_enable); } -- cgit v0.10.2 From ea31b7a7be738d7f4048f9fbe20fe02356999d44 Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Thu, 6 Dec 2012 00:04:18 +0000 Subject: designware_i2c.h: Fixed the correct values for SCL low/high time Signed-off-by: Armando Visconti diff --git a/drivers/i2c/designware_i2c.h b/drivers/i2c/designware_i2c.h index e004152..0dc8884 100644 --- a/drivers/i2c/designware_i2c.h +++ b/drivers/i2c/designware_i2c.h @@ -65,9 +65,9 @@ struct i2c_regs { /* High and low times in different speed modes (in ns) */ #define MIN_SS_SCL_HIGHTIME 4000 -#define MIN_SS_SCL_LOWTIME 5000 -#define MIN_FS_SCL_HIGHTIME 800 -#define MIN_FS_SCL_LOWTIME 1700 +#define MIN_SS_SCL_LOWTIME 4700 +#define MIN_FS_SCL_HIGHTIME 600 +#define MIN_FS_SCL_LOWTIME 1300 #define MIN_HS_SCL_HIGHTIME 60 #define MIN_HS_SCL_LOWTIME 160 -- cgit v0.10.2 From d40d914c06e73c2a7823c2a6dd164d1c10529f84 Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Thu, 6 Dec 2012 00:04:19 +0000 Subject: designware_i2c.h: Define IC_CLK only if not already defined in config file Signed-off-by: Armando Visconti diff --git a/drivers/i2c/designware_i2c.h b/drivers/i2c/designware_i2c.h index 0dc8884..2faf4a8 100644 --- a/drivers/i2c/designware_i2c.h +++ b/drivers/i2c/designware_i2c.h @@ -60,7 +60,9 @@ struct i2c_regs { u32 ic_tx_abrt_source; }; +#if !defined(IC_CLK) #define IC_CLK 166 +#endif #define NANO_TO_MICRO 1000 /* High and low times in different speed modes (in ns) */ -- cgit v0.10.2 From 94740e473920f5f0c71f949281db8dcb132e27cd Mon Sep 17 00:00:00 2001 From: Nikita Kiryanov Date: Mon, 26 Nov 2012 23:06:32 +0000 Subject: gpio: remove duplicate function signature gpio_request() appears twice in asm-generic/gpio.h Remove one of the definitions. Signed-off-by: Nikita Kiryanov diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h index 23c9649..bfedbe4 100644 --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -42,10 +42,11 @@ */ /** - * Request ownership of a GPIO. + * Request a gpio. This should be called before any of the other functions + * are used on this gpio. * - * @param gpio GPIO number - * @param label Name given to the GPIO + * @param gp GPIO number + * @param label User label for this GPIO * @return 0 if ok, -1 on error */ int gpio_request(unsigned gpio, const char *label); @@ -93,14 +94,4 @@ int gpio_get_value(unsigned gpio); * @return 0 if ok, -1 on error */ int gpio_set_value(unsigned gpio, int value); - -/** - * Request a gpio. This should be called before any of the other functions - * are used on this gpio. - * - * @param gp GPIO number - * @param label User label for this GPIO - * @return 0 if ok, -1 on error - */ -int gpio_request(unsigned gpio, const char *label); #endif /* _ASM_GENERIC_GPIO_H_ */ -- cgit v0.10.2 From 01433d60016a5aa4b53608f816e1e220f0108546 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 5 Dec 2012 14:46:28 +0000 Subject: Add new bootstage step for the main loop Mark when we get to the main loop. Signed-off-by: Simon Glass diff --git a/common/main.c b/common/main.c index 5362781..b145f85 100644 --- a/common/main.c +++ b/common/main.c @@ -376,6 +376,8 @@ void main_loop (void) char bcs_set[16]; #endif /* CONFIG_BOOTCOUNT_LIMIT */ + bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop"); + #ifdef CONFIG_BOOTCOUNT_LIMIT bootcount = bootcount_load(); bootcount++; -- cgit v0.10.2 From 53fdc7ef2e43902c4415a81a434c35f9ed8713bc Mon Sep 17 00:00:00 2001 From: Anton Staaf Date: Wed, 5 Dec 2012 14:46:29 +0000 Subject: Add gettime command Gettime returns the current timer value. If CONFIG_SYS_HZ is defined then the timer value is also converted to seconds. Tegra20 (SeaBoard) # gettime Timer val: 7754 Seconds : 7 Remainder : 754 sys_hz = 1000 There has been some discussion about whether this is useful enough to be included in U-Boot. The following boards do not have CONFIG_SYS_HZ defined: M52277EVB M52277EVB_stmicro M53017EVB M54418TWR M54418TWR_nand_mii M54418TWR_nand_rmii M54418TWR_nand_rmii_lowfreq M54418TWR_serial_mii M54418TWR_serial_rmii Signed-off-by: Anton Staaf Signed-off-by: Simon Glass diff --git a/README b/README index 5a86ae9..d1c60de 100644 --- a/README +++ b/README @@ -824,6 +824,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_GETTIME * Get time since boot CONFIG_CMD_GO * the 'go' command (exec code) CONFIG_CMD_GREPENV * search environment CONFIG_CMD_HWFLOW * RTS/CTS hw flow control diff --git a/common/Makefile b/common/Makefile index c29a7d8..b3a6032 100644 --- a/common/Makefile +++ b/common/Makefile @@ -100,6 +100,7 @@ ifdef CONFIG_FPGA COBJS-$(CONFIG_CMD_FPGA) += cmd_fpga.o endif COBJS-$(CONFIG_CMD_FS_GENERIC) += cmd_fs.o +COBJS-$(CONFIG_CMD_GETTIME) += cmd_gettime.o COBJS-$(CONFIG_CMD_GPIO) += cmd_gpio.o COBJS-$(CONFIG_CMD_I2C) += cmd_i2c.o COBJS-$(CONFIG_CMD_IDE) += cmd_ide.o diff --git a/common/cmd_gettime.c b/common/cmd_gettime.c new file mode 100644 index 0000000..d7d36a9 --- /dev/null +++ b/common/cmd_gettime.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. All rights reserved. + * + * Copyright (c) 2009, Code Aurora Forum. All rights reserved. + * + * (C) Copyright 2001 + * 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 + */ + +/* + * Get Timer overflows after 2^32 / CONFIG_SYS_HZ (32Khz) = 131072 sec + */ +#include +#include + +static int do_gettime(cmd_tbl_t *cmdtp, int flag, int argc, + char * const argv[]) +{ + unsigned long int val = get_timer(0); + +#ifdef CONFIG_SYS_HZ + printf("Timer val: %lu\n", val); + printf("Seconds : %lu\n", val / CONFIG_SYS_HZ); + printf("Remainder : %lu\n", val % CONFIG_SYS_HZ); + printf("sys_hz = %lu\n", (unsigned long int)CONFIG_SYS_HZ); +#else + printf("CONFIG_SYS_HZ not defined"); + printf("Timer Val %lu", val); +#endif + + return 0; +} + +U_BOOT_CMD( + gettime, 1, 1, do_gettime, + "get timer val elapsed,\n", + "get time elapsed from uboot start\n" +); diff --git a/include/config_cmd_all.h b/include/config_cmd_all.h index f434cd0..b87967e 100644 --- a/include/config_cmd_all.h +++ b/include/config_cmd_all.h @@ -40,6 +40,7 @@ #define CONFIG_CMD_FDOS /* Floppy DOS support */ #define CONFIG_CMD_FLASH /* flinfo, erase, protect */ #define CONFIG_CMD_FPGA /* FPGA configuration Support */ +#define CONFIG_CMD_GETTIME /* Get time since boot */ #define CONFIG_CMD_HWFLOW /* RTS/CTS hw flow control */ #define CONFIG_CMD_I2C /* I2C serial bus support */ #define CONFIG_CMD_IDE /* IDE harddisk support */ -- cgit v0.10.2 From ff048ea916f74a40c18b5aaa5f357dce1c75bffa Mon Sep 17 00:00:00 2001 From: Kenneth Waters Date: Wed, 5 Dec 2012 14:46:30 +0000 Subject: Add a command to read raw blocks from a partition Sometimes data is on a block device and within a partition, but not in a particular filesystem. This commands permits reading raw data from a partition. Signed-off-by: Kenneth Waters Signed-off-by: Simon Glass diff --git a/README b/README index d1c60de..5810ced 100644 --- a/README +++ b/README @@ -861,6 +861,7 @@ The following options need to be configured: CONFIG_CMD_PING * send ICMP ECHO_REQUEST to network host CONFIG_CMD_PORTIO * Port I/O + CONFIG_CMD_READ * Read raw data from partition CONFIG_CMD_REGINFO * Register dump CONFIG_CMD_RUN run command in env variable CONFIG_CMD_SAVES * save S record dump diff --git a/common/Makefile b/common/Makefile index b3a6032..07a93d1 100644 --- a/common/Makefile +++ b/common/Makefile @@ -142,6 +142,7 @@ endif COBJS-y += cmd_pcmcia.o COBJS-$(CONFIG_CMD_PORTIO) += cmd_portio.o COBJS-$(CONFIG_CMD_PXE) += cmd_pxe.o +COBJS-$(CONFIG_CMD_READ) += cmd_read.o COBJS-$(CONFIG_CMD_REGINFO) += cmd_reginfo.o COBJS-$(CONFIG_CMD_REISER) += cmd_reiser.o COBJS-$(CONFIG_CMD_SATA) += cmd_sata.o diff --git a/common/cmd_read.c b/common/cmd_read.c new file mode 100644 index 0000000..f0fc9bf --- /dev/null +++ b/common/cmd_read.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2011 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. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + */ + +#include +#include +#include + +int do_read(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + char *ep; + block_dev_desc_t *dev_desc = NULL; + int dev; + int part = 0; + disk_partition_t part_info; + ulong offset = 0u; + ulong limit = 0u; + void *addr; + uint blk; + uint cnt; + + if (argc != 6) { + cmd_usage(cmdtp); + return 1; + } + + dev = (int)simple_strtoul(argv[2], &ep, 16); + if (*ep) { + if (*ep != ':') { + printf("Invalid block device %s\n", argv[2]); + return 1; + } + part = (int)simple_strtoul(++ep, NULL, 16); + } + + dev_desc = get_dev(argv[1], dev); + if (dev_desc == NULL) { + printf("Block device %s %d not supported\n", argv[1], dev); + return 1; + } + + addr = (void *)simple_strtoul(argv[3], NULL, 16); + blk = simple_strtoul(argv[4], NULL, 16); + cnt = simple_strtoul(argv[5], NULL, 16); + + if (part != 0) { + if (get_partition_info(dev_desc, part, &part_info)) { + printf("Cannot find partition %d\n", part); + return 1; + } + offset = part_info.start; + limit = part_info.size; + } else { + /* Largest address not available in block_dev_desc_t. */ + limit = ~0; + } + + if (cnt + blk > limit) { + printf("Read out of range\n"); + return 1; + } + + if (dev_desc->block_read(dev, offset + blk, cnt, addr) < 0) { + printf("Error reading blocks\n"); + return 1; + } + + return 0; +} + +U_BOOT_CMD( + read, 6, 0, do_read, + "Load binary data from a partition", + " addr blk# cnt" +); diff --git a/include/config_cmd_all.h b/include/config_cmd_all.h index b87967e..148d676 100644 --- a/include/config_cmd_all.h +++ b/include/config_cmd_all.h @@ -71,6 +71,7 @@ #define CONFIG_CMD_REGINFO /* Register dump */ #define CONFIG_CMD_REISER /* Reiserfs support */ #define CONFIG_CMD_RARP /* rarpboot support */ +#define CONFIG_CMD_READ /* Read data from partition */ #define CONFIG_CMD_RUN /* run command in env variable */ #define CONFIG_CMD_SAVEENV /* saveenv */ #define CONFIG_CMD_SAVES /* save S record dump */ -- cgit v0.10.2 From 75b3afc66532abcc7256933b3bcac712574bca2d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 5 Dec 2012 14:46:31 +0000 Subject: Fix use of conditional LMB This code was not guarded with CONFIG_LMB so failed to build on x86. Signed-off-by: Simon Glass diff --git a/common/cmd_bootm.c b/common/cmd_bootm.c index 4dbe952..f7595c0 100644 --- a/common/cmd_bootm.c +++ b/common/cmd_bootm.c @@ -537,7 +537,7 @@ static int do_bootm_subcommand(cmd_tbl_t *cmdtp, int flag, int argc, } break; #endif -#if defined(CONFIG_OF_LIBFDT) +#if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_LMB) case BOOTM_STATE_FDT: { boot_fdt_add_mem_rsv_regions(&images.lmb, -- cgit v0.10.2 From 6a135efcd0f1a2e0f9e5f24fa7a0b578963c55aa Mon Sep 17 00:00:00 2001 From: Vincent Palatin Date: Wed, 5 Dec 2012 14:46:32 +0000 Subject: stdio: remove useless strncpy The name is already copied when we memcpy() the whole structure. This is because struct stdio_dev has this field: char name[16]; /* Device name */ So the data is inline, rather than being a pointer. Signed-off-by: Vincent Palatin Signed-off-by: Simon Glass diff --git a/common/stdio.c b/common/stdio.c index 9f48e5f..97ff9cf 100644 --- a/common/stdio.c +++ b/common/stdio.c @@ -135,7 +135,6 @@ struct stdio_dev* stdio_clone(struct stdio_dev *dev) return NULL; memcpy(_dev, dev, sizeof(struct stdio_dev)); - strncpy(_dev->name, dev->name, 16); return _dev; } -- cgit v0.10.2 From a7d1d765793a05c0d6f7547122339dbecc94f6c3 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 5 Dec 2012 14:46:33 +0000 Subject: sha1: Use const where possible, and unsigned for input len In preparation for making the hash function common, we may as well use const where we can. Also the input length cannot be negative, but may be very large, so use unsigned. Signed-off-by: Simon Glass diff --git a/include/sha1.h b/include/sha1.h index 734d1fb..da09dab 100644 --- a/include/sha1.h +++ b/include/sha1.h @@ -59,7 +59,8 @@ void sha1_starts( sha1_context *ctx ); * \param input buffer holding the data * \param ilen length of the input data */ -void sha1_update( sha1_context *ctx, unsigned char *input, int ilen ); +void sha1_update(sha1_context *ctx, const unsigned char *input, + unsigned int ilen); /** * \brief SHA-1 final digest @@ -76,8 +77,8 @@ void sha1_finish( sha1_context *ctx, unsigned char output[20] ); * \param ilen length of the input data * \param output SHA-1 checksum result */ -void sha1_csum( unsigned char *input, int ilen, - unsigned char output[20] ); +void sha1_csum(const unsigned char *input, unsigned int ilen, + unsigned char *output); /** * \brief Output = SHA-1( input buffer ), with watchdog triggering @@ -87,17 +88,8 @@ void sha1_csum( unsigned char *input, int ilen, * \param output SHA-1 checksum result * \param chunk_sz watchdog triggering period (in bytes of input processed) */ -void sha1_csum_wd (unsigned char *input, int ilen, - unsigned char output[20], unsigned int chunk_sz); - -/** - * \brief Output = SHA-1( file contents ) - * - * \param path input file name - * \param output SHA-1 checksum result - * \return 0 if successful, or 1 if fopen failed - */ -int sha1_file( char *path, unsigned char output[20] ); +void sha1_csum_wd(const unsigned char *input, unsigned int ilen, + unsigned char *output, unsigned int chunk_sz); /** * \brief Output = HMAC-SHA-1( input buffer, hmac key ) @@ -108,9 +100,9 @@ int sha1_file( char *path, unsigned char output[20] ); * \param ilen length of the input data * \param output HMAC-SHA-1 result */ -void sha1_hmac( unsigned char *key, int keylen, - unsigned char *input, int ilen, - unsigned char output[20] ); +void sha1_hmac(const unsigned char *key, int keylen, + const unsigned char *input, unsigned int ilen, + unsigned char *output); /** * \brief Checkup routine diff --git a/lib/sha1.c b/lib/sha1.c index da5bc16..a121224 100644 --- a/lib/sha1.c +++ b/lib/sha1.c @@ -73,7 +73,7 @@ void sha1_starts (sha1_context * ctx) ctx->state[4] = 0xC3D2E1F0; } -static void sha1_process (sha1_context * ctx, unsigned char data[64]) +static void sha1_process(sha1_context *ctx, const unsigned char data[64]) { unsigned long temp, W[16], A, B, C, D, E; @@ -230,7 +230,8 @@ static void sha1_process (sha1_context * ctx, unsigned char data[64]) /* * SHA-1 process buffer */ -void sha1_update (sha1_context * ctx, unsigned char *input, int ilen) +void sha1_update(sha1_context *ctx, const unsigned char *input, + unsigned int ilen) { int fill; unsigned long left; @@ -305,7 +306,8 @@ void sha1_finish (sha1_context * ctx, unsigned char output[20]) /* * Output = SHA-1( input buffer ) */ -void sha1_csum (unsigned char *input, int ilen, unsigned char output[20]) +void sha1_csum(const unsigned char *input, unsigned int ilen, + unsigned char *output) { sha1_context ctx; @@ -318,12 +320,12 @@ void sha1_csum (unsigned char *input, int ilen, unsigned char output[20]) * Output = SHA-1( input buffer ). Trigger the watchdog every 'chunk_sz' * bytes of input processed. */ -void sha1_csum_wd (unsigned char *input, int ilen, unsigned char output[20], - unsigned int chunk_sz) +void sha1_csum_wd(const unsigned char *input, unsigned int ilen, + unsigned char *output, unsigned int chunk_sz) { sha1_context ctx; #if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG) - unsigned char *end, *curr; + const unsigned char *end, *curr; int chunk; #endif @@ -350,8 +352,9 @@ void sha1_csum_wd (unsigned char *input, int ilen, unsigned char output[20], /* * Output = HMAC-SHA-1( input buffer, hmac key ) */ -void sha1_hmac (unsigned char *key, int keylen, - unsigned char *input, int ilen, unsigned char output[20]) +void sha1_hmac(const unsigned char *key, int keylen, + const unsigned char *input, unsigned int ilen, + unsigned char *output) { int i; sha1_context ctx; -- cgit v0.10.2 From ec7381fbf6f77cc465eb06a1ab6f8a99df1e487c Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 5 Dec 2012 14:46:34 +0000 Subject: sha256: Use const where possible and add watchdog function In preparation for making the hash function common, we may as well use const where we can. Also add a watchdog version of the hashing function. Signed-off-by: Simon Glass diff --git a/include/sha256.h b/include/sha256.h index e38ea89..beadab3 100644 --- a/include/sha256.h +++ b/include/sha256.h @@ -3,6 +3,9 @@ #define SHA256_SUM_LEN 32 +/* Reset watchdog each time we process this many bytes */ +#define CHUNKSZ_SHA256 (64 * 1024) + typedef struct { uint32_t total[2]; uint32_t state[8]; @@ -10,7 +13,10 @@ typedef struct { } sha256_context; void sha256_starts(sha256_context * ctx); -void sha256_update(sha256_context * ctx, uint8_t * input, uint32_t length); +void sha256_update(sha256_context *ctx, const uint8_t *input, uint32_t length); void sha256_finish(sha256_context * ctx, uint8_t digest[SHA256_SUM_LEN]); +void sha256_csum_wd(const unsigned char *input, unsigned int ilen, + unsigned char *output, unsigned int chunk_sz); + #endif /* _SHA256_H */ diff --git a/lib/sha256.c b/lib/sha256.c index deb63a4..ab2db48 100644 --- a/lib/sha256.c +++ b/lib/sha256.c @@ -60,7 +60,7 @@ void sha256_starts(sha256_context * ctx) ctx->state[7] = 0x5BE0CD19; } -void sha256_process(sha256_context * ctx, uint8_t data[64]) +static void sha256_process(sha256_context *ctx, const uint8_t data[64]) { uint32_t temp1, temp2; uint32_t W[64]; @@ -191,7 +191,7 @@ void sha256_process(sha256_context * ctx, uint8_t data[64]) ctx->state[7] += H; } -void sha256_update(sha256_context * ctx, uint8_t * input, uint32_t length) +void sha256_update(sha256_context *ctx, const uint8_t *input, uint32_t length) { uint32_t left, fill; @@ -260,3 +260,36 @@ void sha256_finish(sha256_context * ctx, uint8_t digest[32]) PUT_UINT32_BE(ctx->state[6], digest, 24); PUT_UINT32_BE(ctx->state[7], digest, 28); } + +/* + * Output = SHA-256( input buffer ). Trigger the watchdog every 'chunk_sz' + * bytes of input processed. + */ +void sha256_csum_wd(const unsigned char *input, unsigned int ilen, + unsigned char *output, unsigned int chunk_sz) +{ + sha256_context ctx; +#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG) + unsigned char *end, *curr; + int chunk; +#endif + + sha256_starts(&ctx); + +#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG) + curr = input; + end = input + ilen; + while (curr < end) { + chunk = end - curr; + if (chunk > chunk_sz) + chunk = chunk_sz; + sha256_update(&ctx, curr, chunk); + curr += chunk; + WATCHDOG_RESET(); + } +#else + sha256_update(&ctx, input, ilen); +#endif + + sha256_finish(&ctx, output); +} -- cgit v0.10.2 From b1f17bf5ff63a7e22e0299dd576c3b6cd38ae665 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 5 Dec 2012 14:46:35 +0000 Subject: Add strcasecmp() and strncasecmp() strncasecmp() is present as strnicmp() but disabled. Make it available and define strcasecmp() also. There is a only a small performance penalty to having strcasecmp() call strncasecmp(), so do this instead of a standalone function, to save code space. Update the prototype in arch-specific headers as needed to avoid warnings. Signed-off-by: Simon Glass diff --git a/arch/m68k/include/asm/string.h b/arch/m68k/include/asm/string.h index e0773a8..ecf5e56 100644 --- a/arch/m68k/include/asm/string.h +++ b/arch/m68k/include/asm/string.h @@ -16,7 +16,7 @@ #endif extern int strcasecmp(const char *, const char *); -extern int strncasecmp(const char *, const char *, int); +extern int strncasecmp(const char *, const char *, __kernel_size_t); extern char * strcpy(char *,const char *); extern char * strncpy(char *,const char *, __kernel_size_t); extern __kernel_size_t strlen(const char *); diff --git a/arch/powerpc/include/asm/string.h b/arch/powerpc/include/asm/string.h index d912a6b..036805e 100644 --- a/arch/powerpc/include/asm/string.h +++ b/arch/powerpc/include/asm/string.h @@ -14,7 +14,7 @@ #define __HAVE_ARCH_MEMCHR extern int strcasecmp(const char *, const char *); -extern int strncasecmp(const char *, const char *, int); +extern int strncasecmp(const char *, const char *, __kernel_size_t); extern char * strcpy(char *,const char *); extern char * strncpy(char *,const char *, __kernel_size_t); extern __kernel_size_t strlen(const char *); diff --git a/arch/sparc/include/asm/string.h b/arch/sparc/include/asm/string.h index c6bbc20..af6faea 100644 --- a/arch/sparc/include/asm/string.h +++ b/arch/sparc/include/asm/string.h @@ -40,7 +40,7 @@ */ extern int strcasecmp(const char *, const char *); -extern int strncasecmp(const char *, const char *, int); +extern int strncasecmp(const char *, const char *, __kernel_size_t); extern char *strcpy(char *, const char *); extern char *strncpy(char *, const char *, __kernel_size_t); extern __kernel_size_t strlen(const char *); diff --git a/include/linux/string.h b/include/linux/string.h index 9a8cbc2..de83355 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -38,8 +38,11 @@ extern int strcmp(const char *,const char *); #ifndef __HAVE_ARCH_STRNCMP extern int strncmp(const char *,const char *,__kernel_size_t); #endif -#if 0 /* not used - was: #ifndef __HAVE_ARCH_STRNICMP */ -extern int strnicmp(const char *, const char *, __kernel_size_t); +#ifndef __HAVE_ARCH_STRCASECMP +int strcasecmp(const char *s1, const char *s2); +#endif +#ifndef __HAVE_ARCH_STRNCASECMP +extern int strncasecmp(const char *s1, const char *s2, __kernel_size_t len); #endif #ifndef __HAVE_ARCH_STRCHR extern char * strchr(const char *,int); diff --git a/lib/string.c b/lib/string.c index c3ad055..68f60be 100644 --- a/lib/string.c +++ b/lib/string.c @@ -21,14 +21,13 @@ #include -#if 0 /* not used - was: #ifndef __HAVE_ARCH_STRNICMP */ /** - * strnicmp - Case insensitive, length-limited string comparison + * strncasecmp - Case insensitive, length-limited string comparison * @s1: One string * @s2: The other string * @len: the maximum number of characters to compare */ -int strnicmp(const char *s1, const char *s2, size_t len) +int strncasecmp(const char *s1, const char *s2, size_t len) { /* Yes, Virginia, it had better be unsigned */ unsigned char c1, c2; @@ -52,7 +51,16 @@ int strnicmp(const char *s1, const char *s2, size_t len) } return (int)c1 - (int)c2; } -#endif + +/** + * strcasecmp - Case insensitive string comparison + * @s1: One string + * @s2: The other string + */ +int strcasecmp(const char *s1, const char *s2) +{ + return strncasecmp(s1, s2, -1U); +} char * ___strtok; -- cgit v0.10.2 From 460408ef9a14b8f8896d1bc8aa0f702b47c26bb8 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 5 Dec 2012 14:46:36 +0000 Subject: Add generic hash API We have a SHA1 command and want to add a SHA256 command also. Instead of duplicating the code, create a generic hash API which can process commands for different algorithms. Signed-off-by: Simon Glass diff --git a/common/Makefile b/common/Makefile index 07a93d1..068598b 100644 --- a/common/Makefile +++ b/common/Makefile @@ -30,6 +30,7 @@ ifndef CONFIG_SPL_BUILD COBJS-y += main.o COBJS-y += command.o COBJS-y += exports.o +COBJS-y += hash.o COBJS-$(CONFIG_SYS_HUSH_PARSER) += hush.o COBJS-y += s_record.o COBJS-y += xyzModem.o diff --git a/common/hash.c b/common/hash.c new file mode 100644 index 0000000..e3a6e43 --- /dev/null +++ b/common/hash.c @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2012 The Chromium OS Authors. + * + * (C) Copyright 2011 + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * 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 +#include +#include +#include +#include + +/* + * These are the hash algorithms we support. Chips which support accelerated + * crypto could perhaps add named version of these algorithms here. + */ +static struct hash_algo hash_algo[] = { +#ifdef CONFIG_SHA1 + { + "SHA1", + SHA1_SUM_LEN, + sha1_csum_wd, + CHUNKSZ_SHA1, + }, +#endif +#ifdef CONFIG_SHA256 + { + "SHA256", + SHA256_SUM_LEN, + sha256_csum_wd, + CHUNKSZ_SHA256, + }, +#endif +}; + +/** + * store_result: Store the resulting sum to an address or variable + * + * @algo: Hash algorithm being used + * @sum: Hash digest (algo->digest_size bytes) + * @dest: Destination, interpreted as a hex address if it starts + * with * or otherwise as an environment variable. + */ +static void store_result(struct hash_algo *algo, const u8 *sum, + const char *dest) +{ + unsigned int i; + + if (*dest == '*') { + u8 *ptr; + + ptr = (u8 *)simple_strtoul(dest + 1, NULL, 16); + memcpy(ptr, sum, algo->digest_size); + } else { + char str_output[HASH_MAX_DIGEST_SIZE * 2 + 1]; + char *str_ptr = str_output; + + for (i = 0; i < algo->digest_size; i++) { + sprintf(str_ptr, "%02x", sum[i]); + str_ptr += 2; + } + str_ptr = '\0'; + setenv(dest, str_output); + } +} + +/** + * parse_verify_sum: Parse a hash verification parameter + * + * @algo: Hash algorithm being used + * @verify_str: Argument to parse. If it starts with * then it is + * interpreted as a hex address containing the hash. + * If the length is exactly the right number of hex digits + * for the digest size, then we assume it is a hex digest. + * Otherwise we assume it is an environment variable, and + * look up its value (it must contain a hex digest). + * @vsum: Returns binary digest value (algo->digest_size bytes) + * @return 0 if ok, non-zero on error + */ +static int parse_verify_sum(struct hash_algo *algo, char *verify_str, u8 *vsum) +{ + if (*verify_str == '*') { + u8 *ptr; + + ptr = (u8 *)simple_strtoul(verify_str + 1, NULL, 16); + memcpy(vsum, ptr, algo->digest_size); + } else { + unsigned int i; + char *vsum_str; + int digits = algo->digest_size * 2; + + /* + * As with the original code from sha1sum.c, we assume that a + * string which matches the digest size exactly is a hex + * string and not an environment variable. + */ + if (strlen(verify_str) == digits) + vsum_str = verify_str; + else { + vsum_str = getenv(verify_str); + if (vsum_str == NULL || strlen(vsum_str) != digits) { + printf("Expected %d hex digits in env var\n", + digits); + return 1; + } + } + + for (i = 0; i < algo->digest_size; i++) { + char *nullp = vsum_str + (i + 1) * 2; + char end = *nullp; + + *nullp = '\0'; + vsum[i] = simple_strtoul(vsum_str + (i * 2), NULL, 16); + *nullp = end; + } + } + return 0; +} + +static struct hash_algo *find_hash_algo(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hash_algo); i++) { + if (!strcasecmp(name, hash_algo[i].name)) + return &hash_algo[i]; + } + + return NULL; +} + +static void show_hash(struct hash_algo *algo, ulong addr, ulong len, + u8 *output) +{ + int i; + + printf("%s for %08lx ... %08lx ==> ", algo->name, addr, addr + len - 1); + for (i = 0; i < algo->digest_size; i++) + printf("%02x", output[i]); +} + +int hash_command(const char *algo_name, int verify, cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + struct hash_algo *algo; + ulong addr, len; + u8 output[HASH_MAX_DIGEST_SIZE]; + u8 vsum[HASH_MAX_DIGEST_SIZE]; + + if (argc < 2) + return CMD_RET_USAGE; + + algo = find_hash_algo(algo_name); + if (!algo) { + printf("Unknown hash algorithm '%s'\n", algo_name); + return CMD_RET_USAGE; + } + addr = simple_strtoul(*argv++, NULL, 16); + len = simple_strtoul(*argv++, NULL, 16); + argc -= 2; + + if (algo->digest_size > HASH_MAX_DIGEST_SIZE) { + puts("HASH_MAX_DIGEST_SIZE exceeded\n"); + return 1; + } + + algo->hash_func_ws((const unsigned char *)addr, len, output, + algo->chunk_size); + + /* Try to avoid code bloat when verify is not needed */ +#ifdef CONFIG_HASH_VERIFY + if (verify) { +#else + if (0) { +#endif + if (!argc) + return CMD_RET_USAGE; + if (parse_verify_sum(algo, *argv, vsum)) { + printf("ERROR: %s does not contain a valid %s sum\n", + *argv, algo->name); + return 1; + } + if (memcmp(output, vsum, algo->digest_size) != 0) { + int i; + + show_hash(algo, addr, len, output); + printf(" != "); + for (i = 0; i < algo->digest_size; i++) + printf("%02x", vsum[i]); + puts(" ** ERROR **\n"); + return 1; + } + } else { + show_hash(algo, addr, len, output); + printf("\n"); + + if (argc) + store_result(algo, output, *argv); + } + + return 0; +} diff --git a/include/hash.h b/include/hash.h new file mode 100644 index 0000000..34ba558 --- /dev/null +++ b/include/hash.h @@ -0,0 +1,69 @@ +/* + * 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 _HASH_H +#define _HASH_H + +#ifdef CONFIG_SHA1SUM_VERIFY +#define CONFIG_HASH_VERIFY +#endif + +struct hash_algo { + const char *name; /* Name of algorithm */ + int digest_size; /* Length of digest */ + /** + * hash_func_ws: Generic hashing function + * + * This is the generic prototype for a hashing function. We only + * have the watchdog version at present. + * + * @input: Input buffer + * @ilen: Input buffer length + * @output: Checksum result (length depends on algorithm) + * @chunk_sz: Trigger watchdog after processing this many bytes + */ + void (*hash_func_ws)(const unsigned char *input, unsigned int ilen, + unsigned char *output, unsigned int chunk_sz); + int chunk_size; /* Watchdog chunk size */ +}; + +/* + * Maximum digest size for all algorithms we support. Having this value + * avoids a malloc() or C99 local declaration in common/cmd_hash.c. + */ +#define HASH_MAX_DIGEST_SIZE 32 + +/** + * hash_command: Process a hash command for a particular algorithm + * + * This common function is used to implement specific hash commands. + * + * @algo_name: Hash algorithm being used + * @verify: Non-zero to enable verify mode + * @cmdtp: Pointer to command table entry + * @flag: Some flags normally 0 (see CMD_FLAG_.. above) + * @argc: Number of arguments (arg 0 must be the command text) + * @argv: Arguments + */ +int hash_command(const char *algo_name, int verify, cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]); + +#endif -- cgit v0.10.2 From e50b12c95cb12c8e8a5efcc1047747740fcf8e6a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 5 Dec 2012 14:46:37 +0000 Subject: sha1sum: Use generic hash layer Update the code to use the hash layer instead of local code. Signed-off-by: Simon Glass diff --git a/common/cmd_sha1sum.c b/common/cmd_sha1sum.c index 8db5456..fe927ab 100644 --- a/common/cmd_sha1sum.c +++ b/common/cmd_sha1sum.c @@ -26,73 +26,11 @@ #include #include +#include #include -/* - * Store the resulting sum to an address or variable - */ -static void store_result(const u8 *sum, const char *dest) -{ - unsigned int i; - - if (*dest == '*') { - u8 *ptr; - - ptr = (u8 *)simple_strtoul(dest + 1, NULL, 16); - for (i = 0; i < 20; i++) - *ptr++ = sum[i]; - } else { - char str_output[41]; - char *str_ptr = str_output; - - for (i = 0; i < 20; i++) { - sprintf(str_ptr, "%02x", sum[i]); - str_ptr += 2; - } - str_ptr = '\0'; - setenv(dest, str_output); - } -} - -#ifdef CONFIG_SHA1SUM_VERIFY -static int parse_verify_sum(char *verify_str, u8 *vsum) -{ - if (*verify_str == '*') { - u8 *ptr; - - ptr = (u8 *)simple_strtoul(verify_str + 1, NULL, 16); - memcpy(vsum, ptr, 20); - } else { - unsigned int i; - char *vsum_str; - - if (strlen(verify_str) == 40) - vsum_str = verify_str; - else { - vsum_str = getenv(verify_str); - if (vsum_str == NULL || strlen(vsum_str) != 40) - return 1; - } - - for (i = 0; i < 20; i++) { - char *nullp = vsum_str + (i + 1) * 2; - char end = *nullp; - - *nullp = '\0'; - *(u8 *)(vsum + i) = - simple_strtoul(vsum_str + (i * 2), NULL, 16); - *nullp = end; - } - } - return 0; -} - int do_sha1sum(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - ulong addr, len; - unsigned int i; - u8 output[20]; - u8 vsum[20]; int verify = 0; int ac; char * const *av; @@ -102,75 +40,16 @@ int do_sha1sum(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) av = argv + 1; ac = argc - 1; +#ifdef CONFIG_SHA1SUM_VERIFY if (strcmp(*av, "-v") == 0) { verify = 1; av++; ac--; - if (ac < 3) - return CMD_RET_USAGE; } +#endif - addr = simple_strtoul(*av++, NULL, 16); - len = simple_strtoul(*av++, NULL, 16); - - sha1_csum_wd((unsigned char *) addr, len, output, CHUNKSZ_SHA1); - - if (!verify) { - printf("SHA1 for %08lx ... %08lx ==> ", addr, addr + len - 1); - for (i = 0; i < 20; i++) - printf("%02x", output[i]); - printf("\n"); - - if (ac > 2) - store_result(output, *av); - } else { - char *verify_str = *av++; - - if (parse_verify_sum(verify_str, vsum)) { - printf("ERROR: %s does not contain a valid SHA1 sum\n", - verify_str); - return 1; - } - if (memcmp(output, vsum, 20) != 0) { - printf("SHA1 for %08lx ... %08lx ==> ", addr, - addr + len - 1); - for (i = 0; i < 20; i++) - printf("%02x", output[i]); - printf(" != "); - for (i = 0; i < 20; i++) - printf("%02x", vsum[i]); - printf(" ** ERROR **\n"); - return 1; - } - } - - return 0; -} -#else -static int do_sha1sum(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) -{ - unsigned long addr, len; - unsigned int i; - u8 output[20]; - - if (argc < 3) - return CMD_RET_USAGE; - - addr = simple_strtoul(argv[1], NULL, 16); - len = simple_strtoul(argv[2], NULL, 16); - - sha1_csum_wd((unsigned char *) addr, len, output, CHUNKSZ_SHA1); - printf("SHA1 for %08lx ... %08lx ==> ", addr, addr + len - 1); - for (i = 0; i < 20; i++) - printf("%02x", output[i]); - printf("\n"); - - if (argc > 3) - store_result(output, argv[3]); - - return 0; + return hash_command("sha1", verify, cmdtp, flag, ac, av); } -#endif #ifdef CONFIG_SHA1SUM_VERIFY U_BOOT_CMD( -- cgit v0.10.2 From bf36c5d521c17460553e39d82232a51273b83aed Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 5 Dec 2012 14:46:38 +0000 Subject: Add hash command to perform hashing using various algorithms This new command supports hashing SHA1 and SHA256. It could be extended to others such as MD5 and the CRC algorithms. The syntax is modeled on those: hash
[* | ] to calculate a hash, and: hash -v
[* | ] to verify a hash. Use CONFIG_CMD_HASH to enable the command, CONFIG_SHA1 to enable SHA1 and CONFIG_SHA256 to enable SHA256. The existing sha1sum command remains. Signed-off-by: Simon Glass diff --git a/README b/README index 5810ced..4d9dd57 100644 --- a/README +++ b/README @@ -827,6 +827,7 @@ The following options need to be configured: CONFIG_CMD_GETTIME * Get time since boot CONFIG_CMD_GO * the 'go' command (exec code) CONFIG_CMD_GREPENV * search environment + CONFIG_CMD_HASH * calculate hash / digest CONFIG_CMD_HWFLOW * RTS/CTS hw flow control CONFIG_CMD_I2C * I2C serial bus support CONFIG_CMD_IDE * IDE harddisk support @@ -2411,6 +2412,23 @@ CBFS (Coreboot Filesystem) support A better solution is to properly configure the firewall, but sometimes that is not allowed. +- Hashing support: + CONFIG_CMD_HASH + + This enables a generic 'hash' command which can produce + hashes / digests from a few algorithms (e.g. SHA1, SHA256). + + CONFIG_HASH_VERIFY + + Enable the hash verify command (hash -v). This adds to code + size a little. + + CONFIG_SHA1 - support SHA1 hashing + CONFIG_SHA256 - support SHA256 hashing + + Note: There is also a sha1sum command, which should perhaps + be deprecated in favour of 'hash sha1'. + - Show boot progress: CONFIG_SHOW_BOOT_PROGRESS diff --git a/common/Makefile b/common/Makefile index 068598b..ce5ee6e 100644 --- a/common/Makefile +++ b/common/Makefile @@ -104,6 +104,7 @@ COBJS-$(CONFIG_CMD_FS_GENERIC) += cmd_fs.o COBJS-$(CONFIG_CMD_GETTIME) += cmd_gettime.o COBJS-$(CONFIG_CMD_GPIO) += cmd_gpio.o COBJS-$(CONFIG_CMD_I2C) += cmd_i2c.o +COBJS-$(CONFIG_CMD_HASH) += cmd_hash.o COBJS-$(CONFIG_CMD_IDE) += cmd_ide.o COBJS-$(CONFIG_CMD_IMMAP) += cmd_immap.o COBJS-$(CONFIG_CMD_INI) += cmd_ini.o diff --git a/common/cmd_hash.c b/common/cmd_hash.c new file mode 100644 index 0000000..689c608 --- /dev/null +++ b/common/cmd_hash.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2012 The Chromium OS Authors. + * + * (C) Copyright 2011 + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + * + * (C) Copyright 2000 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * 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 +#include +#include + +static int do_hash(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ +#ifdef CONFIG_HASH_VERIFY + int verify = 0; + + if (!strcmp(argv[1], "-v")) { + verify = 1; + argc--; + argv++; + } +#endif + /* Move forward to 'algorithm' parameter */ + argc--; + argv++; + return hash_command(*argv, verify, cmdtp, flag, argc - 1, argv + 1); +} + +#ifdef CONFIG_HASH_VERIFY +U_BOOT_CMD( + hash, 6, 1, do_hash, + "compute hash message digest", + "algorithm address count [[*]sum_dest]\n" + " - compute message digest [save to env var / *address]\n" + "hash -v algorithm address count [*]sum\n" + " - verify hash of memory area with env var / *address" +); +#else +U_BOOT_CMD( + hash, 5, 1, do_hash, + "compute message digest", + "algorithm address count [[*]sum_dest]\n" + " - compute message digest [save to env var / *address]" +); +#endif diff --git a/include/config_cmd_all.h b/include/config_cmd_all.h index 148d676..124d51f 100644 --- a/include/config_cmd_all.h +++ b/include/config_cmd_all.h @@ -41,6 +41,7 @@ #define CONFIG_CMD_FLASH /* flinfo, erase, protect */ #define CONFIG_CMD_FPGA /* FPGA configuration Support */ #define CONFIG_CMD_GETTIME /* Get time since boot */ +#define CONFIG_CMD_HASH /* calculate hash / digest */ #define CONFIG_CMD_HWFLOW /* RTS/CTS hw flow control */ #define CONFIG_CMD_I2C /* I2C serial bus support */ #define CONFIG_CMD_IDE /* IDE harddisk support */ -- cgit v0.10.2 From d46b5f7dcbf294141d6bba1dc8e2b98d742d0562 Mon Sep 17 00:00:00 2001 From: Tom Wai-Hong Tam Date: Wed, 5 Dec 2012 14:46:39 +0000 Subject: edid: Library of EDID decode and print This implements a library for accessing EDID data from an LCD panel. This is used to obtain information about the panel such as its resolution and type. This is a tidied-up version of the original code pulled from https://github.com/ynezz/u-boot-edid. The changes we made are: - removed bit fields in the struct; - removed endianness cases in the struct; - fixed some wrong definitions; - fixed to fit 80 columns; - fixed some code styles. Signed-off-by: Tom Wai-Hong Tam Signed-off-by: Simon Glass diff --git a/common/Makefile b/common/Makefile index ce5ee6e..ebb7635 100644 --- a/common/Makefile +++ b/common/Makefile @@ -188,6 +188,7 @@ COBJS-$(CONFIG_BOOTSTAGE) += bootstage.o COBJS-$(CONFIG_CONSOLE_MUX) += iomux.o COBJS-y += flash.o COBJS-$(CONFIG_CMD_KGDB) += kgdb.o kgdb_stubs.o +COBJS-$(CONFIG_I2C_EDID) += edid.o COBJS-$(CONFIG_KALLSYMS) += kallsyms.o COBJS-$(CONFIG_LCD) += lcd.o COBJS-$(CONFIG_LYNXKDI) += lynxkdi.o diff --git a/common/edid.c b/common/edid.c new file mode 100644 index 0000000..c82c298 --- /dev/null +++ b/common/edid.c @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2012 The Chromium OS Authors. + * + * (C) Copyright 2010 + * Petr Stetiar + * + * 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 + * + * Contains stolen code from ddcprobe project which is: + * Copyright (C) Nalin Dahyabhai + * + */ + +#include +#include +#include +#include + +int edid_check_info(struct edid1_info *edid_info) +{ + if ((edid_info == NULL) || (edid_info->version == 0)) + return -1; + + if (memcmp(edid_info->header, "\x0\xff\xff\xff\xff\xff\xff\x0", 8)) + return -1; + + if (edid_info->version == 0xff && edid_info->revision == 0xff) + return -1; + + return 0; +} + +int edid_get_ranges(struct edid1_info *edid, unsigned int *hmin, + unsigned int *hmax, unsigned int *vmin, + unsigned int *vmax) +{ + int i; + struct edid_monitor_descriptor *monitor; + + *hmin = *hmax = *vmin = *vmax = 0; + if (edid_check_info(edid)) + return -1; + + for (i = 0; i < ARRAY_SIZE(edid->monitor_details.descriptor); i++) { + monitor = &edid->monitor_details.descriptor[i]; + if (monitor->type == EDID_MONITOR_DESCRIPTOR_RANGE) { + *hmin = monitor->data.range_data.horizontal_min; + *hmax = monitor->data.range_data.horizontal_max; + *vmin = monitor->data.range_data.vertical_min; + *vmax = monitor->data.range_data.vertical_max; + return 0; + } + } + return -1; +} + +/** + * Snip the tailing whitespace/return of a string. + * + * @param string The string to be snipped + * @return the snipped string + */ +static char *snip(char *string) +{ + char *s; + + /* + * This is always a 13 character buffer + * and it's not always terminated. + */ + string[12] = '\0'; + s = &string[strlen(string) - 1]; + + while (s >= string && (isspace(*s) || *s == '\n' || *s == '\r' || + *s == '\0')) + *(s--) = '\0'; + + return string; +} + +/** + * Print an EDID monitor descriptor block + * + * @param monitor The EDID monitor descriptor block + * @have_timing Modifies to 1 if the desciptor contains timing info + */ +static void edid_print_dtd(struct edid_monitor_descriptor *monitor, + unsigned int *have_timing) +{ + unsigned char *bytes = (unsigned char *)monitor; + struct edid_detailed_timing *timing = + (struct edid_detailed_timing *)monitor; + + if (bytes[0] == 0 && bytes[1] == 0) { + if (monitor->type == EDID_MONITOR_DESCRIPTOR_SERIAL) + printf("Monitor serial number: %s\n", + snip(monitor->data.string)); + else if (monitor->type == EDID_MONITOR_DESCRIPTOR_ASCII) + printf("Monitor ID: %s\n", + snip(monitor->data.string)); + else if (monitor->type == EDID_MONITOR_DESCRIPTOR_NAME) + printf("Monitor name: %s\n", + snip(monitor->data.string)); + else if (monitor->type == EDID_MONITOR_DESCRIPTOR_RANGE) + printf("Monitor range limits, horizontal sync: " + "%d-%d kHz, vertical refresh: " + "%d-%d Hz, max pixel clock: " + "%d MHz\n", + monitor->data.range_data.horizontal_min, + monitor->data.range_data.horizontal_max, + monitor->data.range_data.vertical_min, + monitor->data.range_data.vertical_max, + monitor->data.range_data.pixel_clock_max * 10); + } else { + uint32_t pixclock, h_active, h_blanking, v_active, v_blanking; + uint32_t h_total, v_total, vfreq; + + pixclock = EDID_DETAILED_TIMING_PIXEL_CLOCK(*timing); + h_active = EDID_DETAILED_TIMING_HORIZONTAL_ACTIVE(*timing); + h_blanking = EDID_DETAILED_TIMING_HORIZONTAL_BLANKING(*timing); + v_active = EDID_DETAILED_TIMING_VERTICAL_ACTIVE(*timing); + v_blanking = EDID_DETAILED_TIMING_VERTICAL_BLANKING(*timing); + + h_total = h_active + h_blanking; + v_total = v_active + v_blanking; + if (v_total * h_total) + vfreq = pixclock / (v_total * h_total); + else + vfreq = 1; /* Error case */ + printf("\t%dx%d\%c\t%d Hz (detailed)\n", h_active, + v_active, h_active > 1000 ? ' ' : '\t', vfreq); + *have_timing = 1; + } +} + +/** + * Get the manufacturer name from an EDID info. + * + * @param edid_info The EDID info to be printed + * @param name Returns the string of the manufacturer name + */ +static void edid_get_manufacturer_name(struct edid1_info *edid, char *name) +{ + name[0] = EDID1_INFO_MANUFACTURER_NAME_CHAR1(*edid) + 'A' - 1; + name[1] = EDID1_INFO_MANUFACTURER_NAME_CHAR2(*edid) + 'A' - 1; + name[2] = EDID1_INFO_MANUFACTURER_NAME_CHAR3(*edid) + 'A' - 1; + name[3] = '\0'; +} + +void edid_print_info(struct edid1_info *edid_info) +{ + int i; + char manufacturer[4]; + unsigned int have_timing = 0; + uint32_t serial_number; + + if (edid_check_info(edid_info)) { + printf("Not a valid EDID\n"); + return; + } + + printf("EDID version: %d.%d\n", + edid_info->version, edid_info->revision); + + printf("Product ID code: %04x\n", EDID1_INFO_PRODUCT_CODE(*edid_info)); + + edid_get_manufacturer_name(edid_info, manufacturer); + printf("Manufacturer: %s\n", manufacturer); + + serial_number = EDID1_INFO_SERIAL_NUMBER(*edid_info); + if (serial_number != 0xffffffff) { + if (strcmp(manufacturer, "MAG") == 0) + serial_number -= 0x7000000; + if (strcmp(manufacturer, "OQI") == 0) + serial_number -= 456150000; + if (strcmp(manufacturer, "VSC") == 0) + serial_number -= 640000000; + } + printf("Serial number: %08x\n", serial_number); + printf("Manufactured in week: %d year: %d\n", + edid_info->week, edid_info->year + 1990); + + printf("Video input definition: %svoltage level %d%s%s%s%s%s\n", + EDID1_INFO_VIDEO_INPUT_DIGITAL(*edid_info) ? + "digital signal, " : "analog signal, ", + EDID1_INFO_VIDEO_INPUT_VOLTAGE_LEVEL(*edid_info), + EDID1_INFO_VIDEO_INPUT_BLANK_TO_BLACK(*edid_info) ? + ", blank to black" : "", + EDID1_INFO_VIDEO_INPUT_SEPARATE_SYNC(*edid_info) ? + ", separate sync" : "", + EDID1_INFO_VIDEO_INPUT_COMPOSITE_SYNC(*edid_info) ? + ", composite sync" : "", + EDID1_INFO_VIDEO_INPUT_SYNC_ON_GREEN(*edid_info) ? + ", sync on green" : "", + EDID1_INFO_VIDEO_INPUT_SERRATION_V(*edid_info) ? + ", serration v" : ""); + + printf("Monitor is %s\n", + EDID1_INFO_FEATURE_RGB(*edid_info) ? "RGB" : "non-RGB"); + + printf("Maximum visible display size: %d cm x %d cm\n", + edid_info->max_size_horizontal, + edid_info->max_size_vertical); + + printf("Power management features: %s%s, %s%s, %s%s\n", + EDID1_INFO_FEATURE_ACTIVE_OFF(*edid_info) ? + "" : "no ", "active off", + EDID1_INFO_FEATURE_SUSPEND(*edid_info) ? "" : "no ", "suspend", + EDID1_INFO_FEATURE_STANDBY(*edid_info) ? "" : "no ", "standby"); + + printf("Estabilished timings:\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_720X400_70(*edid_info)) + printf("\t720x400\t\t70 Hz (VGA 640x400, IBM)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_720X400_88(*edid_info)) + printf("\t720x400\t\t88 Hz (XGA2)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_640X480_60(*edid_info)) + printf("\t640x480\t\t60 Hz (VGA)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_640X480_67(*edid_info)) + printf("\t640x480\t\t67 Hz (Mac II, Apple)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_640X480_72(*edid_info)) + printf("\t640x480\t\t72 Hz (VESA)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_640X480_75(*edid_info)) + printf("\t640x480\t\t75 Hz (VESA)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_800X600_56(*edid_info)) + printf("\t800x600\t\t56 Hz (VESA)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_800X600_60(*edid_info)) + printf("\t800x600\t\t60 Hz (VESA)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_800X600_72(*edid_info)) + printf("\t800x600\t\t72 Hz (VESA)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_800X600_75(*edid_info)) + printf("\t800x600\t\t75 Hz (VESA)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_832X624_75(*edid_info)) + printf("\t832x624\t\t75 Hz (Mac II)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_1024X768_87I(*edid_info)) + printf("\t1024x768\t87 Hz Interlaced (8514A)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_1024X768_60(*edid_info)) + printf("\t1024x768\t60 Hz (VESA)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_1024X768_70(*edid_info)) + printf("\t1024x768\t70 Hz (VESA)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_1024X768_75(*edid_info)) + printf("\t1024x768\t75 Hz (VESA)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_1280X1024_75(*edid_info)) + printf("\t1280x1024\t75 (VESA)\n"); + if (EDID1_INFO_ESTABLISHED_TIMING_1152X870_75(*edid_info)) + printf("\t1152x870\t75 (Mac II)\n"); + + /* Standard timings. */ + printf("Standard timings:\n"); + for (i = 0; i < ARRAY_SIZE(edid_info->standard_timings); i++) { + unsigned int aspect = 10000; + unsigned int x, y; + unsigned char xres, vfreq; + + xres = EDID1_INFO_STANDARD_TIMING_XRESOLUTION(*edid_info, i); + vfreq = EDID1_INFO_STANDARD_TIMING_VFREQ(*edid_info, i); + if ((xres != vfreq) || + ((xres != 0) && (xres != 1)) || + ((vfreq != 0) && (vfreq != 1))) { + switch (EDID1_INFO_STANDARD_TIMING_ASPECT(*edid_info, + i)) { + case ASPECT_625: + aspect = 6250; + break; + case ASPECT_75: + aspect = 7500; + break; + case ASPECT_8: + aspect = 8000; + break; + case ASPECT_5625: + aspect = 5625; + break; + } + x = (xres + 31) * 8; + y = x * aspect / 10000; + printf("\t%dx%d%c\t%d Hz\n", x, y, + x > 1000 ? ' ' : '\t', (vfreq & 0x3f) + 60); + have_timing = 1; + } + } + + /* Detailed timing information. */ + for (i = 0; i < ARRAY_SIZE(edid_info->monitor_details.descriptor); + i++) { + edid_print_dtd(&edid_info->monitor_details.descriptor[i], + &have_timing); + } + + if (!have_timing) + printf("\tNone\n"); +} diff --git a/include/edid.h b/include/edid.h new file mode 100644 index 0000000..4788de9 --- /dev/null +++ b/include/edid.h @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2012 The Chromium OS Authors. + * + * (C) Copyright 2010 + * Petr Stetiar + * + * 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 + * + * Contains stolen code from ddcprobe project which is: + * Copyright (C) Nalin Dahyabhai + * + */ + +#ifndef __EDID_H_ +#define __EDID_H_ + +#include + +#define GET_BIT(_x, _pos) \ + (((_x) >> (_pos)) & 1) +#define GET_BITS(_x, _pos_msb, _pos_lsb) \ + (((_x) >> (_pos_lsb)) & ((1 << ((_pos_msb) - (_pos_lsb) + 1)) - 1)) + +/* Aspect ratios used in EDID info. */ +enum edid_aspect { + ASPECT_625 = 0, + ASPECT_75, + ASPECT_8, + ASPECT_5625, +}; + +/* Detailed timing information used in EDID v1.x */ +struct edid_detailed_timing { + unsigned char pixel_clock[2]; +#define EDID_DETAILED_TIMING_PIXEL_CLOCK(_x) \ + (((((uint32_t)(_x).pixel_clock[1]) << 8) + \ + (_x).pixel_clock[0]) * 10000) + unsigned char horizontal_active; + unsigned char horizontal_blanking; + unsigned char horizontal_active_blanking_hi; +#define EDID_DETAILED_TIMING_HORIZONTAL_ACTIVE(_x) \ + ((GET_BITS((_x).horizontal_active_blanking_hi, 7, 4) << 8) + \ + (_x).horizontal_active) +#define EDID_DETAILED_TIMING_HORIZONTAL_BLANKING(_x) \ + ((GET_BITS((_x).horizontal_active_blanking_hi, 3, 0) << 8) + \ + (_x).horizontal_blanking) + unsigned char vertical_active; + unsigned char vertical_blanking; + unsigned char vertical_active_blanking_hi; +#define EDID_DETAILED_TIMING_VERTICAL_ACTIVE(_x) \ + ((GET_BITS((_x).vertical_active_blanking_hi, 7, 4) << 8) + \ + (_x).vertical_active) +#define EDID_DETAILED_TIMING_VERTICAL_BLANKING(_x) \ + ((GET_BITS((_x).vertical_active_blanking_hi, 3, 0) << 8) + \ + (_x).vertical_blanking) + unsigned char hsync_offset; + unsigned char hsync_pulse_width; + unsigned char sync_offset_pulse_width; + unsigned char hsync_vsync_offset_pulse_width_hi; +#define EDID_DETAILED_TIMING_HSYNC_OFFSET(_x) \ + ((GET_BITS((_x).hsync_vsync_offset_pulse_width_hi, 7, 6) << 8) + \ + (_x).hsync_offset) +#define EDID_DETAILED_TIMING_HSYNC_PULSE_WIDTH(_x) \ + ((GET_BITS((_x).hsync_vsync_offset_pulse_width_hi, 5, 4) << 8) + \ + (_x).hsync_pulse_width) +#define EDID_DETAILED_TIMING_VSYNC_OFFSET(_x) \ + ((GET_BITS((_x).hsync_vsync_offset_pulse_width_hi, 3, 2) << 4) + \ + GET_BITS((_x).vsync_offset_pulse_width, 7, 4)) +#define EDID_DETAILED_TIMING_VSYNC_PULSE_WIDTH(_x) \ + ((GET_BITS((_x).hsync_vsync_offset_pulse_width_hi, 1, 0) << 4) + \ + GET_BITS((_x).vsync_offset_pulse_width, 3, 0)) + unsigned char himage_size; + unsigned char vimage_size; + unsigned char himage_vimage_size_hi; +#define EDID_DETAILED_TIMING_HIMAGE_SIZE(_x) \ + ((GET_BITS((_x).himage_vimage_size_hi, 7, 4) << 8) + (_x).himage_size) +#define EDID_DETAILED_TIMING_VIMAGE_SIZE(_x) \ + ((GET_BITS((_x).himage_vimage_size_hi, 3, 0) << 8) + (_x).vimage_size) + unsigned char hborder; + unsigned char vborder; + unsigned char flags; +#define EDID_DETAILED_TIMING_FLAG_INTERLACED(_x) \ + GET_BIT((_x).flags, 7) +#define EDID_DETAILED_TIMING_FLAG_STEREO(_x) \ + GET_BITS((_x).flags, 6, 5) +#define EDID_DETAILED_TIMING_FLAG_DIGITAL_COMPOSITE(_x) \ + GET_BITS((_x).flags, 4, 3) +#define EDID_DETAILED_TIMING_FLAG_POLARITY(_x) \ + GET_BITS((_x).flags, 2, 1) +#define EDID_DETAILED_TIMING_FLAG_INTERLEAVED(_x) \ + GET_BIT((_x).flags, 0) +} __attribute__ ((__packed__)); + +enum edid_monitor_descriptor_types { + EDID_MONITOR_DESCRIPTOR_SERIAL = 0xff, + EDID_MONITOR_DESCRIPTOR_ASCII = 0xfe, + EDID_MONITOR_DESCRIPTOR_RANGE = 0xfd, + EDID_MONITOR_DESCRIPTOR_NAME = 0xfc, +}; + +struct edid_monitor_descriptor { + uint16_t zero_flag_1; + unsigned char zero_flag_2; + unsigned char type; + unsigned char zero_flag_3; + union { + char string[13]; + struct { + unsigned char vertical_min; + unsigned char vertical_max; + unsigned char horizontal_min; + unsigned char horizontal_max; + unsigned char pixel_clock_max; + unsigned char gtf_data[8]; + } range_data; + } data; +} __attribute__ ((__packed__)); + +struct edid1_info { + unsigned char header[8]; + unsigned char manufacturer_name[2]; +#define EDID1_INFO_MANUFACTURER_NAME_ZERO(_x) \ + GET_BIT(((_x).manufacturer_name[0]), 7) +#define EDID1_INFO_MANUFACTURER_NAME_CHAR1(_x) \ + GET_BITS(((_x).manufacturer_name[0]), 6, 2) +#define EDID1_INFO_MANUFACTURER_NAME_CHAR2(_x) \ + ((GET_BITS(((_x).manufacturer_name[0]), 1, 0) << 3) + \ + GET_BITS(((_x).manufacturer_name[1]), 7, 5)) +#define EDID1_INFO_MANUFACTURER_NAME_CHAR3(_x) \ + GET_BITS(((_x).manufacturer_name[1]), 4, 0) + unsigned char product_code[2]; +#define EDID1_INFO_PRODUCT_CODE(_x) \ + (((uint16_t)(_x).product_code[1] << 8) + (_x).product_code[0]) + unsigned char serial_number[4]; +#define EDID1_INFO_SERIAL_NUMBER(_x) \ + (((uint32_t)(_x).serial_number[3] << 24) + \ + ((_x).serial_number[2] << 16) + ((_x).serial_number[1] << 8) + \ + (_x).serial_number[0]) + unsigned char week; + unsigned char year; + unsigned char version; + unsigned char revision; + unsigned char video_input_definition; +#define EDID1_INFO_VIDEO_INPUT_DIGITAL(_x) \ + GET_BIT(((_x).video_input_definition), 7) +#define EDID1_INFO_VIDEO_INPUT_VOLTAGE_LEVEL(_x) \ + GET_BITS(((_x).video_input_definition), 6, 5) +#define EDID1_INFO_VIDEO_INPUT_BLANK_TO_BLACK(_x) \ + GET_BIT(((_x).video_input_definition), 4) +#define EDID1_INFO_VIDEO_INPUT_SEPARATE_SYNC(_x) \ + GET_BIT(((_x).video_input_definition), 3) +#define EDID1_INFO_VIDEO_INPUT_COMPOSITE_SYNC(_x) \ + GET_BIT(((_x).video_input_definition), 2) +#define EDID1_INFO_VIDEO_INPUT_SYNC_ON_GREEN(_x) \ + GET_BIT(((_x).video_input_definition), 1) +#define EDID1_INFO_VIDEO_INPUT_SERRATION_V(_x) \ + GET_BIT(((_x).video_input_definition), 0) + unsigned char max_size_horizontal; + unsigned char max_size_vertical; + unsigned char gamma; + unsigned char feature_support; +#define EDID1_INFO_FEATURE_STANDBY(_x) \ + GET_BIT(((_x).feature_support), 7) +#define EDID1_INFO_FEATURE_SUSPEND(_x) \ + GET_BIT(((_x).feature_support), 6) +#define EDID1_INFO_FEATURE_ACTIVE_OFF(_x) \ + GET_BIT(((_x).feature_support), 5) +#define EDID1_INFO_FEATURE_DISPLAY_TYPE(_x) \ + GET_BITS(((_x).feature_support), 4, 3) +#define EDID1_INFO_FEATURE_RGB(_x) \ + GET_BIT(((_x).feature_support), 2) +#define EDID1_INFO_FEATURE_PREFERRED_TIMING_MODE(_x) \ + GET_BIT(((_x).feature_support), 1) +#define EDID1_INFO_FEATURE_DEFAULT_GTF_SUPPORT(_x) \ + GET_BIT(((_x).feature_support), 0) + unsigned char color_characteristics[10]; + unsigned char established_timings[3]; +#define EDID1_INFO_ESTABLISHED_TIMING_720X400_70(_x) \ + GET_BIT(((_x).established_timings[0]), 7) +#define EDID1_INFO_ESTABLISHED_TIMING_720X400_88(_x) \ + GET_BIT(((_x).established_timings[0]), 6) +#define EDID1_INFO_ESTABLISHED_TIMING_640X480_60(_x) \ + GET_BIT(((_x).established_timings[0]), 5) +#define EDID1_INFO_ESTABLISHED_TIMING_640X480_67(_x) \ + GET_BIT(((_x).established_timings[0]), 4) +#define EDID1_INFO_ESTABLISHED_TIMING_640X480_72(_x) \ + GET_BIT(((_x).established_timings[0]), 3) +#define EDID1_INFO_ESTABLISHED_TIMING_640X480_75(_x) \ + GET_BIT(((_x).established_timings[0]), 2) +#define EDID1_INFO_ESTABLISHED_TIMING_800X600_56(_x) \ + GET_BIT(((_x).established_timings[0]), 1) +#define EDID1_INFO_ESTABLISHED_TIMING_800X600_60(_x) \ + GET_BIT(((_x).established_timings[0]), 0) +#define EDID1_INFO_ESTABLISHED_TIMING_800X600_72(_x) \ + GET_BIT(((_x).established_timings[1]), 7) +#define EDID1_INFO_ESTABLISHED_TIMING_800X600_75(_x) \ + GET_BIT(((_x).established_timings[1]), 6) +#define EDID1_INFO_ESTABLISHED_TIMING_832X624_75(_x) \ + GET_BIT(((_x).established_timings[1]), 5) +#define EDID1_INFO_ESTABLISHED_TIMING_1024X768_87I(_x) \ + GET_BIT(((_x).established_timings[1]), 4) +#define EDID1_INFO_ESTABLISHED_TIMING_1024X768_60(_x) \ + GET_BIT(((_x).established_timings[1]), 3) +#define EDID1_INFO_ESTABLISHED_TIMING_1024X768_70(_x) \ + GET_BIT(((_x).established_timings[1]), 2) +#define EDID1_INFO_ESTABLISHED_TIMING_1024X768_75(_x) \ + GET_BIT(((_x).established_timings[1]), 1) +#define EDID1_INFO_ESTABLISHED_TIMING_1280X1024_75(_x) \ + GET_BIT(((_x).established_timings[1]), 0) +#define EDID1_INFO_ESTABLISHED_TIMING_1152X870_75(_x) \ + GET_BIT(((_x).established_timings[2]), 7) + struct { + unsigned char xresolution; + unsigned char aspect_vfreq; + } __attribute__((__packed__)) standard_timings[8]; +#define EDID1_INFO_STANDARD_TIMING_XRESOLUTION(_x, _i) \ + (((_x).standard_timings[_i]).xresolution) +#define EDID1_INFO_STANDARD_TIMING_ASPECT(_x, _i) \ + GET_BITS(((_x).standard_timings[_i].aspect_vfreq), 7, 6) +#define EDID1_INFO_STANDARD_TIMING_VFREQ(_x, _i) \ + GET_BITS(((_x).standard_timings[_i].aspect_vfreq), 5, 0) + union { + unsigned char timing[72]; + struct edid_monitor_descriptor descriptor[4]; + } monitor_details; + unsigned char extension_flag; + unsigned char checksum; +} __attribute__ ((__packed__)); + +/** + * Print the EDID info. + * + * @param edid_info The EDID info to be printed + */ +void edid_print_info(struct edid1_info *edid_info); + +/** + * Check the EDID info. + * + * @param info The EDID info to be checked + * @return 0 on valid, or -1 on invalid + */ +int edid_check_info(struct edid1_info *info); + +/** + * Get the horizontal and vertical rate ranges of the monitor. + * + * @param edid The EDID info + * @param hmin Returns the minimum horizontal rate + * @param hmax Returns the maxium horizontal rate + * @param vmin Returns the minimum vertical rate + * @param vmax Returns the maxium vertical rate + * @return 0 on success, or -1 on error + */ +int edid_get_ranges(struct edid1_info *edid, unsigned int *hmin, + unsigned int *hmax, unsigned int *vmin, + unsigned int *vmax); + +#endif /* __EDID_H_ */ -- cgit v0.10.2 From 735987c5a85b7e249903248f2ec85b467afa3de8 Mon Sep 17 00:00:00 2001 From: Tom Wai-Hong Tam Date: Wed, 5 Dec 2012 14:46:40 +0000 Subject: edid: Add I2C command for printing the EDID Add a single command to read the EDID information over I2C. For example: SMDK5250 # i2c dev 7 Setting bus to 7 SMDK5250 # i2c edid 50 EDID version: 1.4 Product ID code: 305c Manufacturer: AUO Serial number: 00000000 Manufactured in week: 0 year: 2011 Video input definition: digital signal, voltage level 0, blank to black Monitor is non-RGB Maximum visible display size: 26 cm x 14 cm Power management features: no active off, no suspend, no standby Estabilished timings: Standard timings: 1366x768 60 Hz (detailed) 1366x768 60 Hz (detailed) Monitor ID: 2VD2K.B116XW Signed-off-by: Tom Wai-Hong Tam Signed-off-by: Sean Paul Signed-off-by: Simon Glass diff --git a/README b/README index 4d9dd57..a80441c 100644 --- a/README +++ b/README @@ -1488,6 +1488,11 @@ CBFS (Coreboot Filesystem) support Support drawing of RLE8-compressed bitmaps on the LCD. + CONFIG_I2C_EDID + + Enables an 'i2c edid' command which can read EDID + information over I2C from an attached LCD display. + - Splash Screen Support: CONFIG_SPLASH_SCREEN diff --git a/common/cmd_i2c.c b/common/cmd_i2c.c index e7df2e4..4380794 100644 --- a/common/cmd_i2c.c +++ b/common/cmd_i2c.c @@ -78,6 +78,7 @@ #include #include +#include #include #include #include @@ -1340,6 +1341,38 @@ static int do_sdram (cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[]) } #endif +/* + * Syntax: + * i2c edid {i2c_chip} + */ +#if defined(CONFIG_I2C_EDID) +int do_edid(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + u_char chip; + struct edid1_info edid; + + if (argc < 2) { + cmd_usage(cmdtp); + return 1; + } + + chip = simple_strtoul(argv[1], NULL, 16); + if (i2c_read(chip, 0, 1, (uchar *)&edid, sizeof(edid)) != 0) { + puts("Error reading EDID content.\n"); + return 1; + } + + if (edid_check_info(&edid)) { + puts("Content isn't valid EDID.\n"); + return 1; + } + + edid_print_info(&edid); + return 0; + +} +#endif /* CONFIG_I2C_EDID */ + #if defined(CONFIG_I2C_MUX) /** * do_i2c_add_bus() - Handle the "i2c bus" command-line command @@ -1487,6 +1520,9 @@ static cmd_tbl_t cmd_i2c_sub[] = { #if defined(CONFIG_I2C_MULTI_BUS) U_BOOT_CMD_MKENT(dev, 1, 1, do_i2c_bus_num, "", ""), #endif /* CONFIG_I2C_MULTI_BUS */ +#if defined(CONFIG_I2C_EDID) + U_BOOT_CMD_MKENT(edid, 1, 1, do_edid, "", ""), +#endif /* CONFIG_I2C_EDID */ U_BOOT_CMD_MKENT(loop, 3, 1, do_i2c_loop, "", ""), U_BOOT_CMD_MKENT(md, 3, 1, do_i2c_md, "", ""), U_BOOT_CMD_MKENT(mm, 2, 1, do_i2c_mm, "", ""), @@ -1547,6 +1583,9 @@ static char i2c_help_text[] = #if defined(CONFIG_I2C_MULTI_BUS) "i2c dev [dev] - show or set current I2C bus\n" #endif /* CONFIG_I2C_MULTI_BUS */ +#if defined(CONFIG_I2C_EDID) + "i2c edid chip - print EDID configuration information\n" +#endif /* CONFIG_I2C_EDID */ "i2c loop chip address[.0, .1, .2] [# of objects] - looping read of device\n" "i2c md chip address[.0, .1, .2] [# of objects] - read from I2C device\n" "i2c mm chip address[.0, .1, .2] - write to I2C device (auto-incrementing)\n" -- cgit v0.10.2 From 096eb3f59b4b9bd002028a632fa02caafd634aa8 Mon Sep 17 00:00:00 2001 From: Tom Wai-Hong Tam Date: Wed, 5 Dec 2012 14:46:41 +0000 Subject: fdt: edid: Enable fdt_add_edid() function when CONFIG_LCD defined This function can be used for LCDs as well as monitors. Signed-off-by: Tom Wai-Hong Tam Signed-off-by: Simon Glass diff --git a/common/fdt_support.c b/common/fdt_support.c index 963ea90..6b9fa05 100644 --- a/common/fdt_support.c +++ b/common/fdt_support.c @@ -1315,7 +1315,7 @@ int fdt_set_status_by_alias(void *fdt, const char* alias, return fdt_set_node_status(fdt, offset, status, error_code); } -#if defined(CONFIG_VIDEO) +#if defined(CONFIG_VIDEO) || defined(CONFIG_LCD) int fdt_add_edid(void *blob, const char *compat, unsigned char *edid_buf) { int noff; -- cgit v0.10.2 From 9ad557be2fc45bb0207cb6cb5de5bf51c9daa689 Mon Sep 17 00:00:00 2001 From: Vadim Bendebury Date: Wed, 5 Dec 2012 14:46:42 +0000 Subject: Add console command to access io space registers Provide u-boot console functions to access IO space registers. A no thrills implementation, accessing one register at a time. For example: boot > iod 80 0080: 00000094 boot > iod.w 80 0080: 0094 boot > iod.b 80 0080: 94 boot > iow.b 0x80 12 boot > iod 0x80 0080: 00000012 Signed-off-by: Vadim Bendebury Signed-off-by: Simon Glass diff --git a/common/Makefile b/common/Makefile index ebb7635..083df17 100644 --- a/common/Makefile +++ b/common/Makefile @@ -120,6 +120,7 @@ COBJS-$(CONFIG_LOGBUFFER) += cmd_log.o COBJS-$(CONFIG_ID_EEPROM) += cmd_mac.o COBJS-$(CONFIG_CMD_MD5SUM) += cmd_md5sum.o COBJS-$(CONFIG_CMD_MEMORY) += cmd_mem.o +COBJS-$(CONFIG_CMD_IO) += cmd_io.o COBJS-$(CONFIG_CMD_MFSL) += cmd_mfsl.o COBJS-$(CONFIG_MII) += miiphyutil.o COBJS-$(CONFIG_CMD_MII) += miiphyutil.o diff --git a/common/cmd_io.c b/common/cmd_io.c new file mode 100644 index 0000000..6450cb5 --- /dev/null +++ b/common/cmd_io.c @@ -0,0 +1,93 @@ +/* + * 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 + */ + +/* + * IO space access commands. + */ + +#include +#include +#include + +/* + * IO Display + * + * Syntax: + * iod{.b, .w, .l} {addr} + */ +int do_io_iod(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + ulong addr; + int size; + + if (argc != 2) + return CMD_RET_USAGE; + + size = cmd_get_data_size(argv[0], 4); + if (size < 0) + return 1; + + addr = simple_strtoul(argv[1], NULL, 16); + + printf("%04x: ", (u16) addr); + + if (size == 4) + printf("%08x\n", inl(addr)); + else if (size == 2) + printf("%04x\n", inw(addr)); + else + printf("%02x\n", inb(addr)); + + return 0; +} + +int do_io_iow(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + ulong addr, size, val; + + if (argc != 3) + return CMD_RET_USAGE; + + size = cmd_get_data_size(argv[0], 4); + if (size < 0) + return 1; + + addr = simple_strtoul(argv[1], NULL, 16); + val = simple_strtoul(argv[2], NULL, 16); + + if (size == 4) + outl((u32) val, addr); + else if (size == 2) + outw((u16) val, addr); + else + outb((u8) val, addr); + + return 0; +} + +/**************************************************/ +U_BOOT_CMD(iod, 2, 0, do_io_iod, + "IO space display", "[.b, .w, .l] address [# of objects]"); + +U_BOOT_CMD(iow, 3, 0, do_io_iow, + "IO space modify (auto-incrementing address)", + "[.b, .w, .l] address"); diff --git a/include/command.h b/include/command.h index 10bc260..476e7cf 100644 --- a/include/command.h +++ b/include/command.h @@ -89,10 +89,10 @@ extern int cmd_auto_complete(const char *const prompt, char *buf, int *np, int * */ #if defined(CONFIG_CMD_MEMORY) \ - || defined(CONFIG_CMD_I2C) \ - || defined(CONFIG_CMD_ITEST) \ - || defined(CONFIG_CMD_PCI) \ - || defined(CONFIG_CMD_PORTIO) + || defined(CONFIG_CMD_I2C) \ + || defined(CONFIG_CMD_ITEST) \ + || defined(CONFIG_CMD_PCI) \ + || defined(CONFIG_CMD_PORTIO) #define CMD_DATA_SIZE extern int cmd_get_data_size(char* arg, int default_size); #endif diff --git a/include/config_cmd_all.h b/include/config_cmd_all.h index 124d51f..e82f642 100644 --- a/include/config_cmd_all.h +++ b/include/config_cmd_all.h @@ -48,6 +48,7 @@ #define CONFIG_CMD_IMI /* iminfo */ #define CONFIG_CMD_IMLS /* List all found images */ #define CONFIG_CMD_IMMAP /* IMMR dump support */ +#define CONFIG_CMD_IO /* Access to X86 IO space */ #define CONFIG_CMD_IRQ /* irqinfo */ #define CONFIG_CMD_ITEST /* Integer (and string) test */ #define CONFIG_CMD_JFFS2 /* JFFS2 Support */ -- cgit v0.10.2 From 78c112c9f9eb3cd06ea53e1c414cabc55560e9cd Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 5 Dec 2012 14:46:43 +0000 Subject: console: Enable function to display console info The CONFIG_SYS_CONSOLE_INFO_QUIET option should suppress the console information, but allow boards to display it later if required. Adjust the code to support this. This is used to avoid printing the information while the LCD display is not ready, since it only becomes ready when stdio init is complete. Signed-off-by: Simon Glass diff --git a/common/console.c b/common/console.c index 1177f7d..25b141a 100644 --- a/common/console.c +++ b/common/console.c @@ -591,7 +591,6 @@ int console_init_f(void) void stdio_print_current_devices(void) { -#ifndef CONFIG_SYS_CONSOLE_INFO_QUIET /* Print information */ puts("In: "); if (stdio_devices[stdin] == NULL) { @@ -613,7 +612,6 @@ void stdio_print_current_devices(void) } else { printf ("%s\n", stdio_devices[stderr]->name); } -#endif /* CONFIG_SYS_CONSOLE_INFO_QUIET */ } #ifdef CONFIG_SYS_CONSOLE_IS_IN_ENV @@ -685,7 +683,9 @@ done: gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */ +#ifndef CONFIG_SYS_CONSOLE_INFO_QUIET stdio_print_current_devices(); +#endif /* CONFIG_SYS_CONSOLE_INFO_QUIET */ #ifdef CONFIG_SYS_CONSOLE_ENV_OVERWRITE /* set the environment variables (will overwrite previous env settings) */ @@ -760,7 +760,9 @@ int console_init_r(void) gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */ +#ifndef CONFIG_SYS_CONSOLE_INFO_QUIET stdio_print_current_devices(); +#endif /* CONFIG_SYS_CONSOLE_INFO_QUIET */ /* Setting environment variables */ for (i = 0; i < 3; i++) { -- cgit v0.10.2 From eea3f4d3e14d21c1a00b332f3b5c8a2b8c8bc386 Mon Sep 17 00:00:00 2001 From: Luigi Semenzato Date: Wed, 5 Dec 2012 14:46:44 +0000 Subject: tpm: Add TPM stress test Add a simple command to stress-test a TPM (Trusted Platform Module). Signed-off-by: Luigi Semenzato Signed-off-by: Simon Glass diff --git a/common/cmd_tpm.c b/common/cmd_tpm.c index 6f5cd48..0970a6f 100644 --- a/common/cmd_tpm.c +++ b/common/cmd_tpm.c @@ -63,19 +63,68 @@ static int tpm_process(int argc, char * const argv[], cmd_tbl_t *cmdtp) return rv; } -static int do_tpm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +#define CHECK(exp) do { \ + int _rv = exp; \ + if (_rv) { \ + printf("CHECK: %s %d %x\n", #exp, __LINE__, _rv);\ + } \ + } while (0) + +static int tpm_process_stress(int repeat_count) { + int i; int rv = 0; + u8 request[] = {0x0, 0xc1, + 0x0, 0x0, 0x0, 0x16, + 0x0, 0x0, 0x0, 0x65, + 0x0, 0x0, 0x0, 0x4, + 0x0, 0x0, 0x0, 0x4, + 0x0, 0x0, 0x1, 0x9}; + u8 response[MAX_TRANSACTION_SIZE]; + u32 rlength = MAX_TRANSACTION_SIZE; + + CHECK(tis_init()); + + for (i = 0; i < repeat_count; i++) { + CHECK(tis_open()); + rv = tis_sendrecv(request, sizeof(request), response, &rlength); + if (rv) { + printf("tpm test failed at step %d with 0x%x\n", i, rv); + CHECK(tis_close()); + break; + } + CHECK(tis_close()); + if ((response[6] || response[7] || response[8] || response[9]) + && response[9] != 0x26) { + /* Ignore postinit errors */ + printf("tpm command failed at step %d\n" + "tpm error code: %02x%02x%02x%02x\n", i, + response[6], response[7], + response[8], response[9]); + rv = -1; + break; + } + } + return rv; +} - /* - * Verify that in case it is present, the first argument, it is - * exactly one character in size. - */ - if (argc < 7) { + +static int do_tpm_many(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[], int repeat_count) + +{ + int rv = 0; + + if (argc < 7 && repeat_count == 0) { puts("command should be at least six bytes in size\n"); return -1; } + if (repeat_count > 0) { + rv = tpm_process_stress(repeat_count); + return rv; + } + if (tis_init()) { puts("tis_init() failed!\n"); return -1; @@ -96,8 +145,40 @@ static int do_tpm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) return rv; } + +static int do_tpm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + return do_tpm_many(cmdtp, flag, argc, argv, 0); +} + + U_BOOT_CMD(tpm, MAX_TRANSACTION_SIZE, 1, do_tpm, " [ ...] - write data and read response", "send arbitrary data (at least 6 bytes) to the TPM " "device and read the response" ); + +static int do_tpm_stress(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + long unsigned int n; + int rv; + + if (argc != 2) { + puts("usage: tpm_stress \n"); + return -1; + } + + rv = strict_strtoul(argv[1], 10, &n); + if (rv) { + puts("tpm_stress: bad count"); + return -1; + } + + return do_tpm_many(cmdtp, flag, argc, argv, n); +} + +U_BOOT_CMD(tpm_stress, 2, 1, do_tpm_stress, + " - stress-test communication with TPM", + "Repeat a TPM transaction (request-response) N times" +); -- cgit v0.10.2 From 23b479b25c11fdb1efed9cfa84c0a6c4ff46051b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 5 Dec 2012 14:46:45 +0000 Subject: exynos: Enable hashing functions and EDID for smdk5250 Enable SHA1/SHA256 hashing and the hash command. Also enable EDID support for reading from an LCD. Signed-off-by: Simon Glass diff --git a/include/configs/smdk5250.h b/include/configs/smdk5250.h index c0f8622..39a347a 100644 --- a/include/configs/smdk5250.h +++ b/include/configs/smdk5250.h @@ -203,6 +203,7 @@ #define CONFIG_I2C_MULTI_BUS #define CONFIG_MAX_I2C_NUM 8 #define CONFIG_SYS_I2C_SLAVE 0x0 +#define CONFIG_I2C_EDID /* Ethernet Controllor Driver */ #ifdef CONFIG_CMD_NET @@ -215,4 +216,10 @@ /* Enable devicetree support */ #define CONFIG_OF_LIBFDT +/* SHA hashing */ +#define CONFIG_CMD_HASH +#define CONFIG_HASH_VERIFY +#define CONFIG_SHA1 +#define CONFIG_SHA256 + #endif /* __CONFIG_H */ -- cgit v0.10.2 From bf3d58bb7dc5dd4a2b15dd20663040ef43c5f795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Majewski?= Date: Thu, 6 Dec 2012 05:23:38 +0000 Subject: cmd:spl:fix: Prevent from a build error on boards, which don't support FDT Do not compile in FDT related code, when it is not supported. Signed-off-by: Lukasz Majewski Signed-off-by: Kyungmin Park Acked-by: Stefano Babic diff --git a/common/cmd_spl.c b/common/cmd_spl.c index 9ec054a..e3c543b 100644 --- a/common/cmd_spl.c +++ b/common/cmd_spl.c @@ -130,10 +130,12 @@ static int spl_export(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) if (call_bootm(argc, argv, subcmd_list[(int)c->cmd])) return -1; switch ((int)c->cmd) { +#ifdef CONFIG_OF_LIBFDT case SPL_EXPORT_FDT: printf("Argument image is now in RAM: 0x%p\n", (void *)images.ft_addr); break; +#endif case SPL_EXPORT_ATAGS: printf("Argument image is now in RAM at: 0x%p\n", (void *)gd->bd->bi_boot_params); -- cgit v0.10.2 From f0c27993e42e801b1bd0c168809ccdbc5e05f9f3 Mon Sep 17 00:00:00 2001 From: Daniel Schwierzeck Date: Wed, 12 Dec 2012 13:13:48 +0100 Subject: MIPS: constify mips_io_port_base mips_io_port_base is exported as 'extern const unsigned long mips_io_port_base;' in arch/mips/include/asm/io.h. Thus make the variable const too. Signed-off-by: Daniel Schwierzeck diff --git a/arch/mips/lib/board.c b/arch/mips/lib/board.c index 7ddd778..4f330cc 100644 --- a/arch/mips/lib/board.c +++ b/arch/mips/lib/board.c @@ -46,7 +46,7 @@ static char *failed = "*** failed ***\n"; * mips_io_port_base is the begin of the address space to which x86 style * I/O ports are mapped. */ -unsigned long mips_io_port_base = -1; +const unsigned long mips_io_port_base = -1; int __board_early_init_f(void) { -- cgit v0.10.2 From f88e09de8d4ce7307c6aaf3a3adff53e85b4b5b3 Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 11 Dec 2012 17:52:50 +0000 Subject: mips: serial: Fix busted manual relocation serial_initialize() must be called after relocation to adjust the pointers to putc(), getc(), etc. This is busted ever since the serial driver-model-ification series. Signed-off-by: Joe Hershberger Signed-off-by: Daniel Schwierzeck diff --git a/arch/mips/lib/board.c b/arch/mips/lib/board.c index 4f330cc..d79e183 100644 --- a/arch/mips/lib/board.c +++ b/arch/mips/lib/board.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -262,6 +263,8 @@ void board_init_r(gd_t *id, ulong dest_addr) monitor_flash_len = (ulong)&uboot_end_data - dest_addr; + serial_initialize(); + #if defined(CONFIG_NEEDS_MANUAL_RELOC) /* * We have to relocate the command table manually -- cgit v0.10.2 From 3ec44111aa33f568493f565285a3a519ef38e1dc Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Tue, 11 Dec 2012 11:09:42 +0100 Subject: vsprintf:fix: Change type returned by ustrtoul The ustrtoul shall convert string defined size (e.g. 1GiB) to unsigned long type (as its name implies). Up till now it had returned int, which might cause problems with large numbers (GiB range), when interpreted as U2 signed numbers. Signed-off-by: Lukasz Majewski Signed-off-by: Kyungmin Park diff --git a/include/exports.h b/include/exports.h index 63aa4b2..6cf31aa 100644 --- a/include/exports.h +++ b/include/exports.h @@ -23,7 +23,7 @@ char *getenv (const char *name); int setenv (const char *varname, const char *varvalue); long simple_strtol(const char *cp,char **endp,unsigned int base); int strcmp(const char * cs,const char * ct); -int ustrtoul(const char *cp, char **endp, unsigned int base); +unsigned long ustrtoul(const char *cp, char **endp, unsigned int base); #if defined(CONFIG_CMD_I2C) int i2c_write (uchar, uint, int , uchar* , int); int i2c_read (uchar, uint, int , uchar* , int); diff --git a/lib/vsprintf.c b/lib/vsprintf.c index b7a79c0..3c432f8 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -103,7 +103,7 @@ long simple_strtol(const char *cp, char **endp, unsigned int base) return simple_strtoul(cp, endp, base); } -int ustrtoul(const char *cp, char **endp, unsigned int base) +unsigned long ustrtoul(const char *cp, char **endp, unsigned int base) { unsigned long result = simple_strtoul(cp, endp, base); switch (**endp) { -- cgit v0.10.2 From 9386f96ca952529378f64b65633e38fdd8708307 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Tue, 11 Dec 2012 11:09:43 +0100 Subject: part:efi: Move part_efi.h file to ./include This move is necessary to export gpt header and GPT partition entries to be used with other commands or subsystems. Additionally the part_efi.h file has been cleaned-up to supress checkpatch's warnings. Signed-off-by: Lukasz Majewski Signed-off-by: Kyungmin Park diff --git a/disk/part_efi.h b/disk/part_efi.h deleted file mode 100644 index 4e28d1d..0000000 --- a/disk/part_efi.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2008 RuggedCom, Inc. - * Richard Retanubun - * - * 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 - */ - -/* - * See also linux/fs/partitions/efi.h - * - * EFI GUID Partition Table - * Per Intel EFI Specification v1.02 - * http://developer.intel.com/technology/efi/efi.htm -*/ - -#ifndef _DISK_PART_EFI_H -#define _DISK_PART_EFI_H - -#define MSDOS_MBR_SIGNATURE 0xAA55 -#define EFI_PMBR_OSTYPE_EFI 0xEF -#define EFI_PMBR_OSTYPE_EFI_GPT 0xEE - -#define GPT_BLOCK_SIZE 512 -#define GPT_HEADER_SIGNATURE 0x5452415020494645ULL -#define GPT_HEADER_REVISION_V1 0x00010000 -#define GPT_PRIMARY_PARTITION_TABLE_LBA 1ULL -#define GPT_ENTRY_NAME "gpt" - -#define EFI_GUID(a,b,c,d0,d1,d2,d3,d4,d5,d6,d7) \ - ((efi_guid_t) \ - {{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ - (b) & 0xff, ((b) >> 8) & 0xff, \ - (c) & 0xff, ((c) >> 8) & 0xff, \ - (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }}) - -#define PARTITION_SYSTEM_GUID \ - EFI_GUID( 0xC12A7328, 0xF81F, 0x11d2, \ - 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B) -#define LEGACY_MBR_PARTITION_GUID \ - EFI_GUID( 0x024DEE41, 0x33E7, 0x11d3, \ - 0x9D, 0x69, 0x00, 0x08, 0xC7, 0x81, 0xF3, 0x9F) -#define PARTITION_MSFT_RESERVED_GUID \ - EFI_GUID( 0xE3C9E316, 0x0B5C, 0x4DB8, \ - 0x81, 0x7D, 0xF9, 0x2D, 0xF0, 0x02, 0x15, 0xAE) -#define PARTITION_BASIC_DATA_GUID \ - EFI_GUID( 0xEBD0A0A2, 0xB9E5, 0x4433, \ - 0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7) -#define PARTITION_LINUX_RAID_GUID \ - EFI_GUID( 0xa19d880f, 0x05fc, 0x4d3b, \ - 0xa0, 0x06, 0x74, 0x3f, 0x0f, 0x84, 0x91, 0x1e) -#define PARTITION_LINUX_SWAP_GUID \ - EFI_GUID( 0x0657fd6d, 0xa4ab, 0x43c4, \ - 0x84, 0xe5, 0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f) -#define PARTITION_LINUX_LVM_GUID \ - EFI_GUID( 0xe6d6d379, 0xf507, 0x44c2, \ - 0xa2, 0x3c, 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28) - -/* linux/include/efi.h */ -typedef unsigned short efi_char16_t; - -typedef struct { - unsigned char b[16]; -} efi_guid_t; - -/* based on linux/include/genhd.h */ -struct partition { - unsigned char boot_ind; /* 0x80 - active */ - unsigned char head; /* starting head */ - unsigned char sector; /* starting sector */ - unsigned char cyl; /* starting cylinder */ - unsigned char sys_ind; /* What partition type */ - unsigned char end_head; /* end head */ - unsigned char end_sector; /* end sector */ - unsigned char end_cyl; /* end cylinder */ - unsigned char start_sect[4]; /* starting sector counting from 0 */ - unsigned char nr_sects[4]; /* nr of sectors in partition */ -} __attribute__ ((packed)); - -/* based on linux/fs/partitions/efi.h */ -typedef struct _gpt_header { - unsigned char signature[8]; - unsigned char revision[4]; - unsigned char header_size[4]; - unsigned char header_crc32[4]; - unsigned char reserved1[4]; - unsigned char my_lba[8]; - unsigned char alternate_lba[8]; - unsigned char first_usable_lba[8]; - unsigned char last_usable_lba[8]; - efi_guid_t disk_guid; - unsigned char partition_entry_lba[8]; - unsigned char num_partition_entries[4]; - unsigned char sizeof_partition_entry[4]; - unsigned char partition_entry_array_crc32[4]; - unsigned char reserved2[GPT_BLOCK_SIZE - 92]; -} __attribute__ ((packed)) gpt_header; - -typedef union _gpt_entry_attributes { - struct { - unsigned long long required_to_function:1; - unsigned long long no_block_io_protocol:1; - unsigned long long legacy_bios_bootable:1; - unsigned long long reserved:45; - unsigned long long type_guid_specific:16; - } fields; - unsigned long long raw; -} __attribute__ ((packed)) gpt_entry_attributes; - -#define PARTNAME_SZ (72 / sizeof(efi_char16_t)) -typedef struct _gpt_entry { - efi_guid_t partition_type_guid; - efi_guid_t unique_partition_guid; - unsigned char starting_lba[8]; - unsigned char ending_lba[8]; - gpt_entry_attributes attributes; - efi_char16_t partition_name[PARTNAME_SZ]; -} -__attribute__ ((packed)) gpt_entry; - -typedef struct _legacy_mbr { - unsigned char boot_code[440]; - unsigned char unique_mbr_signature[4]; - unsigned char unknown[2]; - struct partition partition_record[4]; - unsigned char signature[2]; -} __attribute__ ((packed)) legacy_mbr; - -#endif /* _DISK_PART_EFI_H */ diff --git a/include/part_efi.h b/include/part_efi.h new file mode 100644 index 0000000..4e28d1d --- /dev/null +++ b/include/part_efi.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2008 RuggedCom, Inc. + * Richard Retanubun + * + * 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 + */ + +/* + * See also linux/fs/partitions/efi.h + * + * EFI GUID Partition Table + * Per Intel EFI Specification v1.02 + * http://developer.intel.com/technology/efi/efi.htm +*/ + +#ifndef _DISK_PART_EFI_H +#define _DISK_PART_EFI_H + +#define MSDOS_MBR_SIGNATURE 0xAA55 +#define EFI_PMBR_OSTYPE_EFI 0xEF +#define EFI_PMBR_OSTYPE_EFI_GPT 0xEE + +#define GPT_BLOCK_SIZE 512 +#define GPT_HEADER_SIGNATURE 0x5452415020494645ULL +#define GPT_HEADER_REVISION_V1 0x00010000 +#define GPT_PRIMARY_PARTITION_TABLE_LBA 1ULL +#define GPT_ENTRY_NAME "gpt" + +#define EFI_GUID(a,b,c,d0,d1,d2,d3,d4,d5,d6,d7) \ + ((efi_guid_t) \ + {{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ + (b) & 0xff, ((b) >> 8) & 0xff, \ + (c) & 0xff, ((c) >> 8) & 0xff, \ + (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }}) + +#define PARTITION_SYSTEM_GUID \ + EFI_GUID( 0xC12A7328, 0xF81F, 0x11d2, \ + 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B) +#define LEGACY_MBR_PARTITION_GUID \ + EFI_GUID( 0x024DEE41, 0x33E7, 0x11d3, \ + 0x9D, 0x69, 0x00, 0x08, 0xC7, 0x81, 0xF3, 0x9F) +#define PARTITION_MSFT_RESERVED_GUID \ + EFI_GUID( 0xE3C9E316, 0x0B5C, 0x4DB8, \ + 0x81, 0x7D, 0xF9, 0x2D, 0xF0, 0x02, 0x15, 0xAE) +#define PARTITION_BASIC_DATA_GUID \ + EFI_GUID( 0xEBD0A0A2, 0xB9E5, 0x4433, \ + 0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7) +#define PARTITION_LINUX_RAID_GUID \ + EFI_GUID( 0xa19d880f, 0x05fc, 0x4d3b, \ + 0xa0, 0x06, 0x74, 0x3f, 0x0f, 0x84, 0x91, 0x1e) +#define PARTITION_LINUX_SWAP_GUID \ + EFI_GUID( 0x0657fd6d, 0xa4ab, 0x43c4, \ + 0x84, 0xe5, 0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f) +#define PARTITION_LINUX_LVM_GUID \ + EFI_GUID( 0xe6d6d379, 0xf507, 0x44c2, \ + 0xa2, 0x3c, 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28) + +/* linux/include/efi.h */ +typedef unsigned short efi_char16_t; + +typedef struct { + unsigned char b[16]; +} efi_guid_t; + +/* based on linux/include/genhd.h */ +struct partition { + unsigned char boot_ind; /* 0x80 - active */ + unsigned char head; /* starting head */ + unsigned char sector; /* starting sector */ + unsigned char cyl; /* starting cylinder */ + unsigned char sys_ind; /* What partition type */ + unsigned char end_head; /* end head */ + unsigned char end_sector; /* end sector */ + unsigned char end_cyl; /* end cylinder */ + unsigned char start_sect[4]; /* starting sector counting from 0 */ + unsigned char nr_sects[4]; /* nr of sectors in partition */ +} __attribute__ ((packed)); + +/* based on linux/fs/partitions/efi.h */ +typedef struct _gpt_header { + unsigned char signature[8]; + unsigned char revision[4]; + unsigned char header_size[4]; + unsigned char header_crc32[4]; + unsigned char reserved1[4]; + unsigned char my_lba[8]; + unsigned char alternate_lba[8]; + unsigned char first_usable_lba[8]; + unsigned char last_usable_lba[8]; + efi_guid_t disk_guid; + unsigned char partition_entry_lba[8]; + unsigned char num_partition_entries[4]; + unsigned char sizeof_partition_entry[4]; + unsigned char partition_entry_array_crc32[4]; + unsigned char reserved2[GPT_BLOCK_SIZE - 92]; +} __attribute__ ((packed)) gpt_header; + +typedef union _gpt_entry_attributes { + struct { + unsigned long long required_to_function:1; + unsigned long long no_block_io_protocol:1; + unsigned long long legacy_bios_bootable:1; + unsigned long long reserved:45; + unsigned long long type_guid_specific:16; + } fields; + unsigned long long raw; +} __attribute__ ((packed)) gpt_entry_attributes; + +#define PARTNAME_SZ (72 / sizeof(efi_char16_t)) +typedef struct _gpt_entry { + efi_guid_t partition_type_guid; + efi_guid_t unique_partition_guid; + unsigned char starting_lba[8]; + unsigned char ending_lba[8]; + gpt_entry_attributes attributes; + efi_char16_t partition_name[PARTNAME_SZ]; +} +__attribute__ ((packed)) gpt_entry; + +typedef struct _legacy_mbr { + unsigned char boot_code[440]; + unsigned char unique_mbr_signature[4]; + unsigned char unknown[2]; + struct partition partition_record[4]; + unsigned char signature[2]; +} __attribute__ ((packed)) legacy_mbr; + +#endif /* _DISK_PART_EFI_H */ -- cgit v0.10.2 From 36f2e8e0d1f83d969d07799119d8e9ff149fe02c Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Tue, 11 Dec 2012 11:09:44 +0100 Subject: gpt:doc: GPT (GUID Partition Table) documentation Documentation of the GPT format. Signed-off-by: Lukasz Majewski Signed-off-by: Kyungmin Park diff --git a/doc/README.gpt b/doc/README.gpt new file mode 100644 index 0000000..a9c58b4 --- /dev/null +++ b/doc/README.gpt @@ -0,0 +1,201 @@ +# +# Copyright (C) 2012 Samsung Electronics +# +# Lukasz Majewski +# +# +# 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 + + +Glossary: +======== +- UUID -(Universally Unique Identifier) +- GUID - (Globally Unique ID) +- EFI - (Extensible Firmware Interface) +- UEFI - (Unified EFI) - EFI evolution +- GPT (GUID Partition Table) - it is the EFI standard part +- partitions - lists of available partitions (defined at u-boot): + ./include/configs/{target}.h + +Introduction: +============= +This document describes the GPT partition table format and usage of +the gpt command in u-boot. + + +UUID introduction: +==================== + +GPT for marking disks/partitions is using the UUID. It is supposed to be a +globally unique value. A UUID is a 16-byte (128-bit) number. The number of +theoretically possible UUIDs is therefore about 3 x 10^38. +More often UUID is displayed as 32 hexadecimal digits, in 5 groups, +separated by hyphens, in the form 8-4-4-4-12 for a total of 36 characters +(32 digits and 4 hyphens) + +For instance, GUID of Linux data partition: EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 + +Historically there are 5 methods to generate this number. The oldest one is +combining machine's MAC address and timer (epoch) value. + +Successive versions are using MD5 hash, random numbers and SHA-1 hash. All major +OSes and programming languages are providing libraries to compute UUID (e.g. +uuid command line tool). + +GPT brief explanation: +====================== + + Layout: + ------- + + -------------------------------------------------- + LBA 0 |Protective MBR | + ---------------------------------------------------------- + LBA 1 |Primary GPT Header | Primary + -------------------------------------------------- GPT + LBA 2 |Entry 1|Entry 2| Entry 3| Entry 4| + -------------------------------------------------- + LBA 3 |Entries 5 - 128 | + | | + | | + ---------------------------------------------------------- + LBA 34 |Partition 1 | + | | + ----------------------------------- + |Partition 2 | + | | + ----------------------------------- + |Partition n | + | | + ---------------------------------------------------------- + LBA -34 |Entry 1|Entry 2| Entry 3| Entry 4| Secondary + -------------------------------------------------- (bkp) + LBA -33 |Entries 5 - 128 | GPT + | | + | | + LBA -2 | | + -------------------------------------------------- + LBA -1 |Secondary GPT Header | + ---------------------------------------------------------- + + +For a legacy reasons, GPT's LBA 0 sector has a MBR structure. It is called +"protective MBR". +Its first partition entry ID has 0xEE value, and disk software, which is not +handling the GPT sees it as a storage device without free space. + +It is possible to define 128 linearly placed partition entries. + +"LBA -1" means the last addressable block (in the mmc subsystem: +"dev_desc->lba - 1") + +Primary/Secondary GPT header: +---------------------------- +Offset Size Description + +0 8 B Signature ("EFI PART", 45 46 49 20 50 41 52 54) +8 4 B Revision (For version 1.0, the value is 00 00 01 00) +12 4 B Header size (in bytes, usually 5C 00 00 00 meaning 92 bytes) +16 4 B CRC32 of header (0 to header size), with this field zeroed + during calculation +20 4 B Reserved (ZERO); +24 8 B Current LBA (location of this header copy) +32 8 B Backup LBA (location of the other header copy) +40 8 B First usable LBA for partitions (primary partition table last + LBA + 1) +48 8 B Last usable LBA (secondary partition table first LBA - 1) +56 16 B Disk GUID (also referred as UUID on UNIXes) +72 8 B Partition entries starting LBA (always 2 in primary copy) +80 4 B Number of partition entries +84 4 B Size of a partition entry (usually 128) +88 4 B CRC32 of partition array +92 * Reserved; must be ZERO (420 bytes for a 512-byte LBA) + +TOTAL: 512 B + + + +IMPORTANT: + +GPT headers and partition entries are protected by CRC32 (the POSIX CRC32). + +Primary GPT header and Secondary GPT header have swapped values of "Current LBA" +and "Backup LBA" and therefore different CRC32 check-sum. + +CRC32 for GPT headers (field "CRC of header") are calculated up till +"Header size" (92), NOT 512 bytes. + +CRC32 for partition entries (field "CRC32 of partition array") is calculated for +the whole array entry ( Number_of_partition_entries * +sizeof(partition_entry_size (usually 128))) + +Observe, how Secondary GPT is placed in the memory. It is NOT a mirror reflect +of the Primary. + + + Partition Entry Format: + ---------------------- + Offset Size Description + + 0 16 B Partition type GUID + 16 16 B Unique partition GUID + 32 8 B First LBA (Little Endian) + 40 8 B Last LBA (inclusive) + 48 8 B Attribute flags [+] + 56 72 B Partition name (text) + + Attribute flags: + Bit 0 - System partition + Bit 60 - Read-only + Bit 62 - Hidden + Bit 63 - Not mount + + +Creating GPT partitions in U-Boot: +============== + +To restore GUID partition table one needs to: +1. Define partition layout in the environment. + Format of partitions layout: + "partitions=uuid_disk=...;name=u-boot,size=60MiB,uuid=...; + name=kernel,size=60MiB,uuid=...;" + or + "partitions=uuid_disk=${uuid_gpt_disk};name=${uboot_name}, + size=${uboot_size},uuid=${uboot_uuid};" + + Fields 'name', 'size' and 'uuid' are mandatory for every partition. + The field 'start' is optional. + +2. Define 'CONFIG_EFI_PARTITION' and 'CONFIG_CMD_GPT' + +2. From u-boot prompt type: + gpt write mmc 0 $partitions + + +Useful info: +============ + +Two programs, namely: 'fdisk' and 'parted' are recommended to work with GPT +recovery. Parted is able to handle GUID partitions. Unfortunately the 'fdisk' +hasn't got such ability. +Please, pay attention at -l switch for parted. + +"uuid" program is recommended to generate UUID string. Moreover it can decode +(-d switch) passed in UUID string. It can be used to generate partitions UUID +passed to u-boot environment variables. -- cgit v0.10.2 From fae2bf22a2b1aee85734fc2643ac6ede88cbbd01 Mon Sep 17 00:00:00 2001 From: Chang Hyun Park Date: Tue, 11 Dec 2012 11:09:45 +0100 Subject: gpt: The leXX_to_int() calls replaced with ones defined at Custom definitions of le_XX_to_int functions have been replaced with standard ones, defined at Replacement of several GPT related structures members with ones indicating its endianness and proper size. Signed-off-by: Chang Hyun Park Signed-off-by: Lukasz Majewski Signed-off-by: Kyungmin Park diff --git a/disk/part_efi.c b/disk/part_efi.c index a3873ce..4aa3647 100644 --- a/disk/part_efi.c +++ b/disk/part_efi.c @@ -34,7 +34,7 @@ #include #include #include -#include "part_efi.h" +#include #include #if defined(CONFIG_CMD_IDE) || \ @@ -44,34 +44,6 @@ defined(CONFIG_MMC) || \ defined(CONFIG_SYSTEMACE) -/* Convert char[2] in little endian format to the host format integer - */ -static inline unsigned short le16_to_int(unsigned char *le16) -{ - return ((le16[1] << 8) + le16[0]); -} - -/* Convert char[4] in little endian format to the host format integer - */ -static inline unsigned long le32_to_int(unsigned char *le32) -{ - return ((le32[3] << 24) + (le32[2] << 16) + (le32[1] << 8) + le32[0]); -} - -/* Convert char[8] in little endian format to the host format integer - */ -static inline unsigned long long le64_to_int(unsigned char *le64) -{ - return (((unsigned long long)le64[7] << 56) + - ((unsigned long long)le64[6] << 48) + - ((unsigned long long)le64[5] << 40) + - ((unsigned long long)le64[4] << 32) + - ((unsigned long long)le64[3] << 24) + - ((unsigned long long)le64[2] << 16) + - ((unsigned long long)le64[1] << 8) + - (unsigned long long)le64[0]); -} - /** * efi_crc32() - EFI version of crc32 function * @buf: buffer to calculate crc32 of @@ -79,7 +51,7 @@ static inline unsigned long long le64_to_int(unsigned char *le64) * * Description: Returns EFI-style CRC32 value for @buf */ -static inline unsigned long efi_crc32(const void *buf, unsigned long len) +static inline u32 efi_crc32(const void *buf, u32 len) { return crc32(0, buf, len); } @@ -171,14 +143,14 @@ void print_part_efi(block_dev_desc_t * dev_desc) printf("\tType UUID\n"); printf("\tPartition UUID\n"); - for (i = 0; i < le32_to_int(gpt_head->num_partition_entries); i++) { + for (i = 0; i < le32_to_cpu(gpt_head->num_partition_entries); i++) { /* Stop at the first non valid PTE */ if (!is_pte_valid(&gpt_pte[i])) break; printf("%3d\t0x%08llx\t0x%08llx\t\"%s\"\n", (i + 1), - le64_to_int(gpt_pte[i].starting_lba), - le64_to_int(gpt_pte[i].ending_lba), + le64_to_cpu(gpt_pte[i].starting_lba), + le64_to_cpu(gpt_pte[i].ending_lba), print_efiname(&gpt_pte[i])); printf("\tattrs:\t0x%016llx\n", gpt_pte[i].attributes.raw); uuid_string(gpt_pte[i].partition_type_guid.b, uuid); @@ -211,7 +183,7 @@ int get_partition_info_efi(block_dev_desc_t * dev_desc, int part, return -1; } - if (part > le32_to_int(gpt_head->num_partition_entries) || + if (part > le32_to_cpu(gpt_head->num_partition_entries) || !is_pte_valid(&gpt_pte[part - 1])) { printf("%s: *** ERROR: Invalid partition number %d ***\n", __func__, part); @@ -219,9 +191,9 @@ int get_partition_info_efi(block_dev_desc_t * dev_desc, int part, } /* The ulong casting limits the maximum disk size to 2 TB */ - info->start = (ulong) le64_to_int(gpt_pte[part - 1].starting_lba); + info->start = (u64)le64_to_cpu(gpt_pte[part - 1].starting_lba); /* The ending LBA is inclusive, to calculate size, add 1 to it */ - info->size = ((ulong)le64_to_int(gpt_pte[part - 1].ending_lba) + 1) + info->size = ((u64)le64_to_cpu(gpt_pte[part - 1].ending_lba) + 1) - info->start; info->blksz = GPT_BLOCK_SIZE; @@ -264,7 +236,7 @@ int test_part_efi(block_dev_desc_t * dev_desc) static int pmbr_part_valid(struct partition *part) { if (part->sys_ind == EFI_PMBR_OSTYPE_EFI_GPT && - le32_to_int(part->start_sect) == 1UL) { + le32_to_cpu(part->start_sect) == 1UL) { return 1; } @@ -283,9 +255,8 @@ static int is_pmbr_valid(legacy_mbr * mbr) { int i = 0; - if (!mbr || le16_to_int(mbr->signature) != MSDOS_MBR_SIGNATURE) { + if (!mbr || le16_to_cpu(mbr->signature) != MSDOS_MBR_SIGNATURE) return 0; - } for (i = 0; i < 4; i++) { if (pmbr_part_valid(&mbr->partition_record[i])) { @@ -308,8 +279,8 @@ static int is_pmbr_valid(legacy_mbr * mbr) static int is_gpt_valid(block_dev_desc_t * dev_desc, unsigned long long lba, gpt_header * pgpt_head, gpt_entry ** pgpt_pte) { - unsigned char crc32_backup[4] = { 0 }; - unsigned long calc_crc32; + u32 crc32_backup = 0; + u32 calc_crc32; unsigned long long lastlba; if (!dev_desc || !pgpt_head) { @@ -324,54 +295,54 @@ static int is_gpt_valid(block_dev_desc_t * dev_desc, unsigned long long lba, } /* Check the GPT header signature */ - if (le64_to_int(pgpt_head->signature) != GPT_HEADER_SIGNATURE) { + if (le64_to_cpu(pgpt_head->signature) != GPT_HEADER_SIGNATURE) { printf("GUID Partition Table Header signature is wrong:" "0x%llX != 0x%llX\n", - (unsigned long long)le64_to_int(pgpt_head->signature), - (unsigned long long)GPT_HEADER_SIGNATURE); + le64_to_cpu(pgpt_head->signature), + GPT_HEADER_SIGNATURE); return 0; } /* Check the GUID Partition Table CRC */ - memcpy(crc32_backup, pgpt_head->header_crc32, sizeof(crc32_backup)); - memset(pgpt_head->header_crc32, 0, sizeof(pgpt_head->header_crc32)); + memcpy(&crc32_backup, &pgpt_head->header_crc32, sizeof(crc32_backup)); + memset(&pgpt_head->header_crc32, 0, sizeof(pgpt_head->header_crc32)); calc_crc32 = efi_crc32((const unsigned char *)pgpt_head, - le32_to_int(pgpt_head->header_size)); + le32_to_cpu(pgpt_head->header_size)); - memcpy(pgpt_head->header_crc32, crc32_backup, sizeof(crc32_backup)); + memcpy(&pgpt_head->header_crc32, &crc32_backup, sizeof(crc32_backup)); - if (calc_crc32 != le32_to_int(crc32_backup)) { + if (calc_crc32 != le32_to_cpu(crc32_backup)) { printf("GUID Partition Table Header CRC is wrong:" - "0x%08lX != 0x%08lX\n", - le32_to_int(crc32_backup), calc_crc32); + "0x%x != 0x%x\n", + le32_to_cpu(crc32_backup), calc_crc32); return 0; } /* Check that the my_lba entry points to the LBA that contains the GPT */ - if (le64_to_int(pgpt_head->my_lba) != lba) { + if (le64_to_cpu(pgpt_head->my_lba) != lba) { printf("GPT: my_lba incorrect: %llX != %llX\n", - (unsigned long long)le64_to_int(pgpt_head->my_lba), - (unsigned long long)lba); + le64_to_cpu(pgpt_head->my_lba), + lba); return 0; } /* Check the first_usable_lba and last_usable_lba are within the disk. */ lastlba = (unsigned long long)dev_desc->lba; - if (le64_to_int(pgpt_head->first_usable_lba) > lastlba) { + if (le64_to_cpu(pgpt_head->first_usable_lba) > lastlba) { printf("GPT: first_usable_lba incorrect: %llX > %llX\n", - le64_to_int(pgpt_head->first_usable_lba), lastlba); + le64_to_cpu(pgpt_head->first_usable_lba), lastlba); return 0; } - if (le64_to_int(pgpt_head->last_usable_lba) > lastlba) { + if (le64_to_cpu(pgpt_head->last_usable_lba) > lastlba) { printf("GPT: last_usable_lba incorrect: %llX > %llX\n", - le64_to_int(pgpt_head->last_usable_lba), lastlba); + (u64) le64_to_cpu(pgpt_head->last_usable_lba), lastlba); return 0; } debug("GPT: first_usable_lba: %llX last_usable_lba %llX last lba %llX\n", - le64_to_int(pgpt_head->first_usable_lba), - le64_to_int(pgpt_head->last_usable_lba), lastlba); + le64_to_cpu(pgpt_head->first_usable_lba), + le64_to_cpu(pgpt_head->last_usable_lba), lastlba); /* Read and allocate Partition Table Entries */ *pgpt_pte = alloc_read_gpt_entries(dev_desc, pgpt_head); @@ -382,13 +353,13 @@ static int is_gpt_valid(block_dev_desc_t * dev_desc, unsigned long long lba, /* Check the GUID Partition Table Entry Array CRC */ calc_crc32 = efi_crc32((const unsigned char *)*pgpt_pte, - le32_to_int(pgpt_head->num_partition_entries) * - le32_to_int(pgpt_head->sizeof_partition_entry)); + le32_to_cpu(pgpt_head->num_partition_entries) * + le32_to_cpu(pgpt_head->sizeof_partition_entry)); - if (calc_crc32 != le32_to_int(pgpt_head->partition_entry_array_crc32)) { + if (calc_crc32 != le32_to_cpu(pgpt_head->partition_entry_array_crc32)) { printf("GUID Partition Table Entry Array CRC is wrong:" - "0x%08lX != 0x%08lX\n", - le32_to_int(pgpt_head->partition_entry_array_crc32), + "0x%x != 0x%x\n", + le32_to_cpu(pgpt_head->partition_entry_array_crc32), calc_crc32); free(*pgpt_pte); @@ -419,12 +390,12 @@ static gpt_entry *alloc_read_gpt_entries(block_dev_desc_t * dev_desc, return NULL; } - count = le32_to_int(pgpt_head->num_partition_entries) * - le32_to_int(pgpt_head->sizeof_partition_entry); + count = le32_to_cpu(pgpt_head->num_partition_entries) * + le32_to_cpu(pgpt_head->sizeof_partition_entry); - debug("%s: count = %lu * %lu = %zu\n", __func__, - le32_to_int(pgpt_head->num_partition_entries), - le32_to_int(pgpt_head->sizeof_partition_entry), count); + debug("%s: count = %u * %u = %zu\n", __func__, + (u32) le32_to_cpu(pgpt_head->num_partition_entries), + (u32) le32_to_cpu(pgpt_head->sizeof_partition_entry), count); /* Allocate memory for PTE, remember to FREE */ if (count != 0) { @@ -440,7 +411,7 @@ static gpt_entry *alloc_read_gpt_entries(block_dev_desc_t * dev_desc, /* Read GPT Entries from device */ if (dev_desc->block_read (dev_desc->dev, - (unsigned long)le64_to_int(pgpt_head->partition_entry_lba), + le64_to_cpu(pgpt_head->partition_entry_lba), (lbaint_t) (count / GPT_BLOCK_SIZE), pte) != (count / GPT_BLOCK_SIZE)) { diff --git a/include/part_efi.h b/include/part_efi.h index 4e28d1d..6de0a32 100644 --- a/include/part_efi.h +++ b/include/part_efi.h @@ -29,6 +29,8 @@ * http://developer.intel.com/technology/efi/efi.htm */ +#include + #ifndef _DISK_PART_EFI_H #define _DISK_PART_EFI_H @@ -41,6 +43,8 @@ #define GPT_HEADER_REVISION_V1 0x00010000 #define GPT_PRIMARY_PARTITION_TABLE_LBA 1ULL #define GPT_ENTRY_NAME "gpt" +#define GPT_ENTRY_NUMBERS 128 +#define GPT_ENTRY_SIZE 128 #define EFI_GUID(a,b,c,d0,d1,d2,d3,d4,d5,d6,d7) \ ((efi_guid_t) \ @@ -72,73 +76,72 @@ 0xa2, 0x3c, 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28) /* linux/include/efi.h */ -typedef unsigned short efi_char16_t; +typedef u16 efi_char16_t; typedef struct { - unsigned char b[16]; + u8 b[16]; } efi_guid_t; /* based on linux/include/genhd.h */ struct partition { - unsigned char boot_ind; /* 0x80 - active */ - unsigned char head; /* starting head */ - unsigned char sector; /* starting sector */ - unsigned char cyl; /* starting cylinder */ - unsigned char sys_ind; /* What partition type */ - unsigned char end_head; /* end head */ - unsigned char end_sector; /* end sector */ - unsigned char end_cyl; /* end cylinder */ - unsigned char start_sect[4]; /* starting sector counting from 0 */ - unsigned char nr_sects[4]; /* nr of sectors in partition */ -} __attribute__ ((packed)); + u8 boot_ind; /* 0x80 - active */ + u8 head; /* starting head */ + u8 sector; /* starting sector */ + u8 cyl; /* starting cylinder */ + u8 sys_ind; /* What partition type */ + u8 end_head; /* end head */ + u8 end_sector; /* end sector */ + u8 end_cyl; /* end cylinder */ + __le32 start_sect; /* starting sector counting from 0 */ + __le32 nr_sects; /* nr of sectors in partition */ +} __packed; /* based on linux/fs/partitions/efi.h */ typedef struct _gpt_header { - unsigned char signature[8]; - unsigned char revision[4]; - unsigned char header_size[4]; - unsigned char header_crc32[4]; - unsigned char reserved1[4]; - unsigned char my_lba[8]; - unsigned char alternate_lba[8]; - unsigned char first_usable_lba[8]; - unsigned char last_usable_lba[8]; + __le64 signature; + __le32 revision; + __le32 header_size; + __le32 header_crc32; + __le32 reserved1; + __le64 my_lba; + __le64 alternate_lba; + __le64 first_usable_lba; + __le64 last_usable_lba; efi_guid_t disk_guid; - unsigned char partition_entry_lba[8]; - unsigned char num_partition_entries[4]; - unsigned char sizeof_partition_entry[4]; - unsigned char partition_entry_array_crc32[4]; - unsigned char reserved2[GPT_BLOCK_SIZE - 92]; -} __attribute__ ((packed)) gpt_header; + __le64 partition_entry_lba; + __le32 num_partition_entries; + __le32 sizeof_partition_entry; + __le32 partition_entry_array_crc32; + u8 reserved2[GPT_BLOCK_SIZE - 92]; +} __packed gpt_header; typedef union _gpt_entry_attributes { struct { - unsigned long long required_to_function:1; - unsigned long long no_block_io_protocol:1; - unsigned long long legacy_bios_bootable:1; - unsigned long long reserved:45; - unsigned long long type_guid_specific:16; + u64 required_to_function:1; + u64 no_block_io_protocol:1; + u64 legacy_bios_bootable:1; + u64 reserved:45; + u64 type_guid_specific:16; } fields; unsigned long long raw; -} __attribute__ ((packed)) gpt_entry_attributes; +} __packed gpt_entry_attributes; #define PARTNAME_SZ (72 / sizeof(efi_char16_t)) typedef struct _gpt_entry { efi_guid_t partition_type_guid; efi_guid_t unique_partition_guid; - unsigned char starting_lba[8]; - unsigned char ending_lba[8]; + __le64 starting_lba; + __le64 ending_lba; gpt_entry_attributes attributes; efi_char16_t partition_name[PARTNAME_SZ]; -} -__attribute__ ((packed)) gpt_entry; +} __packed gpt_entry; typedef struct _legacy_mbr { - unsigned char boot_code[440]; - unsigned char unique_mbr_signature[4]; - unsigned char unknown[2]; + u8 boot_code[440]; + __le32 unique_mbr_signature; + __le16 unknown; struct partition partition_record[4]; - unsigned char signature[2]; -} __attribute__ ((packed)) legacy_mbr; + __le16 signature; +} __packed legacy_mbr; #endif /* _DISK_PART_EFI_H */ -- cgit v0.10.2 From 40684ddb83a500f25d813fab7323a190a707402c Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Tue, 11 Dec 2012 11:09:46 +0100 Subject: gpt: Support for GPT (GUID Partition Table) restoration The restoration of GPT table (both primary and secondary) is now possible. Function 'gpt_restore' presents example of partition restoration process. Signed-off-by: Lukasz Majewski Signed-off-by: Piotr Wilczek Signed-off-by: Kyungmin Park diff --git a/disk/part_efi.c b/disk/part_efi.c index 4aa3647..7665017 100644 --- a/disk/part_efi.c +++ b/disk/part_efi.c @@ -37,6 +37,8 @@ #include #include +DECLARE_GLOBAL_DATA_PTR; + #if defined(CONFIG_CMD_IDE) || \ defined(CONFIG_CMD_SATA) || \ defined(CONFIG_CMD_SCSI) || \ @@ -62,13 +64,10 @@ static inline u32 efi_crc32(const void *buf, u32 len) static int pmbr_part_valid(struct partition *part); static int is_pmbr_valid(legacy_mbr * mbr); - static int is_gpt_valid(block_dev_desc_t * dev_desc, unsigned long long lba, gpt_header * pgpt_head, gpt_entry ** pgpt_pte); - static gpt_entry *alloc_read_gpt_entries(block_dev_desc_t * dev_desc, gpt_header * pgpt_head); - static int is_pte_valid(gpt_entry * pte); static char *print_efiname(gpt_entry *pte) @@ -114,6 +113,7 @@ static inline int is_bootable(gpt_entry *p) sizeof(efi_guid_t)); } +#ifdef CONFIG_EFI_PARTITION /* * Public Functions (include/part.h) */ @@ -225,6 +225,281 @@ int test_part_efi(block_dev_desc_t * dev_desc) return 0; } +/** + * set_protective_mbr(): Set the EFI protective MBR + * @param dev_desc - block device descriptor + * + * @return - zero on success, otherwise error + */ +static int set_protective_mbr(block_dev_desc_t *dev_desc) +{ + legacy_mbr *p_mbr; + + /* Setup the Protective MBR */ + p_mbr = calloc(1, sizeof(p_mbr)); + if (p_mbr == NULL) { + printf("%s: calloc failed!\n", __func__); + return -1; + } + /* Append signature */ + p_mbr->signature = MSDOS_MBR_SIGNATURE; + p_mbr->partition_record[0].sys_ind = EFI_PMBR_OSTYPE_EFI_GPT; + p_mbr->partition_record[0].start_sect = 1; + p_mbr->partition_record[0].nr_sects = (u32) dev_desc->lba; + + /* Write MBR sector to the MMC device */ + if (dev_desc->block_write(dev_desc->dev, 0, 1, p_mbr) != 1) { + printf("** Can't write to device %d **\n", + dev_desc->dev); + free(p_mbr); + return -1; + } + + free(p_mbr); + return 0; +} + +/** + * string_uuid(); Convert UUID stored as string to bytes + * + * @param uuid - UUID represented as string + * @param dst - GUID buffer + * + * @return return 0 on successful conversion + */ +static int string_uuid(char *uuid, u8 *dst) +{ + efi_guid_t guid; + u16 b, c, d; + u64 e; + u32 a; + u8 *p; + u8 i; + + const u8 uuid_str_len = 36; + + /* The UUID is written in text: */ + /* 1 9 14 19 24 */ + /* xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx */ + + debug("%s: uuid: %s\n", __func__, uuid); + + if (strlen(uuid) != uuid_str_len) + return -1; + + for (i = 0; i < uuid_str_len; i++) { + if ((i == 8) || (i == 13) || (i == 18) || (i == 23)) { + if (uuid[i] != '-') + return -1; + } else { + if (!isxdigit(uuid[i])) + return -1; + } + } + + a = (u32)simple_strtoul(uuid, NULL, 16); + b = (u16)simple_strtoul(uuid + 9, NULL, 16); + c = (u16)simple_strtoul(uuid + 14, NULL, 16); + d = (u16)simple_strtoul(uuid + 19, NULL, 16); + e = (u64)simple_strtoull(uuid + 24, NULL, 16); + + p = (u8 *) &e; + guid = EFI_GUID(a, b, c, d >> 8, d & 0xFF, + *(p + 5), *(p + 4), *(p + 3), + *(p + 2), *(p + 1) , *p); + + memcpy(dst, guid.b, sizeof(efi_guid_t)); + + return 0; +} + +int write_gpt_table(block_dev_desc_t *dev_desc, + gpt_header *gpt_h, gpt_entry *gpt_e) +{ + const int pte_blk_num = (gpt_h->num_partition_entries + * sizeof(gpt_entry)) / dev_desc->blksz; + + u32 calc_crc32; + u64 val; + + debug("max lba: %x\n", (u32) dev_desc->lba); + /* Setup the Protective MBR */ + if (set_protective_mbr(dev_desc) < 0) + goto err; + + /* Generate CRC for the Primary GPT Header */ + calc_crc32 = efi_crc32((const unsigned char *)gpt_e, + le32_to_cpu(gpt_h->num_partition_entries) * + le32_to_cpu(gpt_h->sizeof_partition_entry)); + gpt_h->partition_entry_array_crc32 = cpu_to_le32(calc_crc32); + + calc_crc32 = efi_crc32((const unsigned char *)gpt_h, + le32_to_cpu(gpt_h->header_size)); + gpt_h->header_crc32 = cpu_to_le32(calc_crc32); + + /* Write the First GPT to the block right after the Legacy MBR */ + if (dev_desc->block_write(dev_desc->dev, 1, 1, gpt_h) != 1) + goto err; + + if (dev_desc->block_write(dev_desc->dev, 2, pte_blk_num, gpt_e) + != pte_blk_num) + goto err; + + /* recalculate the values for the Second GPT Header */ + val = le64_to_cpu(gpt_h->my_lba); + gpt_h->my_lba = gpt_h->alternate_lba; + gpt_h->alternate_lba = cpu_to_le64(val); + gpt_h->header_crc32 = 0; + + calc_crc32 = efi_crc32((const unsigned char *)gpt_h, + le32_to_cpu(gpt_h->header_size)); + gpt_h->header_crc32 = cpu_to_le32(calc_crc32); + + if (dev_desc->block_write(dev_desc->dev, + le32_to_cpu(gpt_h->last_usable_lba + 1), + pte_blk_num, gpt_e) != pte_blk_num) + goto err; + + if (dev_desc->block_write(dev_desc->dev, + le32_to_cpu(gpt_h->my_lba), 1, gpt_h) != 1) + goto err; + + debug("GPT successfully written to block device!\n"); + return 0; + + err: + printf("** Can't write to device %d **\n", dev_desc->dev); + return -1; +} + +int gpt_fill_pte(gpt_header *gpt_h, gpt_entry *gpt_e, + disk_partition_t *partitions, int parts) +{ + u32 offset = (u32)le32_to_cpu(gpt_h->first_usable_lba); + ulong start; + int i, k; + size_t name_len; +#ifdef CONFIG_PARTITION_UUIDS + char *str_uuid; +#endif + + for (i = 0; i < parts; i++) { + /* partition starting lba */ + start = partitions[i].start; + if (start && (start < offset)) { + printf("Partition overlap\n"); + return -1; + } + if (start) { + gpt_e[i].starting_lba = cpu_to_le64(start); + offset = start + partitions[i].size; + } else { + gpt_e[i].starting_lba = cpu_to_le64(offset); + offset += partitions[i].size; + } + if (offset >= gpt_h->last_usable_lba) { + printf("Partitions layout exceds disk size\n"); + return -1; + } + /* partition ending lba */ + if ((i == parts - 1) && (partitions[i].size == 0)) + /* extend the last partition to maximuim */ + gpt_e[i].ending_lba = gpt_h->last_usable_lba; + else + gpt_e[i].ending_lba = cpu_to_le64(offset - 1); + + /* partition type GUID */ + memcpy(gpt_e[i].partition_type_guid.b, + &PARTITION_BASIC_DATA_GUID, 16); + +#ifdef CONFIG_PARTITION_UUIDS + str_uuid = partitions[i].uuid; + if (string_uuid(str_uuid, gpt_e[i].unique_partition_guid.b)) { + printf("Partition no. %d: invalid guid: %s\n", + i, str_uuid); + return -1; + } +#endif + + /* partition attributes */ + memset(&gpt_e[i].attributes, 0, + sizeof(gpt_entry_attributes)); + + /* partition name */ + name_len = sizeof(gpt_e[i].partition_name) + / sizeof(efi_char16_t); + for (k = 0; k < name_len; k++) + gpt_e[i].partition_name[k] = + (efi_char16_t)(partitions[i].name[k]); + + debug("%s: name: %s offset[%d]: 0x%x size[%d]: 0x%lx\n", + __func__, partitions[i].name, i, + offset, i, partitions[i].size); + } + + return 0; +} + +int gpt_fill_header(block_dev_desc_t *dev_desc, gpt_header *gpt_h, + char *str_guid, int parts_count) +{ + gpt_h->signature = cpu_to_le64(GPT_HEADER_SIGNATURE); + gpt_h->revision = cpu_to_le32(GPT_HEADER_REVISION_V1); + gpt_h->header_size = cpu_to_le32(sizeof(gpt_header)); + gpt_h->my_lba = cpu_to_le64(1); + gpt_h->alternate_lba = cpu_to_le64(dev_desc->lba - 1); + gpt_h->first_usable_lba = cpu_to_le64(34); + gpt_h->last_usable_lba = cpu_to_le64(dev_desc->lba - 34); + gpt_h->partition_entry_lba = cpu_to_le64(2); + gpt_h->num_partition_entries = cpu_to_le32(GPT_ENTRY_NUMBERS); + gpt_h->sizeof_partition_entry = cpu_to_le32(sizeof(gpt_entry)); + gpt_h->header_crc32 = 0; + gpt_h->partition_entry_array_crc32 = 0; + + if (string_uuid(str_guid, gpt_h->disk_guid.b)) + return -1; + + return 0; +} + +int gpt_restore(block_dev_desc_t *dev_desc, char *str_disk_guid, + disk_partition_t *partitions, int parts_count) +{ + int ret; + + gpt_header *gpt_h = calloc(1, sizeof(gpt_header)); + if (gpt_h == NULL) { + printf("%s: calloc failed!\n", __func__); + return -1; + } + + gpt_entry *gpt_e = calloc(GPT_ENTRY_NUMBERS, sizeof(gpt_entry)); + if (gpt_e == NULL) { + printf("%s: calloc failed!\n", __func__); + free(gpt_h); + return -1; + } + + /* Generate Primary GPT header (LBA1) */ + ret = gpt_fill_header(dev_desc, gpt_h, str_disk_guid, parts_count); + if (ret) + goto err; + + /* Generate partition entries */ + ret = gpt_fill_pte(gpt_h, gpt_e, partitions, parts_count); + if (ret) + goto err; + + /* Write GPT partition table */ + ret = write_gpt_table(dev_desc, gpt_h, gpt_e); + +err: + free(gpt_e); + free(gpt_h); + return ret; +} +#endif + /* * Private functions */ diff --git a/include/part.h b/include/part.h index 27ea283..c58a734 100644 --- a/include/part.h +++ b/include/part.h @@ -176,10 +176,62 @@ int test_part_amiga (block_dev_desc_t *dev_desc); #endif #ifdef CONFIG_EFI_PARTITION +#include /* disk/part_efi.c */ int get_partition_info_efi (block_dev_desc_t * dev_desc, int part, disk_partition_t *info); void print_part_efi (block_dev_desc_t *dev_desc); int test_part_efi (block_dev_desc_t *dev_desc); + +/** + * write_gpt_table() - Write the GUID Partition Table to disk + * + * @param dev_desc - block device descriptor + * @param gpt_h - pointer to GPT header representation + * @param gpt_e - pointer to GPT partition table entries + * + * @return - zero on success, otherwise error + */ +int write_gpt_table(block_dev_desc_t *dev_desc, + gpt_header *gpt_h, gpt_entry *gpt_e); + +/** + * gpt_fill_pte(): Fill the GPT partition table entry + * + * @param gpt_h - GPT header representation + * @param gpt_e - GPT partition table entries + * @param partitions - list of partitions + * @param parts - number of partitions + * + * @return zero on success + */ +int gpt_fill_pte(gpt_header *gpt_h, gpt_entry *gpt_e, + disk_partition_t *partitions, int parts); + +/** + * gpt_fill_header(): Fill the GPT header + * + * @param dev_desc - block device descriptor + * @param gpt_h - GPT header representation + * @param str_guid - disk guid string representation + * @param parts_count - number of partitions + * + * @return - error on str_guid conversion error + */ +int gpt_fill_header(block_dev_desc_t *dev_desc, gpt_header *gpt_h, + char *str_guid, int parts_count); + +/** + * gpt_restore(): Restore GPT partition table + * + * @param dev_desc - block device descriptor + * @param str_disk_guid - disk GUID + * @param partitions - list of partitions + * @param parts - number of partitions + * + * @return zero on success + */ +int gpt_restore(block_dev_desc_t *dev_desc, char *str_disk_guid, + disk_partition_t *partitions, const int parts_count); #endif #endif /* _PART_H */ -- cgit v0.10.2 From 8b096237667d0993c6900bd10068a25ce368fe16 Mon Sep 17 00:00:00 2001 From: Piotr Wilczek Date: Tue, 11 Dec 2012 11:09:47 +0100 Subject: gpt: Support for new "gpt" command New command - "gpt" is supported. It restores the GPT partition table. It looks into the given environment variable for partitions definition. It can be enabled at target configuration file with CONFIG_CMD_GPT. Signed-off-by: Lukasz Majewski Signed-off-by: Piotr Wilczek Signed-off-by: Kyungmin Park diff --git a/common/Makefile b/common/Makefile index 083df17..ce30359 100644 --- a/common/Makefile +++ b/common/Makefile @@ -198,6 +198,7 @@ COBJS-$(CONFIG_MODEM_SUPPORT) += modem.o COBJS-$(CONFIG_UPDATE_TFTP) += update.o COBJS-$(CONFIG_USB_KEYBOARD) += usb_kbd.o COBJS-$(CONFIG_CMD_DFU) += cmd_dfu.o +COBJS-$(CONFIG_CMD_GPT) += cmd_gpt.o endif ifdef CONFIG_SPL_BUILD diff --git a/common/cmd_gpt.c b/common/cmd_gpt.c new file mode 100644 index 0000000..da7705d --- /dev/null +++ b/common/cmd_gpt.c @@ -0,0 +1,333 @@ +/* + * cmd_gpt.c -- GPT (GUID Partition Table) handling command + * + * Copyright (C) 2012 Samsung Electronics + * author: Lukasz Majewski + * author: Piotr Wilczek + * + * 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 +#include +#include +#include +#include +#include +#include + +#ifndef CONFIG_PARTITION_UUIDS +#error CONFIG_PARTITION_UUIDS must be enabled for CONFIG_CMD_GPT to be enabled +#endif + +/** + * extract_env(): Expand env name from string format '&{env_name}' + * and return pointer to the env (if the env is set) + * + * @param str - pointer to string + * @param env - pointer to pointer to extracted env + * + * @return - zero on successful expand and env is set + */ +static char extract_env(const char *str, char **env) +{ + char *e, *s; + + if (!str || strlen(str) < 4) + return -1; + + if ((strncmp(str, "${", 2) == 0) && (str[strlen(str) - 1] == '}')) { + s = strdup(str); + if (s == NULL) + return -1; + memset(s + strlen(s) - 1, '\0', 1); + memmove(s, s + 2, strlen(s) - 1); + e = getenv(s); + free(s); + if (e == NULL) { + printf("Environmental '%s' not set\n", str); + return -1; /* env not set */ + } + *env = e; + return 0; + } + + return -1; +} + +/** + * extract_val(): Extract value from a key=value pair list (comma separated). + * Only value for the given key is returend. + * Function allocates memory for the value, remember to free! + * + * @param str - pointer to string with key=values pairs + * @param key - pointer to the key to search for + * + * @return - pointer to allocated string with the value + */ +static char *extract_val(const char *str, const char *key) +{ + char *v, *k; + char *s, *strcopy; + char *new = NULL; + + strcopy = strdup(str); + if (strcopy == NULL) + return NULL; + + s = strcopy; + while (s) { + v = strsep(&s, ","); + if (!v) + break; + k = strsep(&v, "="); + if (!k) + break; + if (strcmp(k, key) == 0) { + new = strdup(v); + break; + } + } + + free(strcopy); + + return new; +} + +/** + * set_gpt_info(): Fill partition information from string + * function allocates memory, remember to free! + * + * @param dev_desc - pointer block device descriptor + * @param str_part - pointer to string with partition information + * @param str_disk_guid - pointer to pointer to allocated string with disk guid + * @param partitions - pointer to pointer to allocated partitions array + * @param parts_count - number of partitions + * + * @return - zero on success, otherwise error + * + */ +static int set_gpt_info(block_dev_desc_t *dev_desc, + const char *str_part, + char **str_disk_guid, + disk_partition_t **partitions, + u8 *parts_count) +{ + char *tok, *str, *s; + int i; + char *val, *p; + int p_count; + disk_partition_t *parts; + int errno = 0; + + debug("%s: MMC lba num: 0x%x %d\n", __func__, + (unsigned int)dev_desc->lba, (unsigned int)dev_desc->lba); + + if (str_part == NULL) + return -1; + + str = strdup(str_part); + + /* extract disk guid */ + s = str; + tok = strsep(&s, ";"); + val = extract_val(tok, "uuid_disk"); + if (!val) { + free(str); + return -2; + } + if (extract_env(val, &p)) + p = val; + *str_disk_guid = strdup(p); + free(val); + + if (strlen(s) == 0) + return -3; + + i = strlen(s) - 1; + if (s[i] == ';') + s[i] = '\0'; + + /* calculate expected number of partitions */ + p_count = 1; + p = s; + while (*p) { + if (*p++ == ';') + p_count++; + } + + /* allocate memory for partitions */ + parts = calloc(sizeof(disk_partition_t), p_count); + + /* retrive partions data from string */ + for (i = 0; i < p_count; i++) { + tok = strsep(&s, ";"); + + if (tok == NULL) + break; + + /* uuid */ + val = extract_val(tok, "uuid"); + if (!val) { /* 'uuid' is mandatory */ + errno = -4; + goto err; + } + if (extract_env(val, &p)) + p = val; + if (strlen(p) >= sizeof(parts[i].uuid)) { + printf("Wrong uuid format for partition %d\n", i); + errno = -4; + goto err; + } + strcpy((char *)parts[i].uuid, p); + free(val); + + /* name */ + val = extract_val(tok, "name"); + if (!val) { /* name is mandatory */ + errno = -4; + goto err; + } + if (extract_env(val, &p)) + p = val; + if (strlen(p) >= sizeof(parts[i].name)) { + errno = -4; + goto err; + } + strcpy((char *)parts[i].name, p); + free(val); + + /* size */ + val = extract_val(tok, "size"); + if (!val) { /* 'size' is mandatory */ + errno = -4; + goto err; + } + if (extract_env(val, &p)) + p = val; + parts[i].size = ustrtoul(p, &p, 0); + parts[i].size /= dev_desc->blksz; + free(val); + + /* start address */ + val = extract_val(tok, "start"); + if (val) { /* start address is optional */ + if (extract_env(val, &p)) + p = val; + parts[i].start = ustrtoul(p, &p, 0); + parts[i].start /= dev_desc->blksz; + free(val); + } + } + + *parts_count = p_count; + *partitions = parts; + free(str); + + return 0; +err: + free(str); + free(*str_disk_guid); + free(parts); + + return errno; +} + +static int gpt_mmc_default(int dev, const char *str_part) +{ + int ret; + char *str_disk_guid; + u8 part_count = 0; + disk_partition_t *partitions = NULL; + + struct mmc *mmc = find_mmc_device(dev); + + if (mmc == NULL) { + printf("%s: mmc dev %d NOT available\n", __func__, dev); + return CMD_RET_FAILURE; + } + + if (!str_part) + return -1; + + /* fill partitions */ + ret = set_gpt_info(&mmc->block_dev, str_part, + &str_disk_guid, &partitions, &part_count); + if (ret) { + if (ret == -1) + printf("No partition list provided\n"); + if (ret == -2) + printf("Missing disk guid\n"); + if ((ret == -3) || (ret == -4)) + printf("Partition list incomplete\n"); + return -1; + } + + /* save partitions layout to disk */ + gpt_restore(&mmc->block_dev, str_disk_guid, partitions, part_count); + free(str_disk_guid); + free(partitions); + + return 0; +} + +/** + * do_gpt(): Perform GPT operations + * + * @param cmdtp - command name + * @param flag + * @param argc + * @param argv + * + * @return zero on success; otherwise error + */ +static int do_gpt(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int ret = CMD_RET_SUCCESS; + int dev = 0; + char *pstr; + + if (argc < 5) + return CMD_RET_USAGE; + + /* command: 'write' */ + if ((strcmp(argv[1], "write") == 0) && (argc == 5)) { + /* device: 'mmc' */ + if (strcmp(argv[2], "mmc") == 0) { + /* check if 'dev' is a number */ + for (pstr = argv[3]; *pstr != '\0'; pstr++) + if (!isdigit(*pstr)) { + printf("'%s' is not a number\n", + argv[3]); + return CMD_RET_USAGE; + } + dev = (int)simple_strtoul(argv[3], NULL, 10); + /* write to mmc */ + if (gpt_mmc_default(dev, argv[4])) + return CMD_RET_FAILURE; + } + } else { + return CMD_RET_USAGE; + } + return ret; +} + +U_BOOT_CMD(gpt, CONFIG_SYS_MAXARGS, 1, do_gpt, + "GUID Partition Table", + " \n" + " - GUID partition table restoration\n" + " Restore GPT information on a device connected\n" + " to interface\n" +); -- cgit v0.10.2 From 9960d9a8bccebc8418857c15de94a7bc23049573 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Tue, 11 Dec 2012 11:09:48 +0100 Subject: gpt: Enable support for GPT partition table restoration at Samsung's Trats Enable support for GPT partition table restoration at Samsung's Trats development board. Signed-off-by: Lukasz Majewski Signed-off-by: Kyungmin Park CC: Minkyu Kang diff --git a/include/configs/trats.h b/include/configs/trats.h index 355029e..94ba55e 100644 --- a/include/configs/trats.h +++ b/include/configs/trats.h @@ -98,6 +98,7 @@ #undef CONFIG_CMD_MTDPARTS #define CONFIG_CMD_MMC #define CONFIG_CMD_DFU +#define CONFIG_CMD_GPT /* FAT */ #define CONFIG_CMD_FAT @@ -122,6 +123,26 @@ #define CONFIG_BOOTBLOCK "10" #define CONFIG_ENV_COMMON_BOOT "${console} ${meminfo}" +/* Tizen - partitions definitions */ +#define PARTS_CSA "csa-mmc" +#define PARTS_BOOTLOADER "u-boot" +#define PARTS_BOOT "boot" +#define PARTS_ROOT "platform" +#define PARTS_DATA "data" +#define PARTS_CSC "csc" +#define PARTS_UMS "ums" + +#define PARTS_DEFAULT \ + "uuid_disk=${uuid_gpt_disk};" \ + "name="PARTS_CSA",size=8MiB,uuid=${uuid_gpt_"PARTS_CSA"};" \ + "name="PARTS_BOOTLOADER",size=60MiB," \ + "uuid=${uuid_gpt_"PARTS_BOOTLOADER"};" \ + "name="PARTS_BOOT",size=100MiB,uuid=${uuid_gpt_"PARTS_BOOT"};" \ + "name="PARTS_ROOT",size=1GiB,uuid=${uuid_gpt_"PARTS_ROOT"};" \ + "name="PARTS_DATA",size=3GiB,uuid=${uuid_gpt_"PARTS_DATA"};" \ + "name="PARTS_CSC",size=150MiB,uuid=${uuid_gpt_"PARTS_CSC"};" \ + "name="PARTS_UMS",size=-,uuid=${uuid_gpt_"PARTS_UMS"}\0" \ + #define CONFIG_DFU_ALT \ "dfu_alt_info=" \ "u-boot mmc 80 400;" \ @@ -171,7 +192,8 @@ "mmcbootpart=2\0" \ "mmcrootpart=3\0" \ "opts=always_resume=1\0" \ - CONFIG_DFU_ALT + "partitions=" PARTS_DEFAULT \ + CONFIG_DFU_ALT \ /* Miscellaneous configurable options */ #define CONFIG_SYS_LONGHELP /* undef to save memory */ @@ -208,6 +230,10 @@ #define CONFIG_DOS_PARTITION +/* GPT */ +#define CONFIG_EFI_PARTITION +#define CONFIG_PARTITION_UUIDS + #define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_LOAD_ADDR - GENERATED_GBL_DATA_SIZE) #define CONFIG_SYS_CACHELINE_SIZE 32 -- cgit v0.10.2 From e772cb30f649c1bb8c9cb15e4c05cbf0760f2f61 Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 11 Dec 2012 22:16:18 -0600 Subject: Make linux kernel string funcs available to tools isspace() and strim() are not in the typical user-mode string.h, so put them in a separate compilation unit so that they can be built into tools that need them independent of the other common string functions. This allows code shared by u-boot and the linux user-mode tools to link. Signed-off-by: Joe Hershberger diff --git a/include/linux/linux_string.h b/include/linux/linux_string.h new file mode 100644 index 0000000..192b4c9 --- /dev/null +++ b/include/linux/linux_string.h @@ -0,0 +1,8 @@ +#ifndef _LINUX_LINUX_STRING_H_ +#define _LINUX_LINUX_STRING_H_ + +extern char * skip_spaces(const char *); + +extern char *strim(char *); + +#endif diff --git a/include/linux/string.h b/include/linux/string.h index de83355..e9b134d 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -50,10 +50,7 @@ extern char * strchr(const char *,int); #ifndef __HAVE_ARCH_STRRCHR extern char * strrchr(const char *,int); #endif -extern char * skip_spaces(const char *); - -extern char *strim(char *); - +#include #ifndef __HAVE_ARCH_STRSTR extern char * strstr(const char *,const char *); #endif diff --git a/lib/Makefile b/lib/Makefile index f83f6e8..86ca1a6 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -69,6 +69,7 @@ endif COBJS-y += crc32.o COBJS-y += ctype.o COBJS-y += div64.o +COBJS-y += linux_string.o COBJS-y += string.o COBJS-y += time.o COBJS-$(CONFIG_BOOTP_PXE) += uuid.o diff --git a/lib/linux_string.c b/lib/linux_string.c new file mode 100644 index 0000000..d5a5e08 --- /dev/null +++ b/lib/linux_string.c @@ -0,0 +1,51 @@ +/* + * linux/lib/string.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#ifdef USE_HOSTCC +#include +#endif + +#include +#include + +/** + * skip_spaces - Removes leading whitespace from @str. + * @str: The string to be stripped. + * + * Returns a pointer to the first non-whitespace character in @str. + */ +char *skip_spaces(const char *str) +{ + while (isspace(*str)) + ++str; + return (char *)str; +} + +/** + * strim - Removes leading and trailing whitespace from @s. + * @s: The string to be stripped. + * + * Note that the first trailing whitespace is replaced with a %NUL-terminator + * in the given string @s. Returns a pointer to the first non-whitespace + * character in @s. + */ +char *strim(char *s) +{ + size_t size; + char *end; + + s = skip_spaces(s); + size = strlen(s); + if (!size) + return s; + + end = s + size - 1; + while (end >= s && isspace(*end)) + end--; + *(end + 1) = '\0'; + + return s; +} diff --git a/lib/string.c b/lib/string.c index 68f60be..09dfae0 100644 --- a/lib/string.c +++ b/lib/string.c @@ -222,45 +222,6 @@ char * strrchr(const char * s, int c) } #endif - -/** - * skip_spaces - Removes leading whitespace from @str. - * @str: The string to be stripped. - * - * Returns a pointer to the first non-whitespace character in @str. - */ -char *skip_spaces(const char *str) -{ - while (isspace(*str)) - ++str; - return (char *)str; -} - -/** - * strim - Removes leading and trailing whitespace from @s. - * @s: The string to be stripped. - * - * Note that the first trailing whitespace is replaced with a %NUL-terminator - * in the given string @s. Returns a pointer to the first non-whitespace - * character in @s. - */ -char *strim(char *s) -{ - size_t size; - char *end; - - s = skip_spaces(s); - size = strlen(s); - if (!size) - return s; - - end = s + size - 1; - while (end >= s && isspace(*end)) - end--; - *(end + 1) = '\0'; - - return s; -} #ifndef __HAVE_ARCH_STRLEN /** * strlen - Find the length of a string -- cgit v0.10.2 From c4e0057fa78ebb524b9241ad7245fcd1074ba414 Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 11 Dec 2012 22:16:19 -0600 Subject: env: Refactor do_apply to a flag Use a flag in hsearch_r for insert mode passed from import to allow the behavior be different based on use. Now that "do_check" is called for all imports, ensure console init is complete before updating the console on relocation import Signed-off-by: Joe Hershberger diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c index 006131f..a8dc9a6 100644 --- a/common/cmd_nvedit.c +++ b/common/cmd_nvedit.c @@ -116,7 +116,7 @@ static int env_print(char *name) e.key = name; e.data = NULL; - hsearch_r(e, FIND, &ep, &env_htab); + hsearch_r(e, FIND, &ep, &env_htab, 0); if (ep == NULL) return 0; len = printf("%s=%s\n", ep->key, ep->data); @@ -224,7 +224,7 @@ int env_check_apply(const char *name, const char *oldval, else if (strcmp(name, "stderr") == 0) console = stderr; - if (console != -1) { + if (console != -1 && (gd->flags & GD_FLG_DEVINIT) != 0) { if ((newval == NULL) || (*newval == '\0')) { /* We cannot delete stdin/stdout/stderr */ if ((flag & H_FORCE) == 0) @@ -344,20 +344,19 @@ static int _do_env_set(int flag, int argc, char * const argv[]) */ e.key = name; e.data = NULL; - hsearch_r(e, FIND, &ep, &env_htab); + hsearch_r(e, FIND, &ep, &env_htab, 0); /* - * Perform requested checks. Notice how since we are overwriting - * a single variable, we need to set H_NOCLEAR + * Perform requested checks. */ - if (env_check_apply(name, ep ? ep->data : NULL, value, H_NOCLEAR)) { + if (env_check_apply(name, ep ? ep->data : NULL, value, 0)) { debug("check function did not approve, refusing\n"); return 1; } /* Delete only ? */ if (argc < 3 || argv[2] == NULL) { - int rc = hdelete_r(name, &env_htab, 0); + int rc = hdelete_r(name, &env_htab, H_INTERACTIVE); return !rc; } @@ -384,7 +383,7 @@ static int _do_env_set(int flag, int argc, char * const argv[]) e.key = name; e.data = value; - hsearch_r(e, ENTER, &ep, &env_htab); + hsearch_r(e, ENTER, &ep, &env_htab, H_INTERACTIVE); free(value); if (!ep) { printf("## Error inserting \"%s\" variable, errno=%d\n", @@ -552,7 +551,7 @@ char *getenv(const char *name) e.key = name; e.data = NULL; - hsearch_r(e, FIND, &ep, &env_htab); + hsearch_r(e, FIND, &ep, &env_htab, 0); return ep ? ep->data : NULL; } @@ -951,7 +950,7 @@ static int do_env_import(cmd_tbl_t *cmdtp, int flag, } if (himport_r(&env_htab, addr, size, sep, del ? 0 : H_NOCLEAR, - 0, NULL, 0 /* do_apply */) == 0) { + 0, NULL) == 0) { error("Environment import failed: errno = %d\n", errno); return 1; } diff --git a/common/console.c b/common/console.c index 25b141a..c21934d 100644 --- a/common/console.c +++ b/common/console.c @@ -681,8 +681,6 @@ int console_init_r(void) done: #endif - gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */ - #ifndef CONFIG_SYS_CONSOLE_INFO_QUIET stdio_print_current_devices(); #endif /* CONFIG_SYS_CONSOLE_INFO_QUIET */ @@ -694,6 +692,8 @@ done: } #endif /* CONFIG_SYS_CONSOLE_ENV_OVERWRITE */ + gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */ + #if 0 /* If nothing usable installed, use only the initial console */ if ((stdio_devices[stdin] == NULL) && (stdio_devices[stdout] == NULL)) @@ -758,8 +758,6 @@ int console_init_r(void) #endif } - gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */ - #ifndef CONFIG_SYS_CONSOLE_INFO_QUIET stdio_print_current_devices(); #endif /* CONFIG_SYS_CONSOLE_INFO_QUIET */ @@ -769,6 +767,8 @@ int console_init_r(void) setenv(stdio_names[i], stdio_devices[i]->name); } + gd->flags |= GD_FLG_DEVINIT; /* device initialization completed */ + #if 0 /* If nothing usable installed, use only the initial console */ if ((stdio_devices[stdin] == NULL) && (stdio_devices[stdout] == NULL)) diff --git a/common/env_common.c b/common/env_common.c index 3d3cb70..f22f5b9 100644 --- a/common/env_common.c +++ b/common/env_common.c @@ -83,11 +83,8 @@ const uchar *env_get_addr(int index) void set_default_env(const char *s) { - /* - * By default, do not apply changes as they will eventually - * be applied by someone else - */ - int do_apply = 0; + int flags = 0; + if (sizeof(default_environment) > ENV_SIZE) { puts("*** Error - default environment is too large\n\n"); return; @@ -99,14 +96,7 @@ void set_default_env(const char *s) "using default environment\n\n", s + 1); } else { - /* - * This set_to_default was explicitly asked for - * by the user, as opposed to being a recovery - * mechanism. Therefore we check every single - * variable and apply changes to the system - * right away (e.g. baudrate, console). - */ - do_apply = 1; + flags = H_INTERACTIVE; puts(s); } } else { @@ -114,8 +104,8 @@ void set_default_env(const char *s) } if (himport_r(&env_htab, (char *)default_environment, - sizeof(default_environment), '\0', 0, - 0, NULL, do_apply) == 0) + sizeof(default_environment), '\0', flags, + 0, NULL) == 0) error("Environment import failed: errno = %d\n", errno); gd->flags |= GD_FLG_ENV_READY; @@ -130,8 +120,8 @@ int set_default_vars(int nvars, char * const vars[]) * (and use \0 as a separator) */ return himport_r(&env_htab, (const char *)default_environment, - sizeof(default_environment), '\0', H_NOCLEAR, - nvars, vars, 1 /* do_apply */); + sizeof(default_environment), '\0', + H_NOCLEAR | H_INTERACTIVE, nvars, vars); } #ifndef CONFIG_SPL_BUILD @@ -155,7 +145,7 @@ int env_import(const char *buf, int check) } if (himport_r(&env_htab, (char *)ep->data, ENV_SIZE, '\0', 0, - 0, NULL, 0 /* do_apply */)) { + 0, NULL)) { gd->flags |= GD_FLG_ENV_READY; return 1; } diff --git a/include/search.h b/include/search.h index 93e1cbc..f5165b0 100644 --- a/include/search.h +++ b/include/search.h @@ -73,7 +73,7 @@ struct hsearch_data { extern int hcreate_r(size_t __nel, struct hsearch_data *__htab); /* Destroy current internal hashing table. */ -extern void hdestroy_r(struct hsearch_data *__htab, int do_apply); +extern void hdestroy_r(struct hsearch_data *__htab); /* * Search for entry matching ITEM.key in internal hash table. If @@ -82,7 +82,7 @@ extern void hdestroy_r(struct hsearch_data *__htab, int do_apply); * ITEM.data. * */ extern int hsearch_r(ENTRY __item, ACTION __action, ENTRY ** __retval, - struct hsearch_data *__htab); + struct hsearch_data *__htab, int __flag); /* * Search for an entry matching `MATCH'. Otherwise, Same semantics @@ -99,7 +99,7 @@ extern int hstrstr_r(const char *__match, int __last_idx, ENTRY ** __retval, /* Search and delete entry matching ITEM.key in internal hash table. */ extern int hdelete_r(const char *__key, struct hsearch_data *__htab, - int do_apply); + int __flag); extern ssize_t hexport_r(struct hsearch_data *__htab, const char __sep, char **__resp, size_t __size, @@ -113,10 +113,11 @@ extern ssize_t hexport_r(struct hsearch_data *__htab, */ extern int himport_r(struct hsearch_data *__htab, const char *__env, size_t __size, const char __sep, - int __flag, int nvars, char * const vars[], int do_apply); + int __flag, int nvars, char * const vars[]); -/* Flags for himport_r() */ -#define H_NOCLEAR (1 << 0) /* do not clear hash table before importing */ -#define H_FORCE (1 << 1) /* overwrite read-only/write-once variables */ +/* Flags for himport_r(), hdelete_r(), and hsearch_r() */ +#define H_NOCLEAR (1 << 0) /* do not clear hash table before importing */ +#define H_FORCE (1 << 1) /* overwrite read-only/write-once variables */ +#define H_INTERACTIVE (1 << 2) /* indicate that an import is user directed */ #endif /* search.h */ diff --git a/lib/hashtable.c b/lib/hashtable.c index 94a7b61..f0056ac 100644 --- a/lib/hashtable.c +++ b/lib/hashtable.c @@ -142,7 +142,7 @@ int hcreate_r(size_t nel, struct hsearch_data *htab) * be freed and the local static variable can be marked as not used. */ -void hdestroy_r(struct hsearch_data *htab, int do_apply) +void hdestroy_r(struct hsearch_data *htab) { int i; @@ -156,10 +156,7 @@ void hdestroy_r(struct hsearch_data *htab, int do_apply) for (i = 1; i <= htab->size; ++i) { if (htab->table[i].used > 0) { ENTRY *ep = &htab->table[i].entry; - if (do_apply && htab->apply != NULL) { - /* deletion is always forced */ - htab->apply(ep->key, ep->data, NULL, H_FORCE); - } + free((void *)ep->key); free(ep->data); } @@ -251,7 +248,7 @@ int hmatch_r(const char *match, int last_idx, ENTRY ** retval, } int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval, - struct hsearch_data *htab) + struct hsearch_data *htab, int flag) { unsigned int hval; unsigned int count; @@ -404,7 +401,7 @@ int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval, * do that. */ -int hdelete_r(const char *key, struct hsearch_data *htab, int do_apply) +int hdelete_r(const char *key, struct hsearch_data *htab, int flag) { ENTRY e, *ep; int idx; @@ -413,15 +410,21 @@ int hdelete_r(const char *key, struct hsearch_data *htab, int do_apply) e.key = (char *)key; - if ((idx = hsearch_r(e, FIND, &ep, htab)) == 0) { + idx = hsearch_r(e, FIND, &ep, htab, 0); + if (idx == 0) { __set_errno(ESRCH); return 0; /* not found */ } + /* Check for permission */ + if (htab->apply != NULL && + htab->apply(ep->key, ep->data, NULL, flag)) { + __set_errno(EPERM); + return 0; + } + /* free used ENTRY */ debug("hdelete: DELETING key \"%s\"\n", key); - if (do_apply && htab->apply != NULL) - htab->apply(ep->key, ep->data, NULL, H_FORCE); free((void *)ep->key); free(ep->data); htab->table[idx].used = -1; @@ -674,7 +677,7 @@ static int drop_var_from_set(const char *name, int nvars, char * vars[]) int himport_r(struct hsearch_data *htab, const char *env, size_t size, const char sep, int flag, - int nvars, char * const vars[], int do_apply) + int nvars, char * const vars[]) { char *data, *sp, *dp, *name, *value; char *localvars[nvars]; @@ -704,7 +707,7 @@ int himport_r(struct hsearch_data *htab, debug("Destroy Hash Table: %p table = %p\n", htab, htab->table); if (htab->table) - hdestroy_r(htab, do_apply); + hdestroy_r(htab); } /* @@ -770,7 +773,7 @@ int himport_r(struct hsearch_data *htab, if (!drop_var_from_set(name, nvars, localvars)) continue; - if (hdelete_r(name, htab, do_apply) == 0) + if (hdelete_r(name, htab, flag) == 0) debug("DELETE ERROR ##############################\n"); continue; @@ -795,14 +798,14 @@ int himport_r(struct hsearch_data *htab, e.data = value; /* if there is an apply function, check what it has to say */ - if (do_apply && htab->apply != NULL) { + if (htab->apply != NULL) { debug("searching before calling cb function" " for %s\n", name); /* * Search for variable in existing env, so to pass * its previous value to the apply callback */ - hsearch_r(e, FIND, &rv, htab); + hsearch_r(e, FIND, &rv, htab, 0); debug("previous value was %s\n", rv ? rv->data : ""); if (htab->apply(name, rv ? rv->data : NULL, value, flag)) { @@ -812,7 +815,7 @@ int himport_r(struct hsearch_data *htab, } } - hsearch_r(e, ENTER, &rv, htab); + hsearch_r(e, ENTER, &rv, htab, flag); if (rv == NULL) { printf("himport_r: can't insert \"%s=%s\" into hash table\n", name, value); @@ -839,7 +842,7 @@ int himport_r(struct hsearch_data *htab, * b) if the variable was not present in current env, we notify * it might be a typo */ - if (hdelete_r(localvars[i], htab, do_apply) == 0) + if (hdelete_r(localvars[i], htab, flag) == 0) printf("WARNING: '%s' neither in running nor in imported env!\n", localvars[i]); else printf("WARNING: '%s' not in imported env, deleting it!\n", localvars[i]); -- cgit v0.10.2 From 3d3b52f2586a8bf1c53496547062594fd4386454 Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 11 Dec 2012 22:16:20 -0600 Subject: env: Consolidate common code in hsearch_r() The same chunk of code was replicated in two places and the following changes will make that chunk grow a bit, so combine into a static func. Signed-off-by: Joe Hershberger diff --git a/lib/hashtable.c b/lib/hashtable.c index f0056ac..f4d5795 100644 --- a/lib/hashtable.c +++ b/lib/hashtable.c @@ -247,6 +247,34 @@ int hmatch_r(const char *match, int last_idx, ENTRY ** retval, return 0; } +/* + * Compare an existing entry with the desired key, and overwrite if the action + * is ENTER. This is simply a helper function for hsearch_r(). + */ +static inline int _compare_and_overwrite_entry(ENTRY item, ACTION action, + ENTRY **retval, struct hsearch_data *htab, int flag, + unsigned int hval, unsigned int idx) +{ + if (htab->table[idx].used == hval + && strcmp(item.key, htab->table[idx].entry.key) == 0) { + /* Overwrite existing value? */ + if ((action == ENTER) && (item.data != NULL)) { + free(htab->table[idx].entry.data); + htab->table[idx].entry.data = strdup(item.data); + if (!htab->table[idx].entry.data) { + __set_errno(ENOMEM); + *retval = NULL; + return 0; + } + } + /* return found entry */ + *retval = &htab->table[idx].entry; + return idx; + } + /* keep searching */ + return -1; +} + int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval, struct hsearch_data *htab, int flag) { @@ -255,6 +283,7 @@ int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval, unsigned int len = strlen(item.key); unsigned int idx; unsigned int first_deleted = 0; + int ret; /* Compute an value for the given string. Perhaps use a better method. */ hval = len; @@ -286,23 +315,10 @@ int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval, && !first_deleted) first_deleted = idx; - if (htab->table[idx].used == hval - && strcmp(item.key, htab->table[idx].entry.key) == 0) { - /* Overwrite existing value? */ - if ((action == ENTER) && (item.data != NULL)) { - free(htab->table[idx].entry.data); - htab->table[idx].entry.data = - strdup(item.data); - if (!htab->table[idx].entry.data) { - __set_errno(ENOMEM); - *retval = NULL; - return 0; - } - } - /* return found entry */ - *retval = &htab->table[idx].entry; - return idx; - } + ret = _compare_and_overwrite_entry(item, action, retval, htab, + flag, hval, idx); + if (ret != -1) + return ret; /* * Second hash function: @@ -328,23 +344,10 @@ int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval, break; /* If entry is found use it. */ - if ((htab->table[idx].used == hval) - && strcmp(item.key, htab->table[idx].entry.key) == 0) { - /* Overwrite existing value? */ - if ((action == ENTER) && (item.data != NULL)) { - free(htab->table[idx].entry.data); - htab->table[idx].entry.data = - strdup(item.data); - if (!htab->table[idx].entry.data) { - __set_errno(ENOMEM); - *retval = NULL; - return 0; - } - } - /* return found entry */ - *retval = &htab->table[idx].entry; - return idx; - } + ret = _compare_and_overwrite_entry(item, action, retval, + htab, flag, hval, idx); + if (ret != -1) + return ret; } while (htab->table[idx].used); } -- cgit v0.10.2 From 7afcf3a55b5f484b3d3442053fae8186a3fb92d7 Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 11 Dec 2012 22:16:21 -0600 Subject: env: Refactor apply into change_ok Move the read of the old value to inside the check function. In some cases it can be avoided all together and at the least the code is only called from one place. Also name the function and the callback to more clearly describe what it does. Pass the ENTRY instead of just the name for direct access to the whole data structure. Pass an enum to the callback that specifies the operation being approved. Signed-off-by: Joe Hershberger diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c index a8dc9a6..da5689c 100644 --- a/common/cmd_nvedit.c +++ b/common/cmd_nvedit.c @@ -208,10 +208,20 @@ static int do_env_grep(cmd_tbl_t *cmdtp, int flag, * overwriting of write-once variables. */ -int env_check_apply(const char *name, const char *oldval, - const char *newval, int flag) +int env_change_ok(const ENTRY *item, const char *newval, enum env_op op, + int flag) { int console = -1; + const char *name; +#if !defined(CONFIG_ENV_OVERWRITE) && defined(CONFIG_OVERWRITE_ETHADDR_ONCE) \ +&& defined(CONFIG_ETHADDR) + const char *oldval = NULL; + + if (op != env_op_create) + oldval = item->data; +#endif + + name = item->key; /* Default value for NULL to protect string-manipulating functions */ newval = newval ? : ""; @@ -242,12 +252,12 @@ int env_check_apply(const char *name, const char *oldval, #endif /* CONFIG_CONSOLE_MUX */ } +#ifndef CONFIG_ENV_OVERWRITE /* * Some variables like "ethaddr" and "serial#" can be set only once and * cannot be deleted, unless CONFIG_ENV_OVERWRITE is defined. */ -#ifndef CONFIG_ENV_OVERWRITE - if (oldval != NULL && /* variable exists */ + if (op != env_op_create && /* variable exists */ (flag & H_FORCE) == 0) { /* and we are not forced */ if (strcmp(name, "serial#") == 0 || (strcmp(name, "ethaddr") == 0 @@ -265,7 +275,7 @@ int env_check_apply(const char *name, const char *oldval, * (which will erase all variables prior to calling this), * we want the baudrate to actually change - for real. */ - if (oldval != NULL || /* variable exists */ + if (op != env_op_create || /* variable exists */ (flag & H_NOCLEAR) == 0) { /* or env is clear */ /* * Switch to new baudrate if new baudrate is supported @@ -339,20 +349,6 @@ static int _do_env_set(int flag, int argc, char * const argv[]) } env_id++; - /* - * search if variable with this name already exists - */ - e.key = name; - e.data = NULL; - hsearch_r(e, FIND, &ep, &env_htab, 0); - - /* - * Perform requested checks. - */ - if (env_check_apply(name, ep ? ep->data : NULL, value, 0)) { - debug("check function did not approve, refusing\n"); - return 1; - } /* Delete only ? */ if (argc < 3 || argv[2] == NULL) { diff --git a/common/env_common.c b/common/env_common.c index f22f5b9..a960aa8 100644 --- a/common/env_common.c +++ b/common/env_common.c @@ -40,7 +40,7 @@ DECLARE_GLOBAL_DATA_PTR; #include struct hsearch_data env_htab = { - .apply = env_check_apply, + .change_ok = env_change_ok, }; static uchar __env_get_char_spec(int index) @@ -162,6 +162,7 @@ void env_relocate(void) { #if defined(CONFIG_NEEDS_MANUAL_RELOC) env_reloc(); + env_htab.change_ok += gd->reloc_off; #endif if (gd->env_valid == 0) { #if defined(CONFIG_ENV_IS_NOWHERE) || defined(CONFIG_SPL_BUILD) diff --git a/include/environment.h b/include/environment.h index e8ab703..4b19f32 100644 --- a/include/environment.h +++ b/include/environment.h @@ -188,13 +188,12 @@ int set_default_vars(int nvars, char * const vars[]); int env_import(const char *buf, int check); /* - * Check if variable "name" can be changed from oldval to newval, - * and if so, apply the changes (e.g. baudrate). + * Check if variable "item" can be changed to newval * When (flag & H_FORCE) is set, it does not print out any error * message and forces overwriting of write-once variables. */ -int env_check_apply(const char *name, const char *oldval, - const char *newval, int flag); +int env_change_ok(const ENTRY *item, const char *newval, enum env_op op, + int flag); #endif /* DO_DEPS_ONLY */ diff --git a/include/search.h b/include/search.h index f5165b0..fa00ea1 100644 --- a/include/search.h +++ b/include/search.h @@ -32,6 +32,12 @@ #define __set_errno(val) do { errno = val; } while (0) +enum env_op { + env_op_create, + env_op_delete, + env_op_overwrite, +}; + /* Action which shall be performed in the call the hsearch. */ typedef enum { FIND, @@ -59,14 +65,13 @@ struct hsearch_data { unsigned int filled; /* * Callback function which will check whether the given change for variable - * "name" from "oldval" to "newval" may be applied or not, and possibly apply - * such change. + * "item" to "newval" may be applied or not, and possibly apply such change. * When (flag & H_FORCE) is set, it shall not print out any error message and * shall force overwriting of write-once variables. .* Must return 0 for approval, 1 for denial. */ - int (*apply)(const char *name, const char *oldval, - const char *newval, int flag); + int (*change_ok)(const ENTRY *__item, const char *newval, enum env_op, + int flag); }; /* Create a new hashing table which will at most contain NEL elements. */ diff --git a/lib/hashtable.c b/lib/hashtable.c index f4d5795..6861a42 100644 --- a/lib/hashtable.c +++ b/lib/hashtable.c @@ -66,12 +66,16 @@ * Instead the interface of all functions is extended to take an argument * which describes the current status. */ + typedef struct _ENTRY { int used; ENTRY entry; } _ENTRY; +static void _hdelete(const char *key, struct hsearch_data *htab, ENTRY *ep, + int idx); + /* * hcreate() */ @@ -259,6 +263,17 @@ static inline int _compare_and_overwrite_entry(ENTRY item, ACTION action, && strcmp(item.key, htab->table[idx].entry.key) == 0) { /* Overwrite existing value? */ if ((action == ENTER) && (item.data != NULL)) { + /* check for permission */ + if (htab->change_ok != NULL && htab->change_ok( + &htab->table[idx].entry, item.data, + env_op_overwrite, flag)) { + debug("change_ok() rejected setting variable " + "%s, skipping it!\n", item.key); + __set_errno(EPERM); + *retval = NULL; + return 0; + } + free(htab->table[idx].entry.data); htab->table[idx].entry.data = strdup(item.data); if (!htab->table[idx].entry.data) { @@ -383,6 +398,17 @@ int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval, ++htab->filled; + /* check for permission */ + if (htab->change_ok != NULL && htab->change_ok( + &htab->table[idx].entry, item.data, env_op_create, flag)) { + debug("change_ok() rejected setting variable " + "%s, skipping it!\n", item.key); + _hdelete(item.key, htab, &htab->table[idx].entry, idx); + __set_errno(EPERM); + *retval = NULL; + return 0; + } + /* return new entry */ *retval = &htab->table[idx].entry; return 1; @@ -404,6 +430,18 @@ int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval, * do that. */ +static void _hdelete(const char *key, struct hsearch_data *htab, ENTRY *ep, + int idx) +{ + /* free used ENTRY */ + debug("hdelete: DELETING key \"%s\"\n", key); + free((void *)ep->key); + free(ep->data); + htab->table[idx].used = -1; + + --htab->filled; +} + int hdelete_r(const char *key, struct hsearch_data *htab, int flag) { ENTRY e, *ep; @@ -420,19 +458,15 @@ int hdelete_r(const char *key, struct hsearch_data *htab, int flag) } /* Check for permission */ - if (htab->apply != NULL && - htab->apply(ep->key, ep->data, NULL, flag)) { + if (htab->change_ok != NULL && + htab->change_ok(ep, NULL, env_op_delete, flag)) { + debug("change_ok() rejected deleting variable " + "%s, skipping it!\n", key); __set_errno(EPERM); return 0; } - /* free used ENTRY */ - debug("hdelete: DELETING key \"%s\"\n", key); - free((void *)ep->key); - free(ep->data); - htab->table[idx].used = -1; - - --htab->filled; + _hdelete(key, htab, ep, idx); return 1; } @@ -800,24 +834,6 @@ int himport_r(struct hsearch_data *htab, e.key = name; e.data = value; - /* if there is an apply function, check what it has to say */ - if (htab->apply != NULL) { - debug("searching before calling cb function" - " for %s\n", name); - /* - * Search for variable in existing env, so to pass - * its previous value to the apply callback - */ - hsearch_r(e, FIND, &rv, htab, 0); - debug("previous value was %s\n", rv ? rv->data : ""); - if (htab->apply(name, rv ? rv->data : NULL, - value, flag)) { - debug("callback function refused to set" - " variable %s, skipping it!\n", name); - continue; - } - } - hsearch_r(e, ENTER, &rv, htab, flag); if (rv == NULL) { printf("himport_r: can't insert \"%s=%s\" into hash table\n", -- cgit v0.10.2 From ec8a252cd492a7a409d6912aebeff34bb9e1e1e1 Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 11 Dec 2012 22:16:22 -0600 Subject: env: Use getenv_yesno() more generally Move the getenv_yesno() to env_common.c and change most checks for 'y' or 'n' to use this helper. Signed-off-by: Joe Hershberger diff --git a/arch/arm/lib/board.c b/arch/arm/lib/board.c index 22a4d9c..e0cb635 100644 --- a/arch/arm/lib/board.c +++ b/arch/arm/lib/board.c @@ -540,15 +540,13 @@ void board_init_r(gd_t *id, ulong dest_addr) flash_size = flash_init(); if (flash_size > 0) { # ifdef CONFIG_SYS_FLASH_CHECKSUM - char *s = getenv("flashchecksum"); - print_size(flash_size, ""); /* * Compute and print flash CRC if flashchecksum is set to 'y' * * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX */ - if (s && (*s == 'y')) { + if (getenv_yesno("flashchecksum") == 1) { printf(" CRC: %08X", crc32(0, (const unsigned char *) CONFIG_SYS_FLASH_BASE, flash_size)); diff --git a/arch/m68k/lib/board.c b/arch/m68k/lib/board.c index 02d73fd..794b867 100644 --- a/arch/m68k/lib/board.c +++ b/arch/m68k/lib/board.c @@ -462,8 +462,7 @@ void board_init_r (gd_t *id, ulong dest_addr) * * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX */ - s = getenv ("flashchecksum"); - if (s && (*s == 'y')) { + if (getenv_yesno("flashchecksum") == 1) { printf (" CRC: %08X", crc32 (0, (const unsigned char *) CONFIG_SYS_FLASH_BASE, diff --git a/arch/microblaze/lib/board.c b/arch/microblaze/lib/board.c index efd63cd..a7c2f76 100644 --- a/arch/microblaze/lib/board.c +++ b/arch/microblaze/lib/board.c @@ -74,7 +74,6 @@ void board_init_f(ulong not_used) gd = (gd_t *) (CONFIG_SYS_SDRAM_BASE + CONFIG_SYS_GBL_DATA_OFFSET); bd = (bd_t *) (CONFIG_SYS_SDRAM_BASE + CONFIG_SYS_GBL_DATA_OFFSET \ - GENERATED_BD_INFO_SIZE); - __maybe_unused char *s; #if defined(CONFIG_CMD_FLASH) ulong flash_size = 0; #endif @@ -143,8 +142,7 @@ void board_init_f(ulong not_used) * * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX */ - s = getenv ("flashchecksum"); - if (s && (*s == 'y')) { + if (getenv_yesno("flashchecksum") == 1) { printf (" CRC: %08X", crc32(0, (const u8 *)bd->bi_flashstart, flash_size) diff --git a/arch/powerpc/cpu/mpc85xx/mp.c b/arch/powerpc/cpu/mpc85xx/mp.c index e1197ac..43d4836 100644 --- a/arch/powerpc/cpu/mpc85xx/mp.c +++ b/arch/powerpc/cpu/mpc85xx/mp.c @@ -46,10 +46,8 @@ u32 get_my_id() */ int hold_cores_in_reset(int verbose) { - const char *s = getenv("mp_holdoff"); - /* Default to no, overriden by 'y', 'yes', 'Y', 'Yes', or '1' */ - if (s && (*s == 'y' || *s == 'Y' || *s == '1')) { + if (getenv_yesno("mp_holdoff") == 1) { if (verbose) { puts("Secondary cores are being held in reset.\n"); puts("See 'mp_holdoff' environment variable\n"); diff --git a/arch/powerpc/lib/board.c b/arch/powerpc/lib/board.c index 1b051e1..6a7bf4b 100644 --- a/arch/powerpc/lib/board.c +++ b/arch/powerpc/lib/board.c @@ -739,16 +739,13 @@ void board_init_r(gd_t *id, ulong dest_addr) flash_size = 0; } else if ((flash_size = flash_init()) > 0) { #ifdef CONFIG_SYS_FLASH_CHECKSUM - char *s; - print_size(flash_size, ""); /* * Compute and print flash CRC if flashchecksum is set to 'y' * * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX */ - s = getenv("flashchecksum"); - if (s && (*s == 'y')) { + if (getenv_yesno("flashchecksum") == 1) { printf(" CRC: %08X", crc32(0, (const unsigned char *) @@ -841,9 +838,7 @@ void board_init_r(gd_t *id, ulong dest_addr) * "i2cfast" into account */ { - char *s = getenv("i2cfast"); - - if (s && ((*s == 'y') || (*s == 'Y'))) { + if (getenv_yesno("i2cfast") == 1) { bd->bi_iic_fast[0] = 1; bd->bi_iic_fast[1] = 1; } diff --git a/arch/sparc/lib/board.c b/arch/sparc/lib/board.c index 32d025a..1b5e995 100644 --- a/arch/sparc/lib/board.c +++ b/arch/sparc/lib/board.c @@ -284,8 +284,7 @@ void board_init_f(ulong bootflag) * * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX */ - s = getenv("flashchecksum"); - if (s && (*s == 'y')) { + if (getenv_yesno("flashchecksum") == 1) { printf(" CRC: %08lX", crc32(0, (const unsigned char *)CONFIG_SYS_FLASH_BASE, flash_size) diff --git a/board/Marvell/db64360/db64360.c b/board/Marvell/db64360/db64360.c index 6cae686..38769e0 100644 --- a/board/Marvell/db64360/db64360.c +++ b/board/Marvell/db64360/db64360.c @@ -834,15 +834,11 @@ int mem_test_walk (void) /*********************************************************************/ int testdram (void) { - char *s; int rundata, runaddress, runwalk; - s = getenv ("testdramdata"); - rundata = (s && (*s == 'y')) ? 1 : 0; - s = getenv ("testdramaddress"); - runaddress = (s && (*s == 'y')) ? 1 : 0; - s = getenv ("testdramwalk"); - runwalk = (s && (*s == 'y')) ? 1 : 0; + rundata = getenv_yesno("testdramdata") == 1; + runaddress = getenv_yesno("testdramaddress") == 1; + runwalk = getenv_yesno("testdramwalk") == 1; /* rundata = 1; */ /* runaddress = 0; */ diff --git a/board/Marvell/db64460/db64460.c b/board/Marvell/db64460/db64460.c index d4f58b3..ddb7ed5 100644 --- a/board/Marvell/db64460/db64460.c +++ b/board/Marvell/db64460/db64460.c @@ -834,15 +834,11 @@ int mem_test_walk (void) /*********************************************************************/ int testdram (void) { - char *s; int rundata, runaddress, runwalk; - s = getenv ("testdramdata"); - rundata = (s && (*s == 'y')) ? 1 : 0; - s = getenv ("testdramaddress"); - runaddress = (s && (*s == 'y')) ? 1 : 0; - s = getenv ("testdramwalk"); - runwalk = (s && (*s == 'y')) ? 1 : 0; + rundata = getenv_yesno("testdramdata") == 1; + runaddress = getenv_yesno("testdramaddress") == 1; + runwalk = getenv_yesno("testdramwalk") == 1; /* rundata = 1; */ /* runaddress = 0; */ diff --git a/board/esd/cpci750/cpci750.c b/board/esd/cpci750/cpci750.c index 98051fb..d7deae4 100644 --- a/board/esd/cpci750/cpci750.c +++ b/board/esd/cpci750/cpci750.c @@ -953,22 +953,18 @@ int mem_test_walk (void) /*********************************************************************/ int testdram (void) { - char *s; int rundata = 0; int runaddress = 0; int runwalk = 0; #ifdef CONFIG_SYS_DRAM_TEST_DATA - s = getenv ("testdramdata"); - rundata = (s && (*s == 'y')) ? 1 : 0; + rundata = getenv_yesno("testdramdata") == 1; #endif #ifdef CONFIG_SYS_DRAM_TEST_ADDRESS - s = getenv ("testdramaddress"); - runaddress = (s && (*s == 'y')) ? 1 : 0; + runaddress = getenv_yesno("testdramaddress") == 1; #endif #ifdef CONFIG_SYS_DRAM_TEST_WALK - s = getenv ("testdramwalk"); - runwalk = (s && (*s == 'y')) ? 1 : 0; + runwalk = getenv_yesno("testdramwalk") == 1; #endif if ((rundata == 1) || (runaddress == 1) || (runwalk == 1)) { diff --git a/board/gw8260/gw8260.c b/board/gw8260/gw8260.c index 77a1e1d..64c54d5 100644 --- a/board/gw8260/gw8260.c +++ b/board/gw8260/gw8260.c @@ -544,15 +544,11 @@ int mem_test_walk (void) /*********************************************************************/ int testdram (void) { - char *s; int rundata, runaddress, runwalk; - s = getenv ("testdramdata"); - rundata = (s && (*s == 'y')) ? 1 : 0; - s = getenv ("testdramaddress"); - runaddress = (s && (*s == 'y')) ? 1 : 0; - s = getenv ("testdramwalk"); - runwalk = (s && (*s == 'y')) ? 1 : 0; + rundata = getenv_yesno("testdramdata") == 1; + runaddress = getenv_yesno("testdramaddress") == 1; + runwalk = getenv_yesno("testdramwalk") == 1; if ((rundata == 1) || (runaddress == 1) || (runwalk == 1)) { printf ("Testing RAM ... "); diff --git a/board/prodrive/p3mx/p3mx.c b/board/prodrive/p3mx/p3mx.c index 389affc..c3fd191 100644 --- a/board/prodrive/p3mx/p3mx.c +++ b/board/prodrive/p3mx/p3mx.c @@ -768,22 +768,18 @@ int mem_test_walk (void) /*********************************************************************/ int testdram (void) { - char *s; int rundata = 0; int runaddress = 0; int runwalk = 0; #ifdef CONFIG_SYS_DRAM_TEST_DATA - s = getenv ("testdramdata"); - rundata = (s && (*s == 'y')) ? 1 : 0; + rundata = getenv_yesno("testdramdata") == 1; #endif #ifdef CONFIG_SYS_DRAM_TEST_ADDRESS - s = getenv ("testdramaddress"); - runaddress = (s && (*s == 'y')) ? 1 : 0; + runaddress = getenv_yesno("testdramaddress") == 1; #endif #ifdef CONFIG_SYS_DRAM_TEST_WALK - s = getenv ("testdramwalk"); - runwalk = (s && (*s == 'y')) ? 1 : 0; + runwalk = getenv_yesno("testdramwalk") == 1; #endif if ((rundata == 1) || (runaddress == 1) || (runwalk == 1)) diff --git a/common/env_common.c b/common/env_common.c index a960aa8..067fe3f 100644 --- a/common/env_common.c +++ b/common/env_common.c @@ -81,6 +81,20 @@ const uchar *env_get_addr(int index) return &default_environment[index]; } +/* + * Read an environment variable as a boolean + * Return -1 if variable does not exist (default to true) + */ +int getenv_yesno(const char *var) +{ + char *s = getenv(var); + + if (s == NULL) + return -1; + return (*s == '1' || *s == 'y' || *s == 'Y' || *s == 't' || *s == 'T') ? + 1 : 0; +} + void set_default_env(const char *s) { int flags = 0; diff --git a/common/image.c b/common/image.c index e93b6e8..69e888c 100644 --- a/common/image.c +++ b/common/image.c @@ -416,12 +416,6 @@ static const image_header_t *image_get_ramdisk(ulong rd_addr, uint8_t arch, /* Shared dual-format routines */ /*****************************************************************************/ #ifndef USE_HOSTCC -int getenv_yesno(char *var) -{ - char *s = getenv(var); - return (s && (*s == 'n')) ? 0 : 1; -} - ulong getenv_bootm_low(void) { char *s = getenv("bootm_low"); diff --git a/include/common.h b/include/common.h index 5e3c5ee..d0bf1e8 100644 --- a/include/common.h +++ b/include/common.h @@ -340,6 +340,11 @@ int envmatch (uchar *, int); char *getenv (const char *); int getenv_f (const char *name, char *buf, unsigned len); ulong getenv_ulong(const char *name, int base, ulong default_val); +/* + * Read an environment variable as a boolean + * Return -1 if variable does not exist (default to true) + */ +int getenv_yesno(const char *var); int saveenv (void); int setenv (const char *, const char *); int setenv_ulong(const char *varname, ulong value); diff --git a/include/image.h b/include/image.h index f54d983..b958b18 100644 --- a/include/image.h +++ b/include/image.h @@ -460,7 +460,6 @@ static inline void image_set_name(image_header_t *hdr, const char *name) int image_check_hcrc(const image_header_t *hdr); int image_check_dcrc(const image_header_t *hdr); #ifndef USE_HOSTCC -int getenv_yesno(char *var); ulong getenv_bootm_low(void); phys_size_t getenv_bootm_size(void); phys_size_t getenv_bootm_mapsize(void); diff --git a/net/net.c b/net/net.c index 82c4cc9..da68a61 100644 --- a/net/net.c +++ b/net/net.c @@ -214,26 +214,24 @@ static int NetTryCount; */ void net_auto_load(void) { +#if defined(CONFIG_CMD_NFS) const char *s = getenv("autoload"); - if (s != NULL) { - if (*s == 'n') { - /* - * Just use BOOTP/RARP to configure system; - * Do not use TFTP to load the bootfile. - */ - net_set_state(NETLOOP_SUCCESS); - return; - } -#if defined(CONFIG_CMD_NFS) - if (strcmp(s, "NFS") == 0) { - /* - * Use NFS to load the bootfile. - */ - NfsStart(); - return; - } + if (s != NULL && strcmp(s, "NFS") == 0) { + /* + * Use NFS to load the bootfile. + */ + NfsStart(); + return; + } #endif + if (getenv_yesno("autoload") == 0) { + /* + * Just use BOOTP/RARP to configure system; + * Do not use TFTP to load the bootfile. + */ + net_set_state(NETLOOP_SUCCESS); + return; } TftpStart(TFTPGET); } -- cgit v0.10.2 From be11235ab802844e12d84921a38fd8ae4ddda080 Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 11 Dec 2012 22:16:23 -0600 Subject: env: Hide '.' variables in env print by default When printing all variables with env print, don't print variables that begin with '.'. If env print is called with a '-a' switch, then include variables that begin with '.' (just like the ls command). Variables printed explicitly will be printed even without the -a. Signed-off-by: Joe Hershberger diff --git a/board/esd/pmc440/cmd_pmc440.c b/board/esd/pmc440/cmd_pmc440.c index f1ffb7b..e9a78a3 100644 --- a/board/esd/pmc440/cmd_pmc440.c +++ b/board/esd/pmc440/cmd_pmc440.c @@ -391,7 +391,7 @@ int do_painit(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) nextbase -= ((CONFIG_ENV_SIZE + 4096 - 1) & ~(4096 - 1)); envp = (env_t *)nextbase; res = (char *)envp->data; - len = hexport_r(&env_htab, '\0', &res, ENV_SIZE, 0, NULL); + len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL); if (len < 0) { error("Cannot export environment: errno = %d\n", errno); return 1; diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c index da5689c..022ad39 100644 --- a/common/cmd_nvedit.c +++ b/common/cmd_nvedit.c @@ -106,7 +106,7 @@ int get_env_id(void) * * Returns 0 in case of error, or length of printed string */ -static int env_print(char *name) +static int env_print(char *name, int flag) { char *res = NULL; size_t len; @@ -116,7 +116,7 @@ static int env_print(char *name) e.key = name; e.data = NULL; - hsearch_r(e, FIND, &ep, &env_htab, 0); + hsearch_r(e, FIND, &ep, &env_htab, flag); if (ep == NULL) return 0; len = printf("%s=%s\n", ep->key, ep->data); @@ -124,7 +124,7 @@ static int env_print(char *name) } /* print whole list */ - len = hexport_r(&env_htab, '\n', &res, 0, 0, NULL); + len = hexport_r(&env_htab, '\n', flag, &res, 0, 0, NULL); if (len > 0) { puts(res); @@ -141,10 +141,17 @@ static int do_env_print(cmd_tbl_t *cmdtp, int flag, int argc, { int i; int rcode = 0; + int env_flag = H_HIDE_DOT; + + if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'a') { + argc--; + argv++; + env_flag &= ~H_HIDE_DOT; + } if (argc == 1) { /* print all env vars */ - rcode = env_print(NULL); + rcode = env_print(NULL, env_flag); if (!rcode) return 1; printf("\nEnvironment size: %d/%ld bytes\n", @@ -153,8 +160,9 @@ static int do_env_print(cmd_tbl_t *cmdtp, int flag, int argc, } /* print selected env vars */ + env_flag &= ~H_HIDE_DOT; for (i = 1; i < argc; ++i) { - int rc = env_print(argv[i]); + int rc = env_print(argv[i], env_flag); if (!rc) { printf("## Error: \"%s\" not defined\n", argv[i]); ++rcode; @@ -807,7 +815,7 @@ NXTARG: ; argv++; if (sep) { /* export as text file */ - len = hexport_r(&env_htab, sep, &addr, size, argc, argv); + len = hexport_r(&env_htab, sep, 0, &addr, size, argc, argv); if (len < 0) { error("Cannot export environment: errno = %d\n", errno); return 1; @@ -825,7 +833,7 @@ NXTARG: ; else /* export as raw binary data */ res = addr; - len = hexport_r(&env_htab, '\0', &res, ENV_SIZE, argc, argv); + len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, argc, argv); if (len < 0) { error("Cannot export environment: errno = %d\n", errno); return 1; @@ -1037,7 +1045,7 @@ static char env_help_text[] = #if defined(CONFIG_CMD_IMPORTENV) "env import [-d] [-t | -b | -c] addr [size] - import environment\n" #endif - "env print [name ...] - print environment\n" + "env print [-a | name ...] - print environment\n" #if defined(CONFIG_CMD_RUN) "env run var [...] - run commands in an environment variable\n" #endif @@ -1069,7 +1077,7 @@ U_BOOT_CMD_COMPLETE( U_BOOT_CMD_COMPLETE( printenv, CONFIG_SYS_MAXARGS, 1, do_env_print, "print environment variables", - "\n - print values of all environment variables\n" + "[-a]\n - print [all] values of all environment variables\n" "printenv name ...\n" " - print value of environment variable 'name'", var_complete diff --git a/common/env_dataflash.c b/common/env_dataflash.c index 3c5af37..38c9615 100644 --- a/common/env_dataflash.c +++ b/common/env_dataflash.c @@ -60,7 +60,7 @@ int saveenv(void) char *res; res = (char *)&env_new.data; - len = hexport_r(&env_htab, '\0', &res, ENV_SIZE, 0, NULL); + len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL); if (len < 0) { error("Cannot export environment: errno = %d\n", errno); return 1; diff --git a/common/env_eeprom.c b/common/env_eeprom.c index b66bba2..45c935b 100644 --- a/common/env_eeprom.c +++ b/common/env_eeprom.c @@ -139,7 +139,7 @@ int saveenv(void) BUG_ON(env_ptr != NULL); res = (char *)&env_new.data; - len = hexport_r(&env_htab, '\0', &res, ENV_SIZE, 0, NULL); + len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL); if (len < 0) { error("Cannot export environment: errno = %d\n", errno); return 1; diff --git a/common/env_fat.c b/common/env_fat.c index 6ef5318..c0f18ab 100644 --- a/common/env_fat.c +++ b/common/env_fat.c @@ -61,7 +61,7 @@ int saveenv(void) int err; res = (char *)&env_new.data; - len = hexport_r(&env_htab, '\0', &res, ENV_SIZE, 0, NULL); + len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL); if (len < 0) { error("Cannot export environment: errno = %d\n", errno); return 1; diff --git a/common/env_flash.c b/common/env_flash.c index aa970d4..e07d336 100644 --- a/common/env_flash.c +++ b/common/env_flash.c @@ -142,7 +142,7 @@ int saveenv(void) goto done; res = (char *)&env_new.data; - len = hexport_r(&env_htab, '\0', &res, ENV_SIZE, 0, NULL); + len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL); if (len < 0) { error("Cannot export environment: errno = %d\n", errno); goto done; @@ -275,7 +275,7 @@ int saveenv(void) goto done; res = (char *)&env_new.data; - len = hexport_r(&env_htab, '\0', &res, ENV_SIZE, 0, NULL); + len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL); if (len < 0) { error("Cannot export environment: errno = %d\n", errno); goto done; diff --git a/common/env_mmc.c b/common/env_mmc.c index a2ff90b..ce21671 100644 --- a/common/env_mmc.c +++ b/common/env_mmc.c @@ -130,7 +130,7 @@ int saveenv(void) } res = (char *)&env_new->data; - len = hexport_r(&env_htab, '\0', &res, ENV_SIZE, 0, NULL); + len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL); if (len < 0) { error("Cannot export environment: errno = %d\n", errno); ret = 1; diff --git a/common/env_nand.c b/common/env_nand.c index 79e8033..22e72a2 100644 --- a/common/env_nand.c +++ b/common/env_nand.c @@ -186,7 +186,7 @@ int saveenv(void) return 1; res = (char *)&env_new.data; - len = hexport_r(&env_htab, '\0', &res, ENV_SIZE, 0, NULL); + len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL); if (len < 0) { error("Cannot export environment: errno = %d\n", errno); return 1; @@ -239,7 +239,7 @@ int saveenv(void) return 1; res = (char *)&env_new->data; - len = hexport_r(&env_htab, '\0', &res, ENV_SIZE, 0, NULL); + len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL); if (len < 0) { error("Cannot export environment: errno = %d\n", errno); return 1; diff --git a/common/env_nvram.c b/common/env_nvram.c index 6483db3..eab0e7b 100644 --- a/common/env_nvram.c +++ b/common/env_nvram.c @@ -90,7 +90,7 @@ int saveenv(void) int rcode = 0; res = (char *)&env_new.data; - len = hexport_r(&env_htab, '\0', &res, ENV_SIZE, 0, NULL); + len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL); if (len < 0) { error("Cannot export environment: errno = %d\n", errno); return 1; diff --git a/common/env_onenand.c b/common/env_onenand.c index da35071..faa903d 100644 --- a/common/env_onenand.c +++ b/common/env_onenand.c @@ -95,7 +95,7 @@ int saveenv(void) }; res = (char *)&env_new.data; - len = hexport_r(&env_htab, '\0', &res, ENV_SIZE, 0, NULL); + len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL); if (len < 0) { error("Cannot export environment: errno = %d\n", errno); return 1; diff --git a/common/env_sf.c b/common/env_sf.c index bbd472f..d9e9085 100644 --- a/common/env_sf.c +++ b/common/env_sf.c @@ -79,7 +79,7 @@ int saveenv(void) } res = (char *)&env_new.data; - len = hexport_r(&env_htab, '\0', &res, ENV_SIZE, 0, NULL); + len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL); if (len < 0) { error("Cannot export environment: errno = %d\n", errno); return 1; @@ -277,7 +277,7 @@ int saveenv(void) } res = (char *)&env_new.data; - len = hexport_r(&env_htab, '\0', &res, ENV_SIZE, 0, NULL); + len = hexport_r(&env_htab, '\0', 0, &res, ENV_SIZE, 0, NULL); if (len < 0) { error("Cannot export environment: errno = %d\n", errno); goto done; diff --git a/include/search.h b/include/search.h index fa00ea1..1e48deb 100644 --- a/include/search.h +++ b/include/search.h @@ -107,7 +107,7 @@ extern int hdelete_r(const char *__key, struct hsearch_data *__htab, int __flag); extern ssize_t hexport_r(struct hsearch_data *__htab, - const char __sep, char **__resp, size_t __size, + const char __sep, int __flag, char **__resp, size_t __size, int argc, char * const argv[]); /* @@ -120,9 +120,10 @@ extern int himport_r(struct hsearch_data *__htab, const char *__env, size_t __size, const char __sep, int __flag, int nvars, char * const vars[]); -/* Flags for himport_r(), hdelete_r(), and hsearch_r() */ +/* Flags for himport_r(), hexport_r(), hdelete_r(), and hsearch_r() */ #define H_NOCLEAR (1 << 0) /* do not clear hash table before importing */ #define H_FORCE (1 << 1) /* overwrite read-only/write-once variables */ #define H_INTERACTIVE (1 << 2) /* indicate that an import is user directed */ +#define H_HIDE_DOT (1 << 3) /* don't print env vars that begin with '.' */ #endif /* search.h */ diff --git a/lib/hashtable.c b/lib/hashtable.c index 6861a42..7c6b96c 100644 --- a/lib/hashtable.c +++ b/lib/hashtable.c @@ -522,7 +522,7 @@ static int cmpkey(const void *p1, const void *p2) return (strcmp(e1->key, e2->key)); } -ssize_t hexport_r(struct hsearch_data *htab, const char sep, +ssize_t hexport_r(struct hsearch_data *htab, const char sep, int flag, char **resp, size_t size, int argc, char * const argv[]) { @@ -559,6 +559,9 @@ ssize_t hexport_r(struct hsearch_data *htab, const char sep, if ((argc > 0) && (found == 0)) continue; + if ((flag & H_HIDE_DOT) && ep->key[0] == '.') + continue; + list[n++] = ep; totlen += strlen(ep->key) + 2; -- cgit v0.10.2 From 170ab11075d3be56e89d6444abf1148329130f4b Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 11 Dec 2012 22:16:24 -0600 Subject: env: Add support for callbacks to environment vars Add support for per-variable callbacks to the "hashtable" functions. Signed-off-by: Joe Hershberger !!!fix comment in callback diff --git a/README b/README index a80441c..e00a7c2 100644 --- a/README +++ b/README @@ -4213,6 +4213,36 @@ Please note that changes to some configuration parameters may take only effect after the next boot (yes, that's just like Windoze :-). +Callback functions for environment variables: +--------------------------------------------- + +For some environment variables, the behavior of u-boot needs to change +when their values are changed. This functionailty allows functions to +be associated with arbitrary variables. On creation, overwrite, or +deletion, the callback will provide the opportunity for some side +effect to happen or for the change to be rejected. + +The callbacks are named and associated with a function using the +U_BOOT_ENV_CALLBACK macro in your board or driver code. + +These callbacks are associated with variables in one of two ways. The +static list can be added to by defining CONFIG_ENV_CALLBACK_LIST_STATIC +in the board configuration to a string that defines a list of +associations. The list must be in the following format: + + entry = variable_name[:callback_name] + list = entry[,list] + +If the callback name is not specified, then the callback is deleted. +Spaces are also allowed anywhere in the list. + +Callbacks can also be associated by defining the ".callbacks" variable +with the same list format above. Any association in ".callbacks" will +override any association in the static list. You can define +CONFIG_ENV_CALLBACK_LIST_DEFAULT to a list (string) to define the +".callbacks" envirnoment variable in the default or embedded environment. + + Command Line Parsing: ===================== diff --git a/common/Makefile b/common/Makefile index ce30359..04812e9 100644 --- a/common/Makefile +++ b/common/Makefile @@ -44,6 +44,8 @@ COBJS-y += cmd_nvedit.o COBJS-y += cmd_version.o # environment +COBJS-y += env_attr.o +COBJS-y += env_callback.o COBJS-y += env_common.o COBJS-$(CONFIG_ENV_IS_IN_DATAFLASH) += env_dataflash.o COBJS-$(CONFIG_ENV_IS_IN_EEPROM) += env_eeprom.o @@ -207,6 +209,8 @@ COBJS-y += env_common.o COBJS-$(CONFIG_ENV_IS_IN_FLASH) += env_flash.o COBJS-$(CONFIG_SPL_YMODEM_SUPPORT) += xyzModem.o COBJS-$(CONFIG_SPL_NET_SUPPORT) += cmd_nvedit.o +COBJS-$(CONFIG_SPL_NET_SUPPORT) += env_attr.o +COBJS-$(CONFIG_SPL_NET_SUPPORT) += env_callback.o COBJS-$(CONFIG_SPL_NET_SUPPORT) += env_common.o COBJS-$(CONFIG_SPL_NET_SUPPORT) += env_nowhere.o COBJS-$(CONFIG_SPL_NET_SUPPORT) += miiphyutil.o diff --git a/common/env_attr.c b/common/env_attr.c new file mode 100644 index 0000000..7d330a5 --- /dev/null +++ b/common/env_attr.c @@ -0,0 +1,222 @@ +/* + * (C) Copyright 2012 + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + * + * 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 +#include +#include +#include +#include + +/* + * Iterate through the whole list calling the callback for each found element. + * "attr_list" takes the form: + * attributes = [^,:\s]* + * entry = name[:attributes] + * list = entry[,list] + */ +int env_attr_walk(const char *attr_list, + int (*callback)(const char *name, const char *attributes)) +{ + const char *entry, *entry_end; + char *name, *attributes; + + if (!attr_list) + /* list not found */ + return 1; + + entry = attr_list; + do { + char *entry_cpy = NULL; + + entry_end = strchr(entry, ENV_ATTR_LIST_DELIM); + /* check if this is the last entry in the list */ + if (entry_end == NULL) { + int entry_len = strlen(entry); + + if (entry_len) { + /* + * allocate memory to copy the entry into since + * we will need to inject '\0' chars and squash + * white-space before calling the callback + */ + entry_cpy = malloc(entry_len + 1); + if (entry_cpy) + /* copy the rest of the list */ + strcpy(entry_cpy, entry); + else + return -ENOMEM; + } + } else { + int entry_len = entry_end - entry; + + if (entry_len) { + /* + * allocate memory to copy the entry into since + * we will need to inject '\0' chars and squash + * white-space before calling the callback + */ + entry_cpy = malloc(entry_len + 1); + if (entry_cpy) { + /* copy just this entry and null term */ + strncpy(entry_cpy, entry, entry_len); + entry_cpy[entry_len] = '\0'; + } else + return -ENOMEM; + } + } + + /* check if there is anything to process (e.g. not ",,,") */ + if (entry_cpy != NULL) { + attributes = strchr(entry_cpy, ENV_ATTR_SEP); + /* check if there is a ':' */ + if (attributes != NULL) { + /* replace the ':' with '\0' to term name */ + *attributes++ = '\0'; + /* remove white-space from attributes */ + attributes = strim(attributes); + } + /* remove white-space from name */ + name = strim(entry_cpy); + + /* only call the callback if there is a name */ + if (strlen(name) != 0) { + int retval = 0; + + retval = callback(name, attributes); + if (retval) { + free(entry_cpy); + return retval; + } + } + } + + free(entry_cpy); + entry = entry_end + 1; + } while (entry_end != NULL); + + return 0; +} + +/* + * Search for the last matching string in another string with the option to + * start looking at a certain point (i.e. ignore anything beyond that point). + */ +static char *reverse_strstr(const char *searched, const char *search_for, + const char *searched_start) +{ + char *result = NULL; + + if (*search_for == '\0') + return (char *)searched; + + for (;;) { + char *match = strstr(searched, search_for); + + /* + * Stop looking if no new match is found or looking past the + * searched_start pointer + */ + if (match == NULL || (searched_start != NULL && + match + strlen(search_for) > searched_start)) + break; + + result = match; + searched = match + 1; + } + + return result; +} + +/* + * Retrieve the attributes string associated with a single name in the list + * There is no protection on attributes being too small for the value + */ +int env_attr_lookup(const char *attr_list, const char *name, char *attributes) +{ + const char *entry = NULL; + + if (!attributes) + /* bad parameter */ + return -1; + if (!attr_list) + /* list not found */ + return 1; + + entry = reverse_strstr(attr_list, name, NULL); + while (entry != NULL) { + const char *prevch = entry - 1; + const char *nextch = entry + strlen(name); + + /* Skip spaces */ + while (*prevch == ' ') + prevch--; + while (*nextch == ' ') + nextch++; + + /* check for an exact match */ + if ((entry == attr_list || + *prevch == ENV_ATTR_LIST_DELIM) && + (*nextch == ENV_ATTR_SEP || + *nextch == ENV_ATTR_LIST_DELIM || + *nextch == '\0')) + break; + + entry = reverse_strstr(attr_list, name, entry); + } + if (entry != NULL) { + int len; + + /* skip the name */ + entry += strlen(name); + /* skip spaces */ + while (*entry == ' ') + entry++; + if (*entry != ENV_ATTR_SEP) + len = 0; + else { + const char *delim; + static const char delims[] = { + ENV_ATTR_LIST_DELIM, ' ', '\0'}; + + /* skip the attr sep */ + entry += 1; + /* skip spaces */ + while (*entry == ' ') + entry++; + + delim = strpbrk(entry, delims); + if (delim == NULL) + len = strlen(entry); + else + len = delim - entry; + memcpy(attributes, entry, len); + } + attributes[len] = '\0'; + + /* success */ + return 0; + } + + /* not found in list */ + return 2; +} diff --git a/common/env_callback.c b/common/env_callback.c new file mode 100644 index 0000000..78ca367 --- /dev/null +++ b/common/env_callback.c @@ -0,0 +1,144 @@ +/* + * (C) Copyright 2012 + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + * + * 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 +#include + +#if defined(CONFIG_NEEDS_MANUAL_RELOC) +DECLARE_GLOBAL_DATA_PTR; +#endif + +/* + * Look up a callback function pointer by name + */ +struct env_clbk_tbl *find_env_callback(const char *name) +{ + struct env_clbk_tbl *clbkp; + int i; + int num_callbacks = ll_entry_count(struct env_clbk_tbl, env_clbk); + + if (name == NULL) + return NULL; + + /* look up the callback in the linker-list */ + for (i = 0, clbkp = ll_entry_start(struct env_clbk_tbl, env_clbk); + i < num_callbacks; + i++, clbkp++) { + if (strcmp(name, clbkp->name) == 0) + return clbkp; + } + + return NULL; +} + +/* + * Look for a possible callback for a newly added variable + * This is called specifically when the variable did not exist in the hash + * previously, so the blanket update did not find this variable. + */ +void env_callback_init(ENTRY *var_entry) +{ + const char *var_name = var_entry->key; + const char *callback_list = getenv(ENV_CALLBACK_VAR); + char callback_name[256] = ""; + struct env_clbk_tbl *clbkp; + int ret = 1; + + /* look in the ".callbacks" var for a reference to this variable */ + if (callback_list != NULL) + ret = env_attr_lookup(callback_list, var_name, callback_name); + + /* only if not found there, look in the static list */ + if (ret) + ret = env_attr_lookup(ENV_CALLBACK_LIST_STATIC, var_name, + callback_name); + + /* if an association was found, set the callback pointer */ + if (!ret && strlen(callback_name)) { + clbkp = find_env_callback(callback_name); + if (clbkp != NULL) +#if defined(CONFIG_NEEDS_MANUAL_RELOC) + var_entry->callback = clbkp->callback + gd->reloc_off; +#else + var_entry->callback = clbkp->callback; +#endif + } +} + +/* + * Called on each existing env var prior to the blanket update since removing + * a callback association should remove its callback. + */ +static int clear_callback(ENTRY *entry) +{ + entry->callback = NULL; + + return 0; +} + +/* + * Call for each element in the list that associates variables to callbacks + */ +static int set_callback(const char *name, const char *value) +{ + ENTRY e, *ep; + struct env_clbk_tbl *clbkp; + + e.key = name; + e.data = NULL; + hsearch_r(e, FIND, &ep, &env_htab, 0); + + /* does the env variable actually exist? */ + if (ep != NULL) { + /* the assocaition delares no callback, so remove the pointer */ + if (value == NULL || strlen(value) == 0) + ep->callback = NULL; + else { + /* assign the requested callback */ + clbkp = find_env_callback(value); + if (clbkp != NULL) +#if defined(CONFIG_NEEDS_MANUAL_RELOC) + ep->callback = clbkp->callback + gd->reloc_off; +#else + ep->callback = clbkp->callback; +#endif + } + } + + return 0; +} + +static int on_callbacks(const char *name, const char *value, enum env_op op, + int flags) +{ + /* remove all callbacks */ + hwalk_r(&env_htab, clear_callback); + + /* configure any static callback bindings */ + env_attr_walk(ENV_CALLBACK_LIST_STATIC, set_callback); + /* configure any dynamic callback bindings */ + env_attr_walk(value, set_callback); + + return 0; +} +U_BOOT_ENV_CALLBACK(callbacks, on_callbacks); diff --git a/include/env_attr.h b/include/env_attr.h new file mode 100644 index 0000000..6ef114f --- /dev/null +++ b/include/env_attr.h @@ -0,0 +1,55 @@ +/* + * (C) Copyright 2012 + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + * + * 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 __ENV_ATTR_H__ +#define __ENV_ATTR_H__ + +#define ENV_ATTR_LIST_DELIM ',' +#define ENV_ATTR_SEP ':' + +/* + * env_attr_walk takes as input an "attr_list" that takes the form: + * attributes = [^,:\s]* + * entry = name[:attributes] + * list = entry[,list] + * It will call the "callback" function with the "name" and attribute as "value" + * The callback may return a non-0 to abort the list walk. + * This return value will be passed through to the caller. + * 0 is returned on success. + */ +extern int env_attr_walk(const char *attr_list, + int (*callback)(const char *name, const char *value)); + +/* + * env_attr_lookup takes as input an "attr_list" with the same form as above. + * It also takes as input a "name" to look for. + * If the name is found in the list, it's value is copied into "attributes". + * There is no protection on attributes being too small for the value. + * It returns -1 if attributes is NULL, 1 if "name" is not found, 2 if + * "attr_list" is NULL. + * Returns 0 on success. + */ +extern int env_attr_lookup(const char *attr_list, const char *name, + char *attributes); + +#endif /* __ENV_ATTR_H__ */ diff --git a/include/env_callback.h b/include/env_callback.h new file mode 100644 index 0000000..5a460f3 --- /dev/null +++ b/include/env_callback.h @@ -0,0 +1,62 @@ +/* + * (C) Copyright 2012 + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + * + * 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 __ENV_CALLBACK_H__ +#define __ENV_CALLBACK_H__ + +#include +#include + +#define ENV_CALLBACK_VAR ".callbacks" + +/* Board configs can define additional static callback bindings */ +#ifndef CONFIG_ENV_CALLBACK_LIST_STATIC +#define CONFIG_ENV_CALLBACK_LIST_STATIC +#endif + +/* + * This list of callback bindings is static, but may be overridden by defining + * a new association in the ".callbacks" environment variable. + */ +#define ENV_CALLBACK_LIST_STATIC ENV_CALLBACK_VAR ":callbacks," \ + CONFIG_ENV_CALLBACK_LIST_STATIC + +struct env_clbk_tbl { + const char *name; /* Callback name */ + int (*callback)(const char *name, const char *value, enum env_op op, + int flags); +}; + +struct env_clbk_tbl *find_env_callback(const char *); +void env_callback_init(ENTRY *var_entry); + +/* + * Define a callback that can be associated with variables. + * when associated through the ".callbacks" environment variable, the callback + * will be executed any time the variable is inserted, overwritten, or deleted. + */ +#define U_BOOT_ENV_CALLBACK(name, callback) \ + ll_entry_declare(struct env_clbk_tbl, name, env_clbk, env_clbk) = \ + {#name, callback} + +#endif /* __ENV_CALLBACK_H__ */ diff --git a/include/env_default.h b/include/env_default.h index a1db73a..d05eba1 100644 --- a/include/env_default.h +++ b/include/env_default.h @@ -24,6 +24,8 @@ * MA 02111-1307 USA */ +#include + #ifdef DEFAULT_ENV_INSTANCE_EMBEDDED env_t environment __PPCENV__ = { ENV_CRC, /* CRC Sum */ @@ -36,6 +38,9 @@ static char default_environment[] = { #else const uchar default_environment[] = { #endif +#ifdef CONFIG_ENV_CALLBACK_LIST_DEFAULT + ENV_CALLBACK_VAR "=" CONFIG_ENV_CALLBACK_LIST_DEFAULT "\0" +#endif #ifdef CONFIG_BOOTARGS "bootargs=" CONFIG_BOOTARGS "\0" #endif diff --git a/include/environment.h b/include/environment.h index 4b19f32..6c30215 100644 --- a/include/environment.h +++ b/include/environment.h @@ -164,6 +164,8 @@ extern void env_reloc(void); #ifndef DO_DEPS_ONLY +#include +#include #include extern struct hsearch_data env_htab; diff --git a/include/search.h b/include/search.h index 1e48deb..d68e24a 100644 --- a/include/search.h +++ b/include/search.h @@ -47,6 +47,8 @@ typedef enum { typedef struct entry { const char *key; char *data; + int (*callback)(const char *name, const char *value, enum env_op op, + int flags); } ENTRY; /* Opaque type for internal use. */ @@ -120,6 +122,9 @@ extern int himport_r(struct hsearch_data *__htab, const char *__env, size_t __size, const char __sep, int __flag, int nvars, char * const vars[]); +/* Walk the whole table calling the callback on each element */ +extern int hwalk_r(struct hsearch_data *__htab, int (*callback)(ENTRY *)); + /* Flags for himport_r(), hexport_r(), hdelete_r(), and hsearch_r() */ #define H_NOCLEAR (1 << 0) /* do not clear hash table before importing */ #define H_FORCE (1 << 1) /* overwrite read-only/write-once variables */ diff --git a/lib/hashtable.c b/lib/hashtable.c index 7c6b96c..e922666 100644 --- a/lib/hashtable.c +++ b/lib/hashtable.c @@ -54,7 +54,8 @@ #define CONFIG_ENV_MAX_ENTRIES 512 #endif -#include "search.h" +#include +#include /* * [Aho,Sethi,Ullman] Compilers: Principles, Techniques and Tools, 1986 @@ -274,6 +275,17 @@ static inline int _compare_and_overwrite_entry(ENTRY item, ACTION action, return 0; } + /* If there is a callback, call it */ + if (htab->table[idx].entry.callback && + htab->table[idx].entry.callback(item.key, + item.data, env_op_overwrite, flag)) { + debug("callback() rejected setting variable " + "%s, skipping it!\n", item.key); + __set_errno(EINVAL); + *retval = NULL; + return 0; + } + free(htab->table[idx].entry.data); htab->table[idx].entry.data = strdup(item.data); if (!htab->table[idx].entry.data) { @@ -398,6 +410,9 @@ int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval, ++htab->filled; + /* This is a new entry, so look up a possible callback */ + env_callback_init(&htab->table[idx].entry); + /* check for permission */ if (htab->change_ok != NULL && htab->change_ok( &htab->table[idx].entry, item.data, env_op_create, flag)) { @@ -409,6 +424,18 @@ int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval, return 0; } + /* If there is a callback, call it */ + if (htab->table[idx].entry.callback && + htab->table[idx].entry.callback(item.key, item.data, + env_op_create, flag)) { + debug("callback() rejected setting variable " + "%s, skipping it!\n", item.key); + _hdelete(item.key, htab, &htab->table[idx].entry, idx); + __set_errno(EINVAL); + *retval = NULL; + return 0; + } + /* return new entry */ *retval = &htab->table[idx].entry; return 1; @@ -437,6 +464,7 @@ static void _hdelete(const char *key, struct hsearch_data *htab, ENTRY *ep, debug("hdelete: DELETING key \"%s\"\n", key); free((void *)ep->key); free(ep->data); + ep->callback = NULL; htab->table[idx].used = -1; --htab->filled; @@ -466,6 +494,15 @@ int hdelete_r(const char *key, struct hsearch_data *htab, int flag) return 0; } + /* If there is a callback, call it */ + if (htab->table[idx].entry.callback && + htab->table[idx].entry.callback(key, NULL, env_op_delete, flag)) { + debug("callback() rejected deleting variable " + "%s, skipping it!\n", key); + __set_errno(EINVAL); + return 0; + } + _hdelete(key, htab, ep, idx); return 1; @@ -838,11 +875,9 @@ int himport_r(struct hsearch_data *htab, e.data = value; hsearch_r(e, ENTER, &rv, htab, flag); - if (rv == NULL) { + if (rv == NULL) printf("himport_r: can't insert \"%s=%s\" into hash table\n", name, value); - return 0; - } debug("INSERT: table %p, filled %d/%d rv %p ==> name=\"%s\" value=\"%s\"\n", htab, htab->filled, htab->size, @@ -873,3 +908,27 @@ int himport_r(struct hsearch_data *htab, debug("INSERT: done\n"); return 1; /* everything OK */ } + +/* + * hwalk_r() + */ + +/* + * Walk all of the entries in the hash, calling the callback for each one. + * this allows some generic operation to be performed on each element. + */ +int hwalk_r(struct hsearch_data *htab, int (*callback)(ENTRY *)) +{ + int i; + int retval; + + for (i = 1; i <= htab->size; ++i) { + if (htab->table[i].used > 0) { + retval = callback(&htab->table[i].entry); + if (retval) + return retval; + } + } + + return 0; +} -- cgit v0.10.2 From 5e2b3e0c59df3313254941b453ffeadba9e673ae Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 11 Dec 2012 22:16:25 -0600 Subject: env: Add a command to view callbacks The callbacks can be bound, but are otherwise invisible. Add a command to show what callbacks are available. Signed-off-by: Joe Hershberger !!! fix callback command diff --git a/README b/README index e00a7c2..ac61a32 100644 --- a/README +++ b/README @@ -815,6 +815,7 @@ The following options need to be configured: CONFIG_CMD_EDITENV edit env variable CONFIG_CMD_EEPROM * EEPROM read/write support CONFIG_CMD_ELF * bootelf, bootvx + CONFIG_CMD_ENV_CALLBACK * display details about env callbacks CONFIG_CMD_EXPORTENV * export the environment CONFIG_CMD_EXT2 * ext2 command support CONFIG_CMD_EXT4 * ext4 command support diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c index 022ad39..1e51b9d 100644 --- a/common/cmd_nvedit.c +++ b/common/cmd_nvedit.c @@ -514,6 +514,81 @@ int do_env_ask(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } #endif +#if defined(CONFIG_CMD_ENV_CALLBACK) +static int print_static_binding(const char *var_name, const char *callback_name) +{ + printf("\t%-20s %-20s\n", var_name, callback_name); + + return 0; +} + +static int print_active_callback(ENTRY *entry) +{ + struct env_clbk_tbl *clbkp; + int i; + int num_callbacks; + + if (entry->callback == NULL) + return 0; + + /* look up the callback in the linker-list */ + num_callbacks = ll_entry_count(struct env_clbk_tbl, env_clbk); + for (i = 0, clbkp = ll_entry_start(struct env_clbk_tbl, env_clbk); + i < num_callbacks; + i++, clbkp++) { +#if defined(CONFIG_NEEDS_MANUAL_RELOC) + if (entry->callback == clbkp->callback + gd->reloc_off) +#else + if (entry->callback == clbkp->callback) +#endif + break; + } + + if (i == num_callbacks) + /* this should probably never happen, but just in case... */ + printf("\t%-20s %p\n", entry->key, entry->callback); + else + printf("\t%-20s %-20s\n", entry->key, clbkp->name); + + return 0; +} + +/* + * Print the callbacks available and what they are bound to + */ +int do_env_callback(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + struct env_clbk_tbl *clbkp; + int i; + int num_callbacks; + + /* Print the available callbacks */ + puts("Available callbacks:\n"); + puts("\tCallback Name\n"); + puts("\t-------------\n"); + num_callbacks = ll_entry_count(struct env_clbk_tbl, env_clbk); + for (i = 0, clbkp = ll_entry_start(struct env_clbk_tbl, env_clbk); + i < num_callbacks; + i++, clbkp++) + printf("\t%s\n", clbkp->name); + puts("\n"); + + /* Print the static bindings that may exist */ + puts("Static callback bindings:\n"); + printf("\t%-20s %-20s\n", "Variable Name", "Callback Name"); + printf("\t%-20s %-20s\n", "-------------", "-------------"); + env_attr_walk(ENV_CALLBACK_LIST_STATIC, print_static_binding); + puts("\n"); + + /* walk through each variable and print the callback if it has one */ + puts("Active callback bindings:\n"); + printf("\t%-20s %-20s\n", "Variable Name", "Callback Name"); + printf("\t%-20s %-20s\n", "-------------", "-------------"); + hwalk_r(&env_htab, print_active_callback); + return 0; +} +#endif + /* * Interactively edit an environment variable */ @@ -981,6 +1056,9 @@ static cmd_tbl_t cmd_env_sub[] = { #if defined(CONFIG_CMD_EDITENV) U_BOOT_CMD_MKENT(edit, 2, 0, do_env_edit, "", ""), #endif +#if defined(CONFIG_CMD_ENV_CALLBACK) + U_BOOT_CMD_MKENT(callbacks, 1, 0, do_env_callback, "", ""), +#endif #if defined(CONFIG_CMD_EXPORTENV) U_BOOT_CMD_MKENT(export, 4, 0, do_env_export, "", ""), #endif @@ -1031,6 +1109,9 @@ static char env_help_text[] = #if defined(CONFIG_CMD_ASKENV) "ask name [message] [size] - ask for environment variable\nenv " #endif +#if defined(CONFIG_CMD_ENV_CALLBACK) + "callbacks - print callbacks and their associated variables\nenv " +#endif "default [-f] -a - [forcibly] reset default environment\n" "env default [-f] var [...] - [forcibly] reset variable(s) to their default values\n" #if defined(CONFIG_CMD_EDITENV) -- cgit v0.10.2 From a9f51c9b4315f699e7185c85d1d09d2d7819a4eb Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 11 Dec 2012 22:16:26 -0600 Subject: env: Add a bootfile env handler Remove the hard-coded bootfile handler and use a callback instead Signed-off-by: Joe Hershberger diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c index 1e51b9d..874baef 100644 --- a/common/cmd_nvedit.c +++ b/common/cmd_nvedit.c @@ -50,9 +50,6 @@ #include #include #include -#if defined(CONFIG_CMD_NET) -#include -#endif DECLARE_GLOBAL_DATA_PTR; @@ -328,12 +325,6 @@ int env_change_ok(const ENTRY *item, const char *newval, enum env_op op, load_addr = simple_strtoul(newval, NULL, 16); return 0; } -#if defined(CONFIG_CMD_NET) - else if (strcmp(name, "bootfile") == 0) { - copy_filename(BootFile, newval, sizeof(BootFile)); - return 0; - } -#endif return 0; } diff --git a/include/env_callback.h b/include/env_callback.h index 5a460f3..bce0136 100644 --- a/include/env_callback.h +++ b/include/env_callback.h @@ -39,6 +39,7 @@ * a new association in the ".callbacks" environment variable. */ #define ENV_CALLBACK_LIST_STATIC ENV_CALLBACK_VAR ":callbacks," \ + "bootfile:bootfile," \ CONFIG_ENV_CALLBACK_LIST_STATIC struct env_clbk_tbl { diff --git a/net/net.c b/net/net.c index da68a61..a40cde1 100644 --- a/net/net.c +++ b/net/net.c @@ -82,6 +82,7 @@ #include #include +#include #include #if defined(CONFIG_STATUS_LED) #include @@ -208,6 +209,22 @@ static int NetTryCount; /**********************************************************************/ +static int on_bootfile(const char *name, const char *value, enum env_op op, + int flags) +{ + switch (op) { + case env_op_create: + case env_op_overwrite: + copy_filename(BootFile, value, sizeof(BootFile)); + break; + default: + break; + } + + return 0; +} +U_BOOT_ENV_CALLBACK(bootfile, on_bootfile); + /* * Check if autoload is enabled. If so, use either NFS or TFTP to download * the boot file. -- cgit v0.10.2 From 32057717e06a4e703fdf3774671cea14554de76b Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 11 Dec 2012 22:16:27 -0600 Subject: env: Add a baudrate env handler Remove the hard-coded baudrate handler and use a callback instead Signed-off-by: Joe Hershberger diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c index 874baef..df136cf 100644 --- a/common/cmd_nvedit.c +++ b/common/cmd_nvedit.c @@ -78,12 +78,6 @@ ulong save_addr; /* Default Save Address */ ulong save_size; /* Default Save Size (in bytes) */ /* - * 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])) - -/* * This variable is incremented on each do_env_set(), so it can * be used via get_env_id() as an indication, if the environment * has changed or not. So it is possible to reread an environment @@ -275,47 +269,6 @@ int env_change_ok(const ENTRY *item, const char *newval, enum env_op op, } } #endif - /* - * When we change baudrate, or we are doing an env default -a - * (which will erase all variables prior to calling this), - * we want the baudrate to actually change - for real. - */ - if (op != env_op_create || /* variable exists */ - (flag & H_NOCLEAR) == 0) { /* or env is clear */ - /* - * Switch to new baudrate if new baudrate is supported - */ - if (strcmp(name, "baudrate") == 0) { - int baudrate = simple_strtoul(newval, NULL, 10); - int i; - for (i = 0; i < N_BAUDRATES; ++i) { - if (baudrate == baudrate_table[i]) - break; - } - if (i == N_BAUDRATES) { - if ((flag & H_FORCE) == 0) - printf("## Baudrate %d bps not " - "supported\n", baudrate); - return 1; - } - if (gd->baudrate == baudrate) { - /* If unchanged, we just say it's OK */ - return 0; - } - printf("## Switch baudrate to %d bps and" - "press ENTER ...\n", baudrate); - udelay(50000); - gd->baudrate = baudrate; -#if defined(CONFIG_PPC) || defined(CONFIG_MCF52x2) - gd->bd->bi_baudrate = baudrate; -#endif - - serial_setbrg(); - udelay(50000); - while (getc() != '\r') - ; - } - } /* * Some variables should be updated when the corresponding diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c index f5f43a6..1f8955a 100644 --- a/drivers/serial/serial.c +++ b/drivers/serial/serial.c @@ -22,6 +22,7 @@ */ #include +#include #include #include #include @@ -32,6 +33,11 @@ DECLARE_GLOBAL_DATA_PTR; static struct serial_device *serial_devices; 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 @@ -46,6 +52,70 @@ static void serial_null(void) } /** + * on_baudrate() - Update the actual baudrate when the env var changes + * + * This will check for a valid baudrate and only apply it if valid. + */ +static int on_baudrate(const char *name, const char *value, enum env_op op, + int flags) +{ + int i; + int baudrate; + + switch (op) { + case env_op_create: + case env_op_overwrite: + /* + * Switch to new baudrate if new baudrate is supported + */ + baudrate = simple_strtoul(value, NULL, 10); + + /* Not actually changing */ + if (gd->baudrate == baudrate) + return 0; + + for (i = 0; i < N_BAUDRATES; ++i) { + if (baudrate == baudrate_table[i]) + break; + } + if (i == N_BAUDRATES) { + if ((flags & H_FORCE) == 0) + printf("## Baudrate %d bps not supported\n", + baudrate); + return 1; + } + if ((flags & H_INTERACTIVE) != 0) { + printf("## Switch baudrate to %d" + " bps and press ENTER ...\n", baudrate); + udelay(50000); + } + + gd->baudrate = baudrate; +#if defined(CONFIG_PPC) || defined(CONFIG_MCF52x2) + gd->bd->bi_baudrate = baudrate; +#endif + + serial_setbrg(); + + udelay(50000); + + if ((flags & H_INTERACTIVE) != 0) + while (1) { + if (getc() == '\r') + break; + } + + return 0; + case env_op_delete: + printf("## Baudrate may not be deleted\n"); + return 1; + default: + return 0; + } +} +U_BOOT_ENV_CALLBACK(baudrate, on_baudrate); + +/** * serial_initfunc() - Forward declare of driver registration routine * @name: Name of the real driver registration routine. * diff --git a/include/env_callback.h b/include/env_callback.h index bce0136..2f5048f 100644 --- a/include/env_callback.h +++ b/include/env_callback.h @@ -39,6 +39,7 @@ * a new association in the ".callbacks" environment variable. */ #define ENV_CALLBACK_LIST_STATIC ENV_CALLBACK_VAR ":callbacks," \ + "baudrate:baudrate," \ "bootfile:bootfile," \ CONFIG_ENV_CALLBACK_LIST_STATIC -- cgit v0.10.2 From 1cf0a8b2fbe38ed07b1babaaacfc22bd427f66f0 Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 11 Dec 2012 22:16:28 -0600 Subject: env: Add a loadaddr env handler Remove the hard-coded loadaddr handler and use a callback instead Signed-off-by: Joe Hershberger diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c index df136cf..9ff8b36 100644 --- a/common/cmd_nvedit.c +++ b/common/cmd_nvedit.c @@ -73,10 +73,6 @@ SPI_FLASH|NVRAM|MMC|FAT|REMOTE} or CONFIG_ENV_IS_NOWHERE */ #define MAX_ENV_SIZE (1 << 20) /* 1 MiB */ -ulong load_addr = CONFIG_SYS_LOAD_ADDR; /* Default Load Address */ -ulong save_addr; /* Default Save Address */ -ulong save_size; /* Default Save Size (in bytes) */ - /* * This variable is incremented on each do_env_set(), so it can * be used via get_env_id() as an indication, if the environment @@ -270,14 +266,6 @@ int env_change_ok(const ENTRY *item, const char *newval, enum env_op op, } #endif - /* - * Some variables should be updated when the corresponding - * entry in the environment is changed - */ - if (strcmp(name, "loadaddr") == 0) { - load_addr = simple_strtoul(newval, NULL, 16); - return 0; - } return 0; } diff --git a/common/image.c b/common/image.c index 69e888c..95498e6 100644 --- a/common/image.c +++ b/common/image.c @@ -43,6 +43,7 @@ #include #endif +#include #include #if defined(CONFIG_FIT) || defined(CONFIG_OF_LIBFDT) @@ -416,6 +417,26 @@ static const image_header_t *image_get_ramdisk(ulong rd_addr, uint8_t arch, /* Shared dual-format routines */ /*****************************************************************************/ #ifndef USE_HOSTCC +ulong load_addr = CONFIG_SYS_LOAD_ADDR; /* Default Load Address */ +ulong save_addr; /* Default Save Address */ +ulong save_size; /* Default Save Size (in bytes) */ + +static int on_loadaddr(const char *name, const char *value, enum env_op op, + int flags) +{ + switch (op) { + case env_op_create: + case env_op_overwrite: + load_addr = simple_strtoul(value, NULL, 16); + break; + default: + break; + } + + return 0; +} +U_BOOT_ENV_CALLBACK(loadaddr, on_loadaddr); + ulong getenv_bootm_low(void) { char *s = getenv("bootm_low"); diff --git a/include/env_callback.h b/include/env_callback.h index 2f5048f..bb398ec 100644 --- a/include/env_callback.h +++ b/include/env_callback.h @@ -41,6 +41,7 @@ #define ENV_CALLBACK_LIST_STATIC ENV_CALLBACK_VAR ":callbacks," \ "baudrate:baudrate," \ "bootfile:bootfile," \ + "loadaddr:loadaddr," \ CONFIG_ENV_CALLBACK_LIST_STATIC struct env_clbk_tbl { -- cgit v0.10.2 From 849d5d9cda0e7c94797874d842e9b132ec45a565 Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 11 Dec 2012 22:16:29 -0600 Subject: env: Add a console env handler Remove the hard-coded console handler and use a callback instead Signed-off-by: Joe Hershberger diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c index 9ff8b36..cb191cd 100644 --- a/common/cmd_nvedit.c +++ b/common/cmd_nvedit.c @@ -47,7 +47,6 @@ #include #include #include -#include #include #include @@ -206,10 +205,9 @@ static int do_env_grep(cmd_tbl_t *cmdtp, int flag, int env_change_ok(const ENTRY *item, const char *newval, enum env_op op, int flag) { - int console = -1; +#ifndef CONFIG_ENV_OVERWRITE const char *name; -#if !defined(CONFIG_ENV_OVERWRITE) && defined(CONFIG_OVERWRITE_ETHADDR_ONCE) \ -&& defined(CONFIG_ETHADDR) +#if defined(CONFIG_OVERWRITE_ETHADDR_ONCE) && defined(CONFIG_ETHADDR) const char *oldval = NULL; if (op != env_op_create) @@ -217,35 +215,7 @@ int env_change_ok(const ENTRY *item, const char *newval, enum env_op op, #endif name = item->key; - - /* Default value for NULL to protect string-manipulating functions */ - newval = newval ? : ""; - - /* Check for console redirection */ - if (strcmp(name, "stdin") == 0) - console = stdin; - else if (strcmp(name, "stdout") == 0) - console = stdout; - else if (strcmp(name, "stderr") == 0) - console = stderr; - - if (console != -1 && (gd->flags & GD_FLG_DEVINIT) != 0) { - if ((newval == NULL) || (*newval == '\0')) { - /* We cannot delete stdin/stdout/stderr */ - if ((flag & H_FORCE) == 0) - printf("Can't delete \"%s\"\n", name); - return 1; - } - -#ifdef CONFIG_CONSOLE_MUX - if (iomux_doenv(console, newval)) - return 1; -#else - /* Try assigning specified device */ - if (console_assign(console, newval) < 0) - return 1; -#endif /* CONFIG_CONSOLE_MUX */ - } +#endif #ifndef CONFIG_ENV_OVERWRITE /* diff --git a/common/console.c b/common/console.c index c21934d..270170b 100644 --- a/common/console.c +++ b/common/console.c @@ -24,11 +24,55 @@ #include #include #include +#include #include #include +#include DECLARE_GLOBAL_DATA_PTR; +static int on_console(const char *name, const char *value, enum env_op op, + int flags) +{ + int console = -1; + + /* Check for console redirection */ + if (strcmp(name, "stdin") == 0) + console = stdin; + else if (strcmp(name, "stdout") == 0) + console = stdout; + else if (strcmp(name, "stderr") == 0) + console = stderr; + + /* if not actually setting a console variable, we don't care */ + if (console == -1 || (gd->flags & GD_FLG_DEVINIT) == 0) + return 0; + + switch (op) { + case env_op_create: + case env_op_overwrite: + +#ifdef CONFIG_CONSOLE_MUX + if (iomux_doenv(console, value)) + return 1; +#else + /* Try assigning specified device */ + if (console_assign(console, value) < 0) + return 1; +#endif /* CONFIG_CONSOLE_MUX */ + return 0; + + case env_op_delete: + if ((flags & H_FORCE) == 0) + printf("Can't delete \"%s\"\n", name); + return 1; + + default: + return 0; + } +} +U_BOOT_ENV_CALLBACK(console, on_console); + #ifdef CONFIG_SYS_CONSOLE_IS_IN_ENV /* * if overwrite_console returns 1, the stdin, stderr and stdout diff --git a/include/env_callback.h b/include/env_callback.h index bb398ec..9d2d2c9 100644 --- a/include/env_callback.h +++ b/include/env_callback.h @@ -42,6 +42,7 @@ "baudrate:baudrate," \ "bootfile:bootfile," \ "loadaddr:loadaddr," \ + "stdin:console,stdout:console,stderr:console," \ CONFIG_ENV_CALLBACK_LIST_STATIC struct env_clbk_tbl { -- cgit v0.10.2 From e080d545f8ffb104a13b07deddf92ecb498b3a94 Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 11 Dec 2012 22:16:30 -0600 Subject: env: Add a silent env handler The silent variable now updates the global data flag anytime it is changed as well as after the env relocation (in case its value is different from the default env in such cases as NAND env) Signed-off-by: Joe Hershberger diff --git a/common/console.c b/common/console.c index 270170b..bf73178 100644 --- a/common/console.c +++ b/common/console.c @@ -73,6 +73,29 @@ static int on_console(const char *name, const char *value, enum env_op op, } U_BOOT_ENV_CALLBACK(console, on_console); +#ifdef CONFIG_SILENT_CONSOLE +static int on_silent(const char *name, const char *value, enum env_op op, + int flags) +{ +#ifndef CONFIG_SILENT_CONSOLE_UPDATE_ON_SET + if (flags & H_INTERACTIVE) + return 0; +#endif +#ifndef CONFIG_SILENT_CONSOLE_UPDATE_ON_RELOC + if ((flags & H_INTERACTIVE) == 0) + return 0; +#endif + + if (value != NULL) + gd->flags |= GD_FLG_SILENT; + else + gd->flags &= ~GD_FLG_SILENT; + + return 0; +} +U_BOOT_ENV_CALLBACK(silent, on_silent); +#endif + #ifdef CONFIG_SYS_CONSOLE_IS_IN_ENV /* * if overwrite_console returns 1, the stdin, stderr and stdout diff --git a/doc/README.silent b/doc/README.silent index a26e3df..70202ce 100644 --- a/doc/README.silent +++ b/doc/README.silent @@ -1,9 +1,15 @@ The config option CONFIG_SILENT_CONSOLE can be used to quiet messages on the console. If the option has been enabled, the output can be -silenced by setting the environment variable "silent". The variable -is latched into the global data at an early stage in the boot process -so deleting it with "setenv" will not take effect until the system is -restarted. +silenced by setting the environment variable "silent". + +- CONFIG_SILENT_CONSOLE_UPDATE_ON_SET + When the "silent" variable is changed with env set, the change + will take effect immediately. + +- CONFIG_SILENT_CONSOLE_UPDATE_ON_RELOC + Some environments are not available until relocation (e.g. NAND) + so this will make the value in the flash env take effect at + relocation. The following actions are taken if "silent" is set at boot time: diff --git a/include/env_callback.h b/include/env_callback.h index 9d2d2c9..f52e133 100644 --- a/include/env_callback.h +++ b/include/env_callback.h @@ -34,6 +34,12 @@ #define CONFIG_ENV_CALLBACK_LIST_STATIC #endif +#ifdef CONFIG_SILENT_CONSOLE +#define SILENT_CALLBACK "silent:silent," +#else +#define SILENT_CALLBACK +#endif + /* * This list of callback bindings is static, but may be overridden by defining * a new association in the ".callbacks" environment variable. @@ -42,6 +48,7 @@ "baudrate:baudrate," \ "bootfile:bootfile," \ "loadaddr:loadaddr," \ + SILENT_CALLBACK \ "stdin:console,stdout:console,stderr:console," \ CONFIG_ENV_CALLBACK_LIST_STATIC -- cgit v0.10.2 From 2598090b7e17f8bdca95b22e7f27217054730e02 Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 11 Dec 2012 22:16:31 -0600 Subject: env: Add environment variable flags Currently just validates variable types as decimal, hexidecimal, boolean, ip address, and mac address. If the entry is not found in the env ".flags", then look in the static one. This allows the env to override the static definitions, but prevents the need to have every definition in the environment distracting you. Signed-off-by: Joe Hershberger diff --git a/README b/README index ac61a32..acb711a 100644 --- a/README +++ b/README @@ -2188,6 +2188,11 @@ CBFS (Coreboot Filesystem) support serial# is unaffected by this, i. e. it remains read-only.] + The same can be accomplished in a more flexible way + for any variable by configuring the type of access + to allow for those variables in the ".flags" variable + or define CONFIG_ENV_FLAGS_LIST_STATIC. + - Protected RAM: CONFIG_PRAM @@ -3113,6 +3118,38 @@ Configuration Settings: cases. This setting can be used to tune behaviour; see lib/hashtable.c for details. +- CONFIG_ENV_FLAGS_LIST_DEFAULT +- CONFIG_ENV_FLAGS_LIST_STATIC + Enable validation of the values given to enviroment variables when + calling env set. Variables can be restricted to only decimal, + hexadecimal, or boolean. If CONFIG_CMD_NET is also defined, + the variables can also be restricted to IP address or MAC address. + + The format of the list is: + type_attribute = [s|d|x|b|i|m] + attributes = type_attribute + entry = variable_name[:attributes] + list = entry[,list] + + The type attributes are: + s - String (default) + d - Decimal + x - Hexadecimal + b - Boolean ([1yYtT|0nNfF]) + i - IP address + m - MAC address + + - CONFIG_ENV_FLAGS_LIST_DEFAULT + Define this to a list (string) to define the ".flags" + envirnoment variable in the default or embedded environment. + + - CONFIG_ENV_FLAGS_LIST_STATIC + Define this to a list (string) to define validation that + should be done if an entry is not found in the ".flags" + environment variable. To override a setting in the static + list, simply add an entry for the same variable name to the + ".flags" variable. + The following definitions that deal with the placement and management of environment data (variable area); in general, we support the following configurations: diff --git a/common/Makefile b/common/Makefile index 04812e9..c774395 100644 --- a/common/Makefile +++ b/common/Makefile @@ -47,6 +47,7 @@ COBJS-y += cmd_version.o COBJS-y += env_attr.o COBJS-y += env_callback.o COBJS-y += env_common.o +COBJS-y += env_flags.o COBJS-$(CONFIG_ENV_IS_IN_DATAFLASH) += env_dataflash.o COBJS-$(CONFIG_ENV_IS_IN_EEPROM) += env_eeprom.o XCOBJS-$(CONFIG_ENV_IS_EMBEDDED) += env_embedded.o @@ -212,6 +213,7 @@ COBJS-$(CONFIG_SPL_NET_SUPPORT) += cmd_nvedit.o COBJS-$(CONFIG_SPL_NET_SUPPORT) += env_attr.o COBJS-$(CONFIG_SPL_NET_SUPPORT) += env_callback.o COBJS-$(CONFIG_SPL_NET_SUPPORT) += env_common.o +COBJS-$(CONFIG_SPL_NET_SUPPORT) += env_flags.o COBJS-$(CONFIG_SPL_NET_SUPPORT) += env_nowhere.o COBJS-$(CONFIG_SPL_NET_SUPPORT) += miiphyutil.o endif diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c index cb191cd..f645194 100644 --- a/common/cmd_nvedit.c +++ b/common/cmd_nvedit.c @@ -192,57 +192,9 @@ static int do_env_grep(cmd_tbl_t *cmdtp, int flag, #endif /* CONFIG_SPL_BUILD */ /* - * Perform consistency checking before setting, replacing, or deleting an - * environment variable, then (if successful) apply the changes to internals so - * to make them effective. Code for this function was taken out of - * _do_env_set(), which now calls it instead. - * Also called as a callback function by himport_r(). - * Returns 0 in case of success, 1 in case of failure. - * When (flag & H_FORCE) is set, do not print out any error message and force - * overwriting of write-once variables. - */ - -int env_change_ok(const ENTRY *item, const char *newval, enum env_op op, - int flag) -{ -#ifndef CONFIG_ENV_OVERWRITE - const char *name; -#if defined(CONFIG_OVERWRITE_ETHADDR_ONCE) && defined(CONFIG_ETHADDR) - const char *oldval = NULL; - - if (op != env_op_create) - oldval = item->data; -#endif - - name = item->key; -#endif - -#ifndef CONFIG_ENV_OVERWRITE - /* - * Some variables like "ethaddr" and "serial#" can be set only once and - * cannot be deleted, unless CONFIG_ENV_OVERWRITE is defined. - */ - if (op != env_op_create && /* variable exists */ - (flag & H_FORCE) == 0) { /* and we are not forced */ - if (strcmp(name, "serial#") == 0 || - (strcmp(name, "ethaddr") == 0 -#if defined(CONFIG_OVERWRITE_ETHADDR_ONCE) && defined(CONFIG_ETHADDR) - && strcmp(oldval, __stringify(CONFIG_ETHADDR)) != 0 -#endif /* CONFIG_OVERWRITE_ETHADDR_ONCE && CONFIG_ETHADDR */ - )) { - printf("Can't overwrite \"%s\"\n", name); - return 1; - } - } -#endif - - return 0; -} - -/* * Set a new environment variable, * or replace or delete an existing one. -*/ + */ static int _do_env_set(int flag, int argc, char * const argv[]) { int i, len; diff --git a/common/env_common.c b/common/env_common.c index 067fe3f..bb18070 100644 --- a/common/env_common.c +++ b/common/env_common.c @@ -40,7 +40,7 @@ DECLARE_GLOBAL_DATA_PTR; #include struct hsearch_data env_htab = { - .change_ok = env_change_ok, + .change_ok = env_flags_validate, }; static uchar __env_get_char_spec(int index) diff --git a/common/env_flags.c b/common/env_flags.c new file mode 100644 index 0000000..a58d614 --- /dev/null +++ b/common/env_flags.c @@ -0,0 +1,319 @@ +/* + * (C) Copyright 2012 + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + * + * 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 +#include + +#include +#include + +#ifdef CONFIG_CMD_NET +#define ENV_FLAGS_NET_VARTYPE_REPS "im" +#else +#define ENV_FLAGS_NET_VARTYPE_REPS "" +#endif + +static const char env_flags_vartype_rep[] = "sdxb" ENV_FLAGS_NET_VARTYPE_REPS; + +/* + * Parse the flags string from a .flags attribute list into the vartype enum. + */ +enum env_flags_vartype env_flags_parse_vartype(const char *flags) +{ + char *type; + + if (strlen(flags) <= ENV_FLAGS_VARTYPE_LOC) + return env_flags_vartype_string; + + type = strchr(env_flags_vartype_rep, + flags[ENV_FLAGS_VARTYPE_LOC]); + + if (type != NULL) + return (enum env_flags_vartype) + (type - &env_flags_vartype_rep[0]); + + printf("## Warning: Unknown environment variable type '%c'\n", + flags[ENV_FLAGS_VARTYPE_LOC]); + return env_flags_vartype_string; +} + +static inline int is_hex_prefix(const char *value) +{ + return value[0] == '0' && (value[1] == 'x' || value[1] == 'X'); +} + +static void skip_num(int hex, const char *value, const char **end, + int max_digits) +{ + int i; + + if (hex && is_hex_prefix(value)) + value += 2; + + for (i = max_digits; i != 0; i--) { + if (hex && !isxdigit(*value)) + break; + if (!hex && !isdigit(*value)) + break; + value++; + } + if (end != NULL) + *end = value; +} + +/* + * Based on the declared type enum, validate that the value string complies + * with that format + */ +static int _env_flags_validate_type(const char *value, + enum env_flags_vartype type) +{ + const char *end; +#ifdef CONFIG_CMD_NET + const char *cur; + int i; +#endif + + switch (type) { + case env_flags_vartype_string: + break; + case env_flags_vartype_decimal: + skip_num(0, value, &end, -1); + if (*end != '\0') + return -1; + break; + case env_flags_vartype_hex: + skip_num(1, value, &end, -1); + if (*end != '\0') + return -1; + if (value + 2 == end && is_hex_prefix(value)) + return -1; + break; + case env_flags_vartype_bool: + if (value[0] != '1' && value[0] != 'y' && value[0] != 't' && + value[0] != 'Y' && value[0] != 'T' && + value[0] != '0' && value[0] != 'n' && value[0] != 'f' && + value[0] != 'N' && value[0] != 'F') + return -1; + if (value[1] != '\0') + return -1; + break; +#ifdef CONFIG_CMD_NET + case env_flags_vartype_ipaddr: + cur = value; + for (i = 0; i < 4; i++) { + skip_num(0, cur, &end, 3); + if (cur == end) + return -1; + if (i != 3 && *end != '.') + return -1; + if (i == 3 && *end != '\0') + return -1; + cur = end + 1; + } + break; + case env_flags_vartype_macaddr: + cur = value; + for (i = 0; i < 6; i++) { + skip_num(1, cur, &end, 2); + if (cur == end) + return -1; + if (cur + 2 == end && is_hex_prefix(cur)) + return -1; + if (i != 5 && *end != ':') + return -1; + if (i == 5 && *end != '\0') + return -1; + cur = end + 1; + } + break; +#endif + case env_flags_vartype_end: + return -1; + } + + /* OK */ + return 0; +} + +/* + * Look for flags in a provided list and failing that the static list + */ +static inline int env_flags_lookup(const char *flags_list, const char *name, + char *flags) +{ + int ret = 1; + + if (!flags) + /* bad parameter */ + return -1; + + /* try the env first */ + if (flags_list) + ret = env_attr_lookup(flags_list, name, flags); + + if (ret != 0) + /* if not found in the env, look in the static list */ + ret = env_attr_lookup(ENV_FLAGS_LIST_STATIC, name, flags); + + return ret; +} + +/* + * Parse the flag charachters from the .flags attribute list into the binary + * form to be stored in the environment entry->flags field. + */ +static int env_parse_flags_to_bin(const char *flags) +{ + return env_flags_parse_vartype(flags) & ENV_FLAGS_VARTYPE_BIN_MASK; +} + +/* + * Look for possible flags for a newly added variable + * This is called specifically when the variable did not exist in the hash + * previously, so the blanket update did not find this variable. + */ +void env_flags_init(ENTRY *var_entry) +{ + const char *var_name = var_entry->key; + const char *flags_list = getenv(ENV_FLAGS_VAR); + char flags[ENV_FLAGS_ATTR_MAX_LEN + 1] = ""; + int ret = 1; + + /* look in the ".flags" and static for a reference to this variable */ + ret = env_flags_lookup(flags_list, var_name, flags); + + /* if any flags were found, set the binary form to the entry */ + if (!ret && strlen(flags)) + var_entry->flags = env_parse_flags_to_bin(flags); +} + +/* + * Called on each existing env var prior to the blanket update since removing + * a flag in the flag list should remove its flags. + */ +static int clear_flags(ENTRY *entry) +{ + entry->flags = 0; + + return 0; +} + +/* + * Call for each element in the list that defines flags for a variable + */ +static int set_flags(const char *name, const char *value) +{ + ENTRY e, *ep; + + e.key = name; + e.data = NULL; + hsearch_r(e, FIND, &ep, &env_htab, 0); + + /* does the env variable actually exist? */ + if (ep != NULL) { + /* the flag list is empty, so clear the flags */ + if (value == NULL || strlen(value) == 0) + ep->flags = 0; + else + /* assign the requested flags */ + ep->flags = env_parse_flags_to_bin(value); + } + + return 0; +} + +static int on_flags(const char *name, const char *value, enum env_op op, + int flags) +{ + /* remove all flags */ + hwalk_r(&env_htab, clear_flags); + + /* configure any static flags */ + env_attr_walk(ENV_FLAGS_LIST_STATIC, set_flags); + /* configure any dynamic flags */ + env_attr_walk(value, set_flags); + + return 0; +} +U_BOOT_ENV_CALLBACK(flags, on_flags); + +/* + * Perform consistency checking before creating, overwriting, or deleting an + * environment variable. Called as a callback function by hsearch_r() and + * hdelete_r(). Returns 0 in case of success, 1 in case of failure. + * When (flag & H_FORCE) is set, do not print out any error message and force + * overwriting of write-once variables. + */ + +int env_flags_validate(const ENTRY *item, const char *newval, enum env_op op, + int flag) +{ + const char *name; +#if !defined(CONFIG_ENV_OVERWRITE) && defined(CONFIG_OVERWRITE_ETHADDR_ONCE) \ +&& defined(CONFIG_ETHADDR) + const char *oldval = NULL; + + if (op != env_op_create) + oldval = item->data; +#endif + + name = item->key; + + /* Default value for NULL to protect string-manipulating functions */ + newval = newval ? : ""; + +#ifndef CONFIG_ENV_OVERWRITE + /* + * Some variables like "ethaddr" and "serial#" can be set only once and + * cannot be deleted, unless CONFIG_ENV_OVERWRITE is defined. + */ + if (op != env_op_create && /* variable exists */ + (flag & H_FORCE) == 0) { /* and we are not forced */ + if (strcmp(name, "serial#") == 0 || + (strcmp(name, "ethaddr") == 0 +#if defined(CONFIG_OVERWRITE_ETHADDR_ONCE) && defined(CONFIG_ETHADDR) + && strcmp(oldval, __stringify(CONFIG_ETHADDR)) != 0 +#endif /* CONFIG_OVERWRITE_ETHADDR_ONCE && CONFIG_ETHADDR */ + )) { + printf("Can't overwrite \"%s\"\n", name); + return 1; + } + } +#endif + + /* validate the value to match the variable type */ + if (op != env_op_delete) { + enum env_flags_vartype type = (enum env_flags_vartype) + (ENV_FLAGS_VARTYPE_BIN_MASK & item->flags); + + if (_env_flags_validate_type(newval, type) < 0) { + printf("## Error: flags type check failure for " + "\"%s\" <= \"%s\" (type: %c)\n", + name, newval, env_flags_vartype_rep[type]); + return -1; + } + } + + return 0; +} diff --git a/include/env_callback.h b/include/env_callback.h index f52e133..47fdc6f 100644 --- a/include/env_callback.h +++ b/include/env_callback.h @@ -24,6 +24,7 @@ #ifndef __ENV_CALLBACK_H__ #define __ENV_CALLBACK_H__ +#include #include #include @@ -45,6 +46,7 @@ * a new association in the ".callbacks" environment variable. */ #define ENV_CALLBACK_LIST_STATIC ENV_CALLBACK_VAR ":callbacks," \ + ENV_FLAGS_VAR ":flags," \ "baudrate:baudrate," \ "bootfile:bootfile," \ "loadaddr:loadaddr," \ diff --git a/include/env_default.h b/include/env_default.h index d05eba1..39c5b7c 100644 --- a/include/env_default.h +++ b/include/env_default.h @@ -41,6 +41,9 @@ const uchar default_environment[] = { #ifdef CONFIG_ENV_CALLBACK_LIST_DEFAULT ENV_CALLBACK_VAR "=" CONFIG_ENV_CALLBACK_LIST_DEFAULT "\0" #endif +#ifdef CONFIG_ENV_FLAGS_LIST_DEFAULT + ENV_FLAGS_VAR "=" CONFIG_ENV_FLAGS_LIST_DEFAULT "\0" +#endif #ifdef CONFIG_BOOTARGS "bootargs=" CONFIG_BOOTARGS "\0" #endif diff --git a/include/env_flags.h b/include/env_flags.h new file mode 100644 index 0000000..bf25f27 --- /dev/null +++ b/include/env_flags.h @@ -0,0 +1,76 @@ +/* + * (C) Copyright 2012 + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + * + * 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 __ENV_FLAGS_H__ +#define __ENV_FLAGS_H__ + +enum env_flags_vartype { + env_flags_vartype_string, + env_flags_vartype_decimal, + env_flags_vartype_hex, + env_flags_vartype_bool, +#ifdef CONFIG_CMD_NET + env_flags_vartype_ipaddr, + env_flags_vartype_macaddr, +#endif + env_flags_vartype_end +}; + +#define ENV_FLAGS_VAR ".flags" +#define ENV_FLAGS_ATTR_MAX_LEN 2 +#define ENV_FLAGS_VARTYPE_LOC 0 + +#ifndef CONFIG_ENV_FLAGS_LIST_STATIC +#define CONFIG_ENV_FLAGS_LIST_STATIC "" +#endif + +#define ENV_FLAGS_LIST_STATIC \ + CONFIG_ENV_FLAGS_LIST_STATIC + +/* + * Parse the flags string from a .flags attribute list into the vartype enum. + */ +enum env_flags_vartype env_flags_parse_vartype(const char *flags); + +#include + +/* + * When adding a variable to the environment, initialize the flags for that + * variable. + */ +void env_flags_init(ENTRY *var_entry); + +/* + * Validate the newval for to conform with the requirements defined by its flags + */ +int env_flags_validate(const ENTRY *item, const char *newval, enum env_op op, + int flag); + +/* + * These are the binary flags used in the environment entry->flags variable to + * decribe properties of veriables in the table + */ +#define ENV_FLAGS_VARTYPE_BIN_MASK 0x00000007 +/* The actual variable type values use the enum value (within the mask) */ + +#endif /* __ENV_FLAGS_H__ */ diff --git a/include/environment.h b/include/environment.h index 6c30215..00e59ba 100644 --- a/include/environment.h +++ b/include/environment.h @@ -166,6 +166,7 @@ extern void env_reloc(void); #include #include +#include #include extern struct hsearch_data env_htab; @@ -189,14 +190,6 @@ int set_default_vars(int nvars, char * const vars[]); /* Import from binary representation into hash table */ int env_import(const char *buf, int check); -/* - * Check if variable "item" can be changed to newval - * When (flag & H_FORCE) is set, it does not print out any error - * message and forces overwriting of write-once variables. - */ -int env_change_ok(const ENTRY *item, const char *newval, enum env_op op, - int flag); - #endif /* DO_DEPS_ONLY */ #endif /* _ENVIRONMENT_H_ */ diff --git a/include/search.h b/include/search.h index d68e24a..13d3be6 100644 --- a/include/search.h +++ b/include/search.h @@ -49,6 +49,7 @@ typedef struct entry { char *data; int (*callback)(const char *name, const char *value, enum env_op op, int flags); + int flags; } ENTRY; /* Opaque type for internal use. */ diff --git a/lib/hashtable.c b/lib/hashtable.c index e922666..07ebfb2 100644 --- a/lib/hashtable.c +++ b/lib/hashtable.c @@ -55,6 +55,7 @@ #endif #include +#include #include /* @@ -412,6 +413,8 @@ int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval, /* This is a new entry, so look up a possible callback */ env_callback_init(&htab->table[idx].entry); + /* Also look for flags */ + env_flags_init(&htab->table[idx].entry); /* check for permission */ if (htab->change_ok != NULL && htab->change_ok( @@ -465,6 +468,7 @@ static void _hdelete(const char *key, struct hsearch_data *htab, ENTRY *ep, free((void *)ep->key); free(ep->data); ep->callback = NULL; + ep->flags = 0; htab->table[idx].used = -1; --htab->filled; -- cgit v0.10.2 From 30fd4fadb319d7c6d43d949e2d30ffaea46a60cf Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 11 Dec 2012 22:16:32 -0600 Subject: tools/env: Add environment variable flags support Currently just validates variable types as decimal, hexidecimal, boolean, ip address, and mac address. Call env_acl_validate_setenv_params() from setenv() in fw_env.c. If the entry is not found in the env .flags, then look in the static one. This allows the env to override the static definitions, but prevents the need to have every definition in the environment distracting you. Need to build in _ctype for isdigit for Linux. Signed-off-by: Joe Hershberger diff --git a/common/env_attr.c b/common/env_attr.c index 7d330a5..210c98d 100644 --- a/common/env_attr.c +++ b/common/env_attr.c @@ -21,7 +21,14 @@ * MA 02111-1307 USA */ +#ifdef USE_HOSTCC /* Eliminate "ANSI does not permit..." warnings */ +#include +#include +#include +#else #include +#endif + #include #include #include diff --git a/common/env_flags.c b/common/env_flags.c index a58d614..ed0857c 100644 --- a/common/env_flags.c +++ b/common/env_flags.c @@ -24,8 +24,17 @@ #include #include +#ifdef USE_HOSTCC /* Eliminate "ANSI does not permit..." warnings */ +#include +#include +#include "fw_env.h" +#include +#include +#define getenv fw_getenv +#else #include #include +#endif #ifdef CONFIG_CMD_NET #define ENV_FLAGS_NET_VARTYPE_REPS "im" @@ -179,6 +188,70 @@ static inline int env_flags_lookup(const char *flags_list, const char *name, return ret; } +#ifdef USE_HOSTCC /* Functions only used from tools/env */ +/* + * Look up any flags directly from the .flags variable and the static list + * and convert them to the vartype enum. + */ +enum env_flags_vartype env_flags_get_type(const char *name) +{ + const char *flags_list = getenv(ENV_FLAGS_VAR); + char flags[ENV_FLAGS_ATTR_MAX_LEN + 1]; + + if (env_flags_lookup(flags_list, name, flags)) + return env_flags_vartype_string; + + if (strlen(flags) <= ENV_FLAGS_VARTYPE_LOC) + return env_flags_vartype_string; + + return env_flags_parse_vartype(flags); +} + +/* + * Validate that the proposed new value for "name" is valid according to the + * defined flags for that variable, if any. + */ +int env_flags_validate_type(const char *name, const char *value) +{ + enum env_flags_vartype type; + + if (value == NULL) + return 0; + type = env_flags_get_type(name); + if (_env_flags_validate_type(value, type) < 0) { + printf("## Error: flags type check failure for " + "\"%s\" <= \"%s\" (type: %c)\n", + name, value, env_flags_vartype_rep[type]); + return -1; + } + return 0; +} + +/* + * Validate the parameters to "env set" directly + */ +int env_flags_validate_env_set_params(int argc, char * const argv[]) +{ + if ((argc >= 3) && argv[2] != NULL) { + enum env_flags_vartype type = env_flags_get_type(argv[1]); + + /* + * we don't currently check types that need more than + * one argument + */ + if (type != env_flags_vartype_string && argc > 3) { + printf("## Error: too many parameters for setting " + "\"%s\"\n", argv[1]); + return -1; + } + return env_flags_validate_type(argv[1], argv[2]); + } + /* ok */ + return 0; +} + +#else /* !USE_HOSTCC - Functions only used from lib/hashtable.c */ + /* * Parse the flag charachters from the .flags attribute list into the binary * form to be stored in the environment entry->flags field. @@ -317,3 +390,5 @@ int env_flags_validate(const ENTRY *item, const char *newval, enum env_op op, return 0; } + +#endif diff --git a/include/env_flags.h b/include/env_flags.h index bf25f27..3333446 100644 --- a/include/env_flags.h +++ b/include/env_flags.h @@ -52,6 +52,23 @@ enum env_flags_vartype { */ enum env_flags_vartype env_flags_parse_vartype(const char *flags); +#ifdef USE_HOSTCC +/* + * Look up the type of a variable directly from the .flags var. + */ +enum env_flags_vartype env_flags_get_type(const char *name); +/* + * Validate the newval for its type to conform with the requirements defined by + * its flags (directly looked at the .flags var). + */ +int env_flags_validate_type(const char *name, const char *newval); +/* + * Validate the parameters passed to "env set" for type compliance + */ +int env_flags_validate_env_set_params(int argc, char * const argv[]); + +#else /* !USE_HOSTCC */ + #include /* @@ -73,4 +90,6 @@ int env_flags_validate(const ENTRY *item, const char *newval, enum env_op op, #define ENV_FLAGS_VARTYPE_BIN_MASK 0x00000007 /* The actual variable type values use the enum value (within the mask) */ +#endif /* USE_HOSTCC */ + #endif /* __ENV_FLAGS_H__ */ diff --git a/tools/env/Makefile b/tools/env/Makefile index ab73c8c..0e798e0 100644 --- a/tools/env/Makefile +++ b/tools/env/Makefile @@ -24,12 +24,15 @@ include $(TOPDIR)/config.mk HOSTSRCS := $(SRCTREE)/lib/crc32.c fw_env.c fw_env_main.c +HOSTSRCS += $(SRCTREE)/lib/ctype.c $(SRCTREE)/lib/linux_string.c +HOSTSRCS += $(SRCTREE)/common/env_attr.c $(SRCTREE)/common/env_flags.c HEADERS := fw_env.h $(OBJTREE)/include/config.h # Compile for a hosted environment on the target HOSTCPPFLAGS = -idirafter $(SRCTREE)/include \ -idirafter $(OBJTREE)/include2 \ -idirafter $(OBJTREE)/include \ + -idirafter $(SRCTREE)/tools/env \ -DUSE_HOSTCC \ -DTEXT_BASE=$(TEXT_BASE) diff --git a/tools/env/fw_env.c b/tools/env/fw_env.c index 9b023e8..5be36fc 100644 --- a/tools/env/fw_env.c +++ b/tools/env/fw_env.c @@ -25,6 +25,7 @@ */ #include +#include #include #include #include @@ -395,6 +396,9 @@ int fw_setenv(int argc, char *argv[]) name = argv[1]; + if (env_flags_validate_env_set_params(argc, argv) < 0) + return 1; + len = 0; for (i = 2; i < argc; ++i) { char *val = argv[i]; @@ -516,6 +520,11 @@ int fw_parse_script(char *fname) name, val ? val : " removed"); #endif + if (env_flags_validate_type(name, val) < 0) { + ret = -1; + break; + } + /* * If there is an error setting a variable, * try to save the environment and returns an error -- cgit v0.10.2 From fffad71bc489cf224eda6d826a1645423852ee45 Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 11 Dec 2012 22:16:33 -0600 Subject: env: Add a command to display details about env flags Similar to the env callback command, this will show details about the options available, the static list, and the currently active variables. Signed-off-by: Joe Hershberger diff --git a/README b/README index acb711a..08c0fcc 100644 --- a/README +++ b/README @@ -816,6 +816,7 @@ The following options need to be configured: CONFIG_CMD_EEPROM * EEPROM read/write support CONFIG_CMD_ELF * bootelf, bootvx CONFIG_CMD_ENV_CALLBACK * display details about env callbacks + CONFIG_CMD_ENV_FLAGS * display details about env flags CONFIG_CMD_EXPORTENV * export the environment CONFIG_CMD_EXT2 * ext2 command support CONFIG_CMD_EXT4 * ext4 command support diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c index f645194..468b89c 100644 --- a/common/cmd_nvedit.c +++ b/common/cmd_nvedit.c @@ -443,6 +443,59 @@ int do_env_callback(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) } #endif +#if defined(CONFIG_CMD_ENV_FLAGS) +static int print_static_flags(const char *var_name, const char *flags) +{ + enum env_flags_vartype type = env_flags_parse_vartype(flags); + + printf("\t%-20s %-20s\n", var_name, env_flags_get_vartype_name(type)); + + return 0; +} + +static int print_active_flags(ENTRY *entry) +{ + enum env_flags_vartype type; + + if (entry->flags == 0) + return 0; + + type = (enum env_flags_vartype) + (entry->flags & ENV_FLAGS_VARTYPE_BIN_MASK); + printf("\t%-20s %-20s\n", entry->key, env_flags_get_vartype_name(type)); + + return 0; +} + +/* + * Print the flags available and what variables have flags + */ +int do_env_flags(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + /* Print the available variable types */ + printf("Available variable type flags (position %d):\n", + ENV_FLAGS_VARTYPE_LOC); + puts("\tFlag\tVariable Type Name\n"); + puts("\t----\t------------------\n"); + env_flags_print_vartypes(); + puts("\n"); + + /* Print the static flags that may exist */ + puts("Static flags:\n"); + printf("\t%-20s %-20s\n", "Variable Name", "Variable Type"); + printf("\t%-20s %-20s\n", "-------------", "-------------"); + env_attr_walk(ENV_FLAGS_LIST_STATIC, print_static_flags); + puts("\n"); + + /* walk through each variable and print the flags if non-default */ + puts("Active flags:\n"); + printf("\t%-20s %-20s\n", "Variable Name", "Variable Type"); + printf("\t%-20s %-20s\n", "-------------", "-------------"); + hwalk_r(&env_htab, print_active_flags); + return 0; +} +#endif + /* * Interactively edit an environment variable */ @@ -913,6 +966,9 @@ static cmd_tbl_t cmd_env_sub[] = { #if defined(CONFIG_CMD_ENV_CALLBACK) U_BOOT_CMD_MKENT(callbacks, 1, 0, do_env_callback, "", ""), #endif +#if defined(CONFIG_CMD_ENV_FLAGS) + U_BOOT_CMD_MKENT(flags, 1, 0, do_env_flags, "", ""), +#endif #if defined(CONFIG_CMD_EXPORTENV) U_BOOT_CMD_MKENT(export, 4, 0, do_env_export, "", ""), #endif @@ -974,6 +1030,9 @@ static char env_help_text[] = #if defined(CONFIG_CMD_EXPORTENV) "env export [-t | -b | -c] [-s size] addr [var ...] - export environment\n" #endif +#if defined(CONFIG_CMD_ENV_FLAGS) + "env flags - print variables that have non-default flags\n" +#endif #if defined(CONFIG_CMD_GREPENV) "env grep string [...] - search environment\n" #endif diff --git a/common/env_flags.c b/common/env_flags.c index ed0857c..09f93d5 100644 --- a/common/env_flags.c +++ b/common/env_flags.c @@ -43,6 +43,40 @@ #endif static const char env_flags_vartype_rep[] = "sdxb" ENV_FLAGS_NET_VARTYPE_REPS; +#ifdef CONFIG_CMD_ENV_FLAGS +static const char * const env_flags_vartype_names[] = { + "string", + "decimal", + "hexadecimal", + "boolean", +#ifdef CONFIG_CMD_NET + "IP address", + "MAC address", +#endif +}; + +/* + * Print the whole list of available type flags. + */ +void env_flags_print_vartypes(void) +{ + enum env_flags_vartype curtype = (enum env_flags_vartype)0; + + while (curtype != env_flags_vartype_end) { + printf("\t%c -\t%s\n", env_flags_vartype_rep[curtype], + env_flags_vartype_names[curtype]); + curtype++; + } +} + +/* + * Return the name of the type. + */ +const char *env_flags_get_vartype_name(enum env_flags_vartype type) +{ + return env_flags_vartype_names[type]; +} +#endif /* CONFIG_CMD_ENV_FLAGS */ /* * Parse the flags string from a .flags attribute list into the vartype enum. diff --git a/include/env_flags.h b/include/env_flags.h index 3333446..7e72523 100644 --- a/include/env_flags.h +++ b/include/env_flags.h @@ -47,6 +47,17 @@ enum env_flags_vartype { #define ENV_FLAGS_LIST_STATIC \ CONFIG_ENV_FLAGS_LIST_STATIC +#ifdef CONFIG_CMD_ENV_FLAGS +/* + * Print the whole list of available type flags. + */ +void env_flags_print_vartypes(void); +/* + * Return the name of the type. + */ +const char *env_flags_get_vartype_name(enum env_flags_vartype type); +#endif + /* * Parse the flags string from a .flags attribute list into the vartype enum. */ -- cgit v0.10.2 From 267541f776f1e2bec21681c6e39a4c93af9621cf Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 11 Dec 2012 22:16:34 -0600 Subject: env: Add support for access control to .flags Add support for read-only, write-once, and change-default. Signed-off-by: Joe Hershberger diff --git a/README b/README index 08c0fcc..b5c1c03 100644 --- a/README +++ b/README @@ -3128,7 +3128,8 @@ Configuration Settings: The format of the list is: type_attribute = [s|d|x|b|i|m] - attributes = type_attribute + access_atribute = [a|r|o|c] + attributes = type_attribute[access_atribute] entry = variable_name[:attributes] list = entry[,list] @@ -3140,6 +3141,12 @@ Configuration Settings: i - IP address m - MAC address + The access attributes are: + a - Any (default) + r - Read-only + o - Write-once + c - Change-default + - CONFIG_ENV_FLAGS_LIST_DEFAULT Define this to a list (string) to define the ".flags" envirnoment variable in the default or embedded environment. @@ -3151,6 +3158,10 @@ Configuration Settings: list, simply add an entry for the same variable name to the ".flags" variable. +- CONFIG_ENV_ACCESS_IGNORE_FORCE + If defined, don't allow the -f switch to env set override variable + access flags. + The following definitions that deal with the placement and management of environment data (variable area); in general, we support the following configurations: diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c index 468b89c..e8dfbf5 100644 --- a/common/cmd_nvedit.c +++ b/common/cmd_nvedit.c @@ -447,8 +447,11 @@ int do_env_callback(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) static int print_static_flags(const char *var_name, const char *flags) { enum env_flags_vartype type = env_flags_parse_vartype(flags); + enum env_flags_varaccess access = env_flags_parse_varaccess(flags); - printf("\t%-20s %-20s\n", var_name, env_flags_get_vartype_name(type)); + printf("\t%-20s %-20s %-20s\n", var_name, + env_flags_get_vartype_name(type), + env_flags_get_varaccess_name(access)); return 0; } @@ -456,13 +459,17 @@ static int print_static_flags(const char *var_name, const char *flags) static int print_active_flags(ENTRY *entry) { enum env_flags_vartype type; + enum env_flags_varaccess access; if (entry->flags == 0) return 0; type = (enum env_flags_vartype) (entry->flags & ENV_FLAGS_VARTYPE_BIN_MASK); - printf("\t%-20s %-20s\n", entry->key, env_flags_get_vartype_name(type)); + access = env_flags_parse_varaccess_from_binflags(entry->flags); + printf("\t%-20s %-20s %-20s\n", entry->key, + env_flags_get_vartype_name(type), + env_flags_get_varaccess_name(access)); return 0; } @@ -480,17 +487,29 @@ int do_env_flags(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) env_flags_print_vartypes(); puts("\n"); + /* Print the available variable access types */ + printf("Available variable access flags (position %d):\n", + ENV_FLAGS_VARACCESS_LOC); + puts("\tFlag\tVariable Access Name\n"); + puts("\t----\t--------------------\n"); + env_flags_print_varaccess(); + puts("\n"); + /* Print the static flags that may exist */ puts("Static flags:\n"); - printf("\t%-20s %-20s\n", "Variable Name", "Variable Type"); - printf("\t%-20s %-20s\n", "-------------", "-------------"); + printf("\t%-20s %-20s %-20s\n", "Variable Name", "Variable Type", + "Variable Access"); + printf("\t%-20s %-20s %-20s\n", "-------------", "-------------", + "---------------"); env_attr_walk(ENV_FLAGS_LIST_STATIC, print_static_flags); puts("\n"); /* walk through each variable and print the flags if non-default */ puts("Active flags:\n"); - printf("\t%-20s %-20s\n", "Variable Name", "Variable Type"); - printf("\t%-20s %-20s\n", "-------------", "-------------"); + printf("\t%-20s %-20s %-20s\n", "Variable Name", "Variable Type", + "Variable Access"); + printf("\t%-20s %-20s %-20s\n", "-------------", "-------------", + "---------------"); hwalk_r(&env_htab, print_active_flags); return 0; } diff --git a/common/env_common.c b/common/env_common.c index bb18070..906b41f 100644 --- a/common/env_common.c +++ b/common/env_common.c @@ -95,6 +95,24 @@ int getenv_yesno(const char *var) 1 : 0; } +/* + * Look up the variable from the default environment + */ +char *getenv_default(const char *name) +{ + char *ret_val; + unsigned long really_valid = gd->env_valid; + unsigned long real_gd_flags = gd->flags; + + /* Pretend that the image is bad. */ + gd->flags &= ~GD_FLG_ENV_READY; + gd->env_valid = 0; + ret_val = getenv(name); + gd->env_valid = really_valid; + gd->flags = real_gd_flags; + return ret_val; +} + void set_default_env(const char *s) { int flags = 0; diff --git a/common/env_flags.c b/common/env_flags.c index 09f93d5..4caf12e 100644 --- a/common/env_flags.c +++ b/common/env_flags.c @@ -43,6 +43,17 @@ #endif static const char env_flags_vartype_rep[] = "sdxb" ENV_FLAGS_NET_VARTYPE_REPS; +static const char env_flags_varaccess_rep[] = "aroc"; +static const int env_flags_varaccess_mask[] = { + 0, + ENV_FLAGS_VARACCESS_PREVENT_DELETE | + ENV_FLAGS_VARACCESS_PREVENT_CREATE | + ENV_FLAGS_VARACCESS_PREVENT_OVERWR, + ENV_FLAGS_VARACCESS_PREVENT_DELETE | + ENV_FLAGS_VARACCESS_PREVENT_OVERWR, + ENV_FLAGS_VARACCESS_PREVENT_DELETE | + ENV_FLAGS_VARACCESS_PREVENT_NONDEF_OVERWR}; + #ifdef CONFIG_CMD_ENV_FLAGS static const char * const env_flags_vartype_names[] = { "string", @@ -54,6 +65,12 @@ static const char * const env_flags_vartype_names[] = { "MAC address", #endif }; +static const char * const env_flags_varaccess_names[] = { + "any", + "read-only", + "write-once", + "change-default", +}; /* * Print the whole list of available type flags. @@ -70,12 +87,34 @@ void env_flags_print_vartypes(void) } /* + * Print the whole list of available access flags. + */ +void env_flags_print_varaccess(void) +{ + enum env_flags_varaccess curaccess = (enum env_flags_varaccess)0; + + while (curaccess != env_flags_varaccess_end) { + printf("\t%c -\t%s\n", env_flags_varaccess_rep[curaccess], + env_flags_varaccess_names[curaccess]); + curaccess++; + } +} + +/* * Return the name of the type. */ const char *env_flags_get_vartype_name(enum env_flags_vartype type) { return env_flags_vartype_names[type]; } + +/* + * Return the name of the access. + */ +const char *env_flags_get_varaccess_name(enum env_flags_varaccess access) +{ + return env_flags_varaccess_names[access]; +} #endif /* CONFIG_CMD_ENV_FLAGS */ /* @@ -100,6 +139,46 @@ enum env_flags_vartype env_flags_parse_vartype(const char *flags) return env_flags_vartype_string; } +/* + * Parse the flags string from a .flags attribute list into the varaccess enum. + */ +enum env_flags_varaccess env_flags_parse_varaccess(const char *flags) +{ + char *access; + + if (strlen(flags) <= ENV_FLAGS_VARACCESS_LOC) + return env_flags_varaccess_any; + + access = strchr(env_flags_varaccess_rep, + flags[ENV_FLAGS_VARACCESS_LOC]); + + if (access != NULL) + return (enum env_flags_varaccess) + (access - &env_flags_varaccess_rep[0]); + + printf("## Warning: Unknown environment variable access method '%c'\n", + flags[ENV_FLAGS_VARACCESS_LOC]); + return env_flags_varaccess_any; +} + +/* + * Parse the binary flags from a hash table entry into the varaccess enum. + */ +enum env_flags_varaccess env_flags_parse_varaccess_from_binflags(int binflags) +{ + int i; + + for (i = 0; i < sizeof(env_flags_varaccess_mask); i++) + if (env_flags_varaccess_mask[i] == + (binflags & ENV_FLAGS_VARACCESS_BIN_MASK)) + return (enum env_flags_varaccess)i; + + printf("Warning: Non-standard access flags. (0x%x)\n", + binflags & ENV_FLAGS_VARACCESS_BIN_MASK); + + return env_flags_varaccess_any; +} + static inline int is_hex_prefix(const char *value) { return value[0] == '0' && (value[1] == 'x' || value[1] == 'X'); @@ -242,6 +321,23 @@ enum env_flags_vartype env_flags_get_type(const char *name) } /* + * Look up the access of a variable directly from the .flags var. + */ +enum env_flags_varaccess env_flags_get_varaccess(const char *name) +{ + const char *flags_list = getenv(ENV_FLAGS_VAR); + char flags[ENV_FLAGS_ATTR_MAX_LEN + 1]; + + if (env_flags_lookup(flags_list, name, flags)) + return env_flags_varaccess_any; + + if (strlen(flags) <= ENV_FLAGS_VARACCESS_LOC) + return env_flags_varaccess_any; + + return env_flags_parse_varaccess(flags); +} + +/* * Validate that the proposed new value for "name" is valid according to the * defined flags for that variable, if any. */ @@ -262,6 +358,21 @@ int env_flags_validate_type(const char *name, const char *value) } /* + * Validate that the proposed access to variable "name" is valid according to + * the defined flags for that variable, if any. + */ +int env_flags_validate_varaccess(const char *name, int check_mask) +{ + enum env_flags_varaccess access; + int access_mask; + + access = env_flags_get_varaccess(name); + access_mask = env_flags_varaccess_mask[access]; + + return (check_mask & access_mask) != 0; +} + +/* * Validate the parameters to "env set" directly */ int env_flags_validate_env_set_params(int argc, char * const argv[]) @@ -292,7 +403,12 @@ int env_flags_validate_env_set_params(int argc, char * const argv[]) */ static int env_parse_flags_to_bin(const char *flags) { - return env_flags_parse_vartype(flags) & ENV_FLAGS_VARTYPE_BIN_MASK; + int binflags; + + binflags = env_flags_parse_vartype(flags) & ENV_FLAGS_VARTYPE_BIN_MASK; + binflags |= env_flags_varaccess_mask[env_flags_parse_varaccess(flags)]; + + return binflags; } /* @@ -377,13 +493,10 @@ int env_flags_validate(const ENTRY *item, const char *newval, enum env_op op, int flag) { const char *name; -#if !defined(CONFIG_ENV_OVERWRITE) && defined(CONFIG_OVERWRITE_ETHADDR_ONCE) \ -&& defined(CONFIG_ETHADDR) const char *oldval = NULL; if (op != env_op_create) oldval = item->data; -#endif name = item->key; @@ -422,6 +535,44 @@ int env_flags_validate(const ENTRY *item, const char *newval, enum env_op op, } } + /* check for access permission */ +#ifndef CONFIG_ENV_ACCESS_IGNORE_FORCE + if (flag & H_FORCE) + return 0; +#endif + switch (op) { + case env_op_delete: + if (item->flags & ENV_FLAGS_VARACCESS_PREVENT_DELETE) { + printf("## Error: Can't delete \"%s\"\n", name); + return 1; + } + break; + case env_op_overwrite: + if (item->flags & ENV_FLAGS_VARACCESS_PREVENT_OVERWR) { + printf("## Error: Can't overwrite \"%s\"\n", name); + return 1; + } else if (item->flags & + ENV_FLAGS_VARACCESS_PREVENT_NONDEF_OVERWR) { + const char *defval = getenv_default(name); + + if (defval == NULL) + defval = ""; + printf("oldval: %s defval: %s\n", oldval, defval); + if (strcmp(oldval, defval) != 0) { + printf("## Error: Can't overwrite \"%s\"\n", + name); + return 1; + } + } + break; + case env_op_create: + if (item->flags & ENV_FLAGS_VARACCESS_PREVENT_CREATE) { + printf("## Error: Can't create \"%s\"\n", name); + return 1; + } + break; + } + return 0; } diff --git a/include/env_flags.h b/include/env_flags.h index 7e72523..0bdae07 100644 --- a/include/env_flags.h +++ b/include/env_flags.h @@ -36,9 +36,18 @@ enum env_flags_vartype { env_flags_vartype_end }; +enum env_flags_varaccess { + env_flags_varaccess_any, + env_flags_varaccess_readonly, + env_flags_varaccess_writeonce, + env_flags_varaccess_changedefault, + env_flags_varaccess_end +}; + #define ENV_FLAGS_VAR ".flags" #define ENV_FLAGS_ATTR_MAX_LEN 2 #define ENV_FLAGS_VARTYPE_LOC 0 +#define ENV_FLAGS_VARACCESS_LOC 1 #ifndef CONFIG_ENV_FLAGS_LIST_STATIC #define CONFIG_ENV_FLAGS_LIST_STATIC "" @@ -53,15 +62,31 @@ enum env_flags_vartype { */ void env_flags_print_vartypes(void); /* + * Print the whole list of available access flags. + */ +void env_flags_print_varaccess(void); +/* * Return the name of the type. */ const char *env_flags_get_vartype_name(enum env_flags_vartype type); +/* + * Return the name of the access. + */ +const char *env_flags_get_varaccess_name(enum env_flags_varaccess access); #endif /* * Parse the flags string from a .flags attribute list into the vartype enum. */ enum env_flags_vartype env_flags_parse_vartype(const char *flags); +/* + * Parse the flags string from a .flags attribute list into the varaccess enum. + */ +enum env_flags_varaccess env_flags_parse_varaccess(const char *flags); +/* + * Parse the binary flags from a hash table entry into the varaccess enum. + */ +enum env_flags_varaccess env_flags_parse_varaccess_from_binflags(int binflags); #ifdef USE_HOSTCC /* @@ -69,11 +94,25 @@ enum env_flags_vartype env_flags_parse_vartype(const char *flags); */ enum env_flags_vartype env_flags_get_type(const char *name); /* + * Look up the access of a variable directly from the .flags var. + */ +enum env_flags_varaccess env_flags_get_access(const char *name); +/* * Validate the newval for its type to conform with the requirements defined by * its flags (directly looked at the .flags var). */ int env_flags_validate_type(const char *name, const char *newval); /* + * Validate the newval for its access to conform with the requirements defined + * by its flags (directly looked at the .flags var). + */ +int env_flags_validate_access(const char *name, int check_mask); +/* + * Validate that the proposed access to variable "name" is valid according to + * the defined flags for that variable, if any. + */ +int env_flags_validate_varaccess(const char *name, int check_mask); +/* * Validate the parameters passed to "env set" for type compliance */ int env_flags_validate_env_set_params(int argc, char * const argv[]); @@ -94,13 +133,18 @@ void env_flags_init(ENTRY *var_entry); int env_flags_validate(const ENTRY *item, const char *newval, enum env_op op, int flag); +#endif /* USE_HOSTCC */ + /* * These are the binary flags used in the environment entry->flags variable to * decribe properties of veriables in the table */ -#define ENV_FLAGS_VARTYPE_BIN_MASK 0x00000007 +#define ENV_FLAGS_VARTYPE_BIN_MASK 0x00000007 /* The actual variable type values use the enum value (within the mask) */ - -#endif /* USE_HOSTCC */ +#define ENV_FLAGS_VARACCESS_PREVENT_DELETE 0x00000008 +#define ENV_FLAGS_VARACCESS_PREVENT_CREATE 0x00000010 +#define ENV_FLAGS_VARACCESS_PREVENT_OVERWR 0x00000020 +#define ENV_FLAGS_VARACCESS_PREVENT_NONDEF_OVERWR 0x00000040 +#define ENV_FLAGS_VARACCESS_BIN_MASK 0x00000078 #endif /* __ENV_FLAGS_H__ */ diff --git a/include/environment.h b/include/environment.h index 00e59ba..e64b43d 100644 --- a/include/environment.h +++ b/include/environment.h @@ -181,6 +181,9 @@ unsigned char env_get_char_memory(int index); /* Function that updates CRC of the enironment */ void env_crc_update(void); +/* Look up the variable from the default environment */ +char *getenv_default(const char *name); + /* [re]set to the default environment */ void set_default_env(const char *s); diff --git a/tools/env/fw_env.c b/tools/env/fw_env.c index 5be36fc..a596a1b 100644 --- a/tools/env/fw_env.c +++ b/tools/env/fw_env.c @@ -182,6 +182,32 @@ char *fw_getenv (char *name) } /* + * Search the default environment for a variable. + * Return the value, if found, or NULL, if not found. + */ +char *fw_getdefenv(char *name) +{ + char *env, *nxt; + + for (env = default_environment; *env; env = nxt + 1) { + char *val; + + for (nxt = env; *nxt; ++nxt) { + if (nxt >= &default_environment[ENV_SIZE]) { + fprintf(stderr, "## Error: " + "default environment not terminated\n"); + return NULL; + } + } + val = envmatch(name, env); + if (!val) + continue; + return val; + } + return NULL; +} + +/* * Print the current definition of one, or more, or all * environment variables */ @@ -282,6 +308,7 @@ int fw_env_write(char *name, char *value) int len; char *env, *nxt; char *oldval = NULL; + int deleting, creating, overwriting; /* * search if variable with this name already exists @@ -299,10 +326,49 @@ int fw_env_write(char *name, char *value) break; } - /* - * Delete any existing definition - */ - if (oldval) { + deleting = (oldval && !(value && strlen(value))); + creating = (!oldval && (value && strlen(value))); + overwriting = (oldval && (value && strlen(value))); + + /* check for permission */ + if (deleting) { + if (env_flags_validate_varaccess(name, + ENV_FLAGS_VARACCESS_PREVENT_DELETE)) { + printf("Can't delete \"%s\"\n", name); + errno = EROFS; + return -1; + } + } else if (overwriting) { + if (env_flags_validate_varaccess(name, + ENV_FLAGS_VARACCESS_PREVENT_OVERWR)) { + printf("Can't overwrite \"%s\"\n", name); + errno = EROFS; + return -1; + } else if (env_flags_validate_varaccess(name, + ENV_FLAGS_VARACCESS_PREVENT_NONDEF_OVERWR)) { + const char *defval = fw_getdefenv(name); + + if (defval == NULL) + defval = ""; + if (strcmp(oldval, defval) + != 0) { + printf("Can't overwrite \"%s\"\n", name); + errno = EROFS; + return -1; + } + } + } else if (creating) { + if (env_flags_validate_varaccess(name, + ENV_FLAGS_VARACCESS_PREVENT_CREATE)) { + printf("Can't create \"%s\"\n", name); + errno = EROFS; + return -1; + } + } else + /* Nothing to do */ + return 0; + + if (deleting || overwriting) { #ifndef CONFIG_ENV_OVERWRITE /* * Ethernet Address and serial# can be set only once -- cgit v0.10.2 From 24ab5a1914283b891fa50bc285128bc5fd4ac50a Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 11 Dec 2012 22:16:35 -0600 Subject: env: Add setenv force support Now that we have support for permissions, add a way to override them. Signed-off-by: Joe Hershberger diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c index e8dfbf5..988d6b3 100644 --- a/common/cmd_nvedit.c +++ b/common/cmd_nvedit.c @@ -200,7 +200,24 @@ static int _do_env_set(int flag, int argc, char * const argv[]) int i, len; char *name, *value, *s; ENTRY e, *ep; + int env_flag = H_INTERACTIVE; + debug("Initial value for argc=%d\n", argc); + while (argc > 1 && **(argv + 1) == '-') { + char *arg = *++argv; + + --argc; + while (*++arg) { + switch (*arg) { + case 'f': /* force */ + env_flag |= H_FORCE; + break; + default: + return CMD_RET_USAGE; + } + } + } + debug("Final value for argc=%d\n", argc); name = argv[1]; value = argv[2]; @@ -214,7 +231,7 @@ static int _do_env_set(int flag, int argc, char * const argv[]) /* Delete only ? */ if (argc < 3 || argv[2] == NULL) { - int rc = hdelete_r(name, &env_htab, H_INTERACTIVE); + int rc = hdelete_r(name, &env_htab, env_flag); return !rc; } @@ -241,7 +258,7 @@ static int _do_env_set(int flag, int argc, char * const argv[]) e.key = name; e.data = value; - hsearch_r(e, ENTER, &ep, &env_htab, H_INTERACTIVE); + hsearch_r(e, ENTER, &ep, &env_htab, env_flag); free(value); if (!ep) { printf("## Error inserting \"%s\" variable, errno=%d\n", @@ -1109,10 +1126,10 @@ U_BOOT_CMD_COMPLETE( U_BOOT_CMD_COMPLETE( setenv, CONFIG_SYS_MAXARGS, 0, do_env_set, "set environment variables", - "name value ...\n" - " - set environment variable 'name' to 'value ...'\n" - "setenv name\n" - " - delete environment variable 'name'", + "[-f] name value ...\n" + " - [forcibly] set environment variable 'name' to 'value ...'\n" + "setenv [-f] name\n" + " - [forcibly] delete environment variable 'name'", var_complete ); -- cgit v0.10.2 From 9d8d661d7fda6ccda1d5fc31565f207b7648bc6d Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 11 Dec 2012 22:16:36 -0600 Subject: env: Implement the env delete command Implement a way to delete more than one variable at a time. Signed-off-by: Joe Hershberger diff --git a/common/cmd_nvedit.c b/common/cmd_nvedit.c index 988d6b3..7633f0c 100644 --- a/common/cmd_nvedit.c +++ b/common/cmd_nvedit.c @@ -725,8 +725,36 @@ static int do_env_default(cmd_tbl_t *cmdtp, int __flag, static int do_env_delete(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - printf("Not implemented yet\n"); - return 0; + int env_flag = H_INTERACTIVE; + int ret = 0; + + debug("Initial value for argc=%d\n", argc); + while (argc > 1 && **(argv + 1) == '-') { + char *arg = *++argv; + + --argc; + while (*++arg) { + switch (*arg) { + case 'f': /* force */ + env_flag |= H_FORCE; + break; + default: + return CMD_RET_USAGE; + } + } + } + debug("Final value for argc=%d\n", argc); + + env_id++; + + while (--argc > 0) { + char *name = *++argv; + + if (!hdelete_r(name, &env_htab, env_flag)) + ret = 1; + } + + return ret; } #ifdef CONFIG_CMD_EXPORTENV @@ -995,7 +1023,7 @@ static cmd_tbl_t cmd_env_sub[] = { U_BOOT_CMD_MKENT(ask, CONFIG_SYS_MAXARGS, 1, do_env_ask, "", ""), #endif U_BOOT_CMD_MKENT(default, 1, 0, do_env_default, "", ""), - U_BOOT_CMD_MKENT(delete, 2, 0, do_env_delete, "", ""), + U_BOOT_CMD_MKENT(delete, CONFIG_SYS_MAXARGS, 0, do_env_delete, "", ""), #if defined(CONFIG_CMD_EDITENV) U_BOOT_CMD_MKENT(edit, 2, 0, do_env_edit, "", ""), #endif @@ -1060,6 +1088,7 @@ static char env_help_text[] = #endif "default [-f] -a - [forcibly] reset default environment\n" "env default [-f] var [...] - [forcibly] reset variable(s) to their default values\n" + "env delete [-f] var [...] - [forcibly] delete variable(s)\n" #if defined(CONFIG_CMD_EDITENV) "env edit name - edit environment variable\n" #endif -- cgit v0.10.2 From 1d6cd0a3f69b549a3fc7e735a045279e7a14947e Mon Sep 17 00:00:00 2001 From: Joe Hershberger Date: Tue, 11 Dec 2012 22:16:37 -0600 Subject: env: Handle write-once ethaddr and serial# generically Use the variable access flags to implement the protection for ethaddr and serial# instead of hard-coding them. Signed-off-by: Joe Hershberger diff --git a/common/env_flags.c b/common/env_flags.c index 4caf12e..336cae4 100644 --- a/common/env_flags.c +++ b/common/env_flags.c @@ -503,25 +503,6 @@ int env_flags_validate(const ENTRY *item, const char *newval, enum env_op op, /* Default value for NULL to protect string-manipulating functions */ newval = newval ? : ""; -#ifndef CONFIG_ENV_OVERWRITE - /* - * Some variables like "ethaddr" and "serial#" can be set only once and - * cannot be deleted, unless CONFIG_ENV_OVERWRITE is defined. - */ - if (op != env_op_create && /* variable exists */ - (flag & H_FORCE) == 0) { /* and we are not forced */ - if (strcmp(name, "serial#") == 0 || - (strcmp(name, "ethaddr") == 0 -#if defined(CONFIG_OVERWRITE_ETHADDR_ONCE) && defined(CONFIG_ETHADDR) - && strcmp(oldval, __stringify(CONFIG_ETHADDR)) != 0 -#endif /* CONFIG_OVERWRITE_ETHADDR_ONCE && CONFIG_ETHADDR */ - )) { - printf("Can't overwrite \"%s\"\n", name); - return 1; - } - } -#endif - /* validate the value to match the variable type */ if (op != env_op_delete) { enum env_flags_vartype type = (enum env_flags_vartype) diff --git a/include/env_flags.h b/include/env_flags.h index 0bdae07..d1aa144 100644 --- a/include/env_flags.h +++ b/include/env_flags.h @@ -53,7 +53,29 @@ enum env_flags_varaccess { #define CONFIG_ENV_FLAGS_LIST_STATIC "" #endif +#ifdef CONFIG_CMD_NET +#ifdef CONFIG_ENV_OVERWRITE +#define ETHADDR_FLAGS "ethaddr:ma," +#else +#ifdef CONFIG_OVERWRITE_ETHADDR_ONCE +#define ETHADDR_FLAGS "ethaddr:mc," +#else +#define ETHADDR_FLAGS "ethaddr:mo," +#endif +#endif +#else +#define ETHADDR_FLAGS "" +#endif + +#ifndef CONFIG_ENV_OVERWRITE +#define SERIAL_FLAGS "serial#:so," +#else +#define SERIAL_FLAGS "" +#endif + #define ENV_FLAGS_LIST_STATIC \ + ETHADDR_FLAGS \ + SERIAL_FLAGS \ CONFIG_ENV_FLAGS_LIST_STATIC #ifdef CONFIG_CMD_ENV_FLAGS diff --git a/tools/env/fw_env.c b/tools/env/fw_env.c index a596a1b..90c7a5d 100644 --- a/tools/env/fw_env.c +++ b/tools/env/fw_env.c @@ -369,23 +369,6 @@ int fw_env_write(char *name, char *value) return 0; if (deleting || overwriting) { -#ifndef CONFIG_ENV_OVERWRITE - /* - * Ethernet Address and serial# can be set only once - */ - if ( - (strcmp(name, "serial#") == 0) || - ((strcmp(name, "ethaddr") == 0) -#if defined(CONFIG_OVERWRITE_ETHADDR_ONCE) && defined(CONFIG_ETHADDR) - && (strcmp(oldval, __stringify(CONFIG_ETHADDR)) != 0) -#endif /* CONFIG_OVERWRITE_ETHADDR_ONCE && CONFIG_ETHADDR */ - ) ) { - fprintf (stderr, "Can't overwrite \"%s\"\n", name); - errno = EROFS; - return -1; - } -#endif /* CONFIG_ENV_OVERWRITE */ - if (*++nxt == '\0') { *env = '\0'; } else { -- cgit v0.10.2 From 6e9005bd96ff0f0548a787ffafee10664a57a8e1 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Thu, 13 Dec 2012 13:58:27 -0700 Subject: omap3_evm: Let CONFIG_EFI_PARTITION be set for SPL The #ifdef here is not enough to stop part_efi.c from being built, only being unused. And with recent changes this now leads to warnings. The easiest solution here is to just let the garbage collection at link time do its job. Signed-off-by: Tom Rini diff --git a/include/configs/omap3_evm.h b/include/configs/omap3_evm.h index b4d925e..fc6e782 100644 --- a/include/configs/omap3_evm.h +++ b/include/configs/omap3_evm.h @@ -95,10 +95,7 @@ #define CONFIG_SPL_FAT_LOAD_PAYLOAD_NAME "u-boot.img" /* Partition tables */ -/* Only need DOS partition support for SPL, currently */ -#ifndef CONFIG_SPL_BUILD #define CONFIG_EFI_PARTITION -#endif #define CONFIG_DOS_PARTITION /* USB -- cgit v0.10.2