busybox/miscutils/flash_eraseall.c
James Byrne 6937487be7 libbb: reduce the overhead of single parameter bb_error_msg() calls
Back in 2007, commit 0c97c9d437 ("'simple' error message functions by
Loic Grenie") introduced bb_simple_perror_msg() to allow for a lower
overhead call to bb_perror_msg() when only a string was being printed
with no parameters. This saves space for some CPU architectures because
it avoids the overhead of a call to a variadic function. However there
has never been a simple version of bb_error_msg(), and since 2007 many
new calls to bb_perror_msg() have been added that only take a single
parameter and so could have been using bb_simple_perror_message().

This changeset introduces 'simple' versions of bb_info_msg(),
bb_error_msg(), bb_error_msg_and_die(), bb_herror_msg() and
bb_herror_msg_and_die(), and replaces all calls that only take a
single parameter, or use something like ("%s", arg), with calls to the
corresponding 'simple' version.

Since it is likely that single parameter calls to the variadic functions
may be accidentally reintroduced in the future a new debugging config
option WARN_SIMPLE_MSG has been introduced. This uses some macro magic
which will cause any such calls to generate a warning, but this is
turned off by default to avoid use of the unpleasant macros in normal
circumstances.

This is a large changeset due to the number of calls that have been
replaced. The only files that contain changes other than simple
substitution of function calls are libbb.h, libbb/herror_msg.c,
libbb/verror_msg.c and libbb/xfuncs_printf.c. In miscutils/devfsd.c,
networking/udhcp/common.h and util-linux/mdev.c additonal macros have
been added for logging so that single parameter and multiple parameter
logging variants exist.

The amount of space saved varies considerably by architecture, and was
found to be as follows (for 'defconfig' using GCC 7.4):

Arm:     -92 bytes
MIPS:    -52 bytes
PPC:   -1836 bytes
x86_64: -938 bytes

Note that for the MIPS architecture only an exception had to be made
disabling the 'simple' calls for 'udhcp' (in networking/udhcp/common.h)
because it made these files larger on MIPS.

Signed-off-by: James Byrne <james.byrne@origamienergy.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2019-07-02 11:35:03 +02:00

219 lines
5.8 KiB
C

/* vi: set sw=4 ts=4: */
/*
* eraseall.c -- erase the whole of a MTD device
*
* Ported to busybox from mtd-utils.
*
* Copyright (C) 2000 Arcom Control System Ltd
*
* Renamed to flash_eraseall.c
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
//config:config FLASH_ERASEALL
//config: bool "flash_eraseall (5.9 kb)"
//config: default n # doesn't build on Ubuntu 8.04
//config: help
//config: The flash_eraseall binary from mtd-utils as of git head c4c6a59eb.
//config: This utility is used to erase the whole MTD device.
//applet:IF_FLASH_ERASEALL(APPLET(flash_eraseall, BB_DIR_USR_SBIN, BB_SUID_DROP))
/* not NOEXEC: if flash operation stalls, use less memory in "hung" process */
//kbuild:lib-$(CONFIG_FLASH_ERASEALL) += flash_eraseall.o
//usage:#define flash_eraseall_trivial_usage
//usage: "[-jNq] MTD_DEVICE"
//usage:#define flash_eraseall_full_usage "\n\n"
//usage: "Erase an MTD device\n"
//usage: "\n -j Format the device for jffs2"
//usage: "\n -N Don't skip bad blocks"
//usage: "\n -q Don't display progress messages"
#include "libbb.h"
#include <mtd/mtd-user.h>
#include <linux/jffs2.h>
#define OPTION_J (1 << 0)
#define OPTION_N (1 << 1)
#define OPTION_Q (1 << 2)
#define IS_NAND (1 << 3)
/* mtd/jffs2-user.h used to have this atrocity:
extern int target_endian;
#define t16(x) ({ __u16 __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); })
#define t32(x) ({ __u32 __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); })
#define cpu_to_je16(x) ((jint16_t){t16(x)})
#define cpu_to_je32(x) ((jint32_t){t32(x)})
#define cpu_to_jemode(x) ((jmode_t){t32(x)})
#define je16_to_cpu(x) (t16((x).v16))
#define je32_to_cpu(x) (t32((x).v32))
#define jemode_to_cpu(x) (t32((x).m))
but mtd/jffs2-user.h is gone now (at least 2.6.31.6 does not have it anymore)
*/
/* We always use native endianness */
#undef cpu_to_je16
#undef cpu_to_je32
#define cpu_to_je16(v) ((jint16_t){(v)})
#define cpu_to_je32(v) ((jint32_t){(v)})
static void show_progress(mtd_info_t *meminfo, erase_info_t *erase)
{
printf("\rErasing %u Kibyte @ %x - %2u%% complete.",
(unsigned)meminfo->erasesize / 1024,
erase->start,
(unsigned) ((unsigned long long) erase->start * 100 / meminfo->size)
);
fflush_all();
}
int flash_eraseall_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int flash_eraseall_main(int argc UNUSED_PARAM, char **argv)
{
struct jffs2_unknown_node cleanmarker;
mtd_info_t meminfo;
int fd, clmpos, clmlen;
erase_info_t erase;
struct stat st;
unsigned int flags;
char *mtd_name;
flags = getopt32(argv, "^" "jNq" "\0" "=1");
mtd_name = argv[optind];
fd = xopen(mtd_name, O_RDWR);
fstat(fd, &st);
if (!S_ISCHR(st.st_mode))
bb_error_msg_and_die("%s: not a char device", mtd_name);
xioctl(fd, MEMGETINFO, &meminfo);
erase.length = meminfo.erasesize;
if (meminfo.type == MTD_NANDFLASH)
flags |= IS_NAND;
clmpos = 0;
clmlen = 8;
if (flags & OPTION_J) {
uint32_t *crc32_table;
crc32_table = crc32_new_table_le();
cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
if (!(flags & IS_NAND))
cleanmarker.totlen = cpu_to_je32(sizeof(struct jffs2_unknown_node));
else {
struct nand_oobinfo oobinfo;
xioctl(fd, MEMGETOOBSEL, &oobinfo);
/* Check for autoplacement */
if (oobinfo.useecc == MTD_NANDECC_AUTOPLACE) {
/* Get the position of the free bytes */
clmpos = oobinfo.oobfree[0][0];
clmlen = oobinfo.oobfree[0][1];
if (clmlen > 8)
clmlen = 8;
if (clmlen == 0)
bb_simple_error_msg_and_die("autoplacement selected and no empty space in oob");
} else {
/* Legacy mode */
switch (meminfo.oobsize) {
case 8:
clmpos = 6;
clmlen = 2;
break;
case 16:
clmpos = 8;
/*clmlen = 8;*/
break;
case 64:
clmpos = 16;
/*clmlen = 8;*/
break;
}
}
cleanmarker.totlen = cpu_to_je32(8);
}
cleanmarker.hdr_crc = cpu_to_je32(
crc32_block_endian0(0, &cleanmarker, sizeof(struct jffs2_unknown_node) - 4, crc32_table)
);
}
/* Don't want to destroy progress indicator by bb_error_msg's */
applet_name = xasprintf("\n%s: %s", applet_name, mtd_name);
for (erase.start = 0; erase.start < meminfo.size;
erase.start += meminfo.erasesize) {
if (!(flags & OPTION_N)) {
int ret;
loff_t offset = erase.start;
ret = ioctl(fd, MEMGETBADBLOCK, &offset);
if (ret > 0) {
if (!(flags & OPTION_Q))
printf("\nSkipping bad block at 0x%08x\n", erase.start);
continue;
}
if (ret < 0) {
/* Black block table is not available on certain flash
* types e.g. NOR
*/
if (errno == EOPNOTSUPP) {
flags |= OPTION_N;
if (flags & IS_NAND)
bb_simple_error_msg_and_die("bad block check not available");
} else {
bb_simple_perror_msg_and_die("MEMGETBADBLOCK error");
}
}
}
if (!(flags & OPTION_Q))
show_progress(&meminfo, &erase);
xioctl(fd, MEMERASE, &erase);
/* format for JFFS2 ? */
if (!(flags & OPTION_J))
continue;
/* write cleanmarker */
if (flags & IS_NAND) {
struct mtd_oob_buf oob;
oob.ptr = (unsigned char *) &cleanmarker;
oob.start = erase.start + clmpos;
oob.length = clmlen;
xioctl(fd, MEMWRITEOOB, &oob);
} else {
xlseek(fd, erase.start, SEEK_SET);
/* if (lseek(fd, erase.start, SEEK_SET) < 0) {
bb_perror_msg("MTD %s failure", "seek");
continue;
} */
xwrite(fd, &cleanmarker, sizeof(cleanmarker));
/* if (write(fd, &cleanmarker, sizeof(cleanmarker)) != sizeof(cleanmarker)) {
bb_perror_msg("MTD %s failure", "write");
continue;
} */
}
if (!(flags & OPTION_Q))
printf(" Cleanmarker written at %x.", erase.start);
}
if (!(flags & OPTION_Q)) {
show_progress(&meminfo, &erase);
bb_putchar('\n');
}
if (ENABLE_FEATURE_CLEAN_UP)
close(fd);
return EXIT_SUCCESS;
}